From 93c6aa684b84311e9d076c6fe2f35ba5520ae928 Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Thu, 16 Nov 2023 17:51:25 +0100 Subject: [PATCH] Accessibility on RRs (#2094) * added accessibility on the read receipts but there is something wrong in the generation * voice over implementation for RRs * Update ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> * done * changelog --------- Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> --- .../en.lproj/Localizable.strings | 5 ++++ .../en.lproj/Localizable.stringsdict | 16 ++++++++++ ElementX/Sources/Generated/Strings.swift | 18 +++++++++++ .../Style/TimelineItemBubbledStylerView.swift | 7 ++++- .../TimelineDeliveryStatusView.swift | 10 +++++++ .../TimelineItemStatusView.swift | 2 ++ .../TimelineReadReceiptsView.swift | 30 ++++++++++++++++++- changelog.d/1139.bugfix | 1 + 8 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 changelog.d/1139.bugfix diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.strings b/ElementX/Resources/Localizations/en.lproj/Localizable.strings index 258b9e543..12ce0b6ff 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.strings +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.strings @@ -1,4 +1,5 @@ "Notification" = "Notification"; +"a11_read_receipts_multiple" = "Read by %1$@ and %2$@"; "a11y_delete" = "Delete"; "a11y_hide_password" = "Hide password"; "a11y_notifications_mentions_only" = "Mentions only"; @@ -8,6 +9,7 @@ "a11y_play" = "Play"; "a11y_poll" = "Poll"; "a11y_poll_end" = "Ended poll"; +"a11y_read_receipts_single" = "Read by %1$@"; "a11y_send_files" = "Send files"; "a11y_show_password" = "Show password"; "a11y_start_call" = "Start a call"; @@ -80,6 +82,7 @@ "action_start_verification" = "Start verification"; "action_static_map_load" = "Tap to load map"; "action_take_photo" = "Take photo"; +"action_tap_for_options" = "Tap for options"; "action_try_again" = "Try again"; "action_view_source" = "View source"; "action_yes" = "Yes"; @@ -149,6 +152,8 @@ "common_search_results" = "Search results"; "common_security" = "Security"; "common_sending" = "Sending…"; +"common_sending_failed" = "Sending failed"; +"common_sent" = "Sent"; "common_server_not_supported" = "Server not supported"; "common_server_url" = "Server URL"; "common_settings" = "Settings"; diff --git a/ElementX/Resources/Localizations/en.lproj/Localizable.stringsdict b/ElementX/Resources/Localizations/en.lproj/Localizable.stringsdict index ae1ffecff..65f2937f7 100644 --- a/ElementX/Resources/Localizations/en.lproj/Localizable.stringsdict +++ b/ElementX/Resources/Localizations/en.lproj/Localizable.stringsdict @@ -18,6 +18,22 @@ %1$d digits entered + a11y_read_receipts_multiple_with_others + + NSStringLocalizedFormatKey + %#@COUNT@ + COUNT + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Read by %1$@ and %2$d other + other + Read by %1$@ and %2$d others + + common_member_count NSStringLocalizedFormatKey diff --git a/ElementX/Sources/Generated/Strings.swift b/ElementX/Sources/Generated/Strings.swift index e8ff1fc4b..17fdc251a 100644 --- a/ElementX/Sources/Generated/Strings.swift +++ b/ElementX/Sources/Generated/Strings.swift @@ -10,6 +10,10 @@ import Foundation // swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length // swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces public enum L10n { + /// Read by %1$@ and %2$@ + public static func a11ReadReceiptsMultiple(_ p1: Any, _ p2: Any) -> String { + return L10n.tr("Localizable", "a11_read_receipts_multiple", String(describing: p1), String(describing: p2)) + } /// Delete public static var a11yDelete: String { return L10n.tr("Localizable", "a11y_delete") } /// Plural format key: "%#@COUNT@" @@ -32,6 +36,14 @@ public enum L10n { public static var a11yPoll: String { return L10n.tr("Localizable", "a11y_poll") } /// Ended poll public static var a11yPollEnd: String { return L10n.tr("Localizable", "a11y_poll_end") } + /// Plural format key: "%#@COUNT@" + public static func a11yReadReceiptsMultipleWithOthers(_ p1: Int) -> String { + return L10n.tr("Localizable", "a11y_read_receipts_multiple_with_others", p1) + } + /// Read by %1$@ + public static func a11yReadReceiptsSingle(_ p1: Any) -> String { + return L10n.tr("Localizable", "a11y_read_receipts_single", String(describing: p1)) + } /// Send files public static var a11ySendFiles: String { return L10n.tr("Localizable", "a11y_send_files") } /// Show password @@ -180,6 +192,8 @@ public enum L10n { public static var actionStaticMapLoad: String { return L10n.tr("Localizable", "action_static_map_load") } /// Take photo public static var actionTakePhoto: String { return L10n.tr("Localizable", "action_take_photo") } + /// Tap for options + public static var actionTapForOptions: String { return L10n.tr("Localizable", "action_tap_for_options") } /// Try again public static var actionTryAgain: String { return L10n.tr("Localizable", "action_try_again") } /// View source @@ -338,6 +352,10 @@ public enum L10n { public static var commonSecurity: String { return L10n.tr("Localizable", "common_security") } /// Sending… public static var commonSending: String { return L10n.tr("Localizable", "common_sending") } + /// Sending failed + public static var commonSendingFailed: String { return L10n.tr("Localizable", "common_sending_failed") } + /// Sent + public static var commonSent: String { return L10n.tr("Localizable", "common_sent") } /// Server not supported public static var commonServerNotSupported: String { return L10n.tr("Localizable", "common_server_not_supported") } /// Server URL diff --git a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift index f292d49dd..aabd660b4 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Style/TimelineItemBubbledStylerView.swift @@ -100,7 +100,11 @@ struct TimelineItemBubbledStylerView: View { VStack(alignment: alignment, spacing: -3) { messageBubble .timelineItemAccessibility(timelineItem) { - context.send(viewAction: .timelineItemMenu(itemID: timelineItem.id)) + if adjustedDeliveryStatus == .sendingFailed { + context.sendFailedConfirmationDialogInfo = .init(itemID: timelineItem.id) + } else { + context.send(viewAction: .timelineItemMenu(itemID: timelineItem.id)) + } } if !timelineItem.properties.reactions.isEmpty { @@ -206,6 +210,7 @@ struct TimelineItemBubbledStylerView: View { if adjustedDeliveryStatus == .sendingFailed { CompoundIcon(\.error, size: .xSmall, relativeTo: .compound.bodyXS) + .accessibilityLabel(L10n.commonSendingFailed) } } .font(.compound.bodyXS) diff --git a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineDeliveryStatusView.swift b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineDeliveryStatusView.swift index 55e4d3f82..44e9e5ba4 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineDeliveryStatusView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineDeliveryStatusView.swift @@ -37,6 +37,16 @@ struct TimelineDeliveryStatusView: View { var body: some View { icon .foregroundColor(.compound.iconSecondary) + .accessibilityLabel(accessibilityLabel) + } + + private var accessibilityLabel: String { + switch deliveryStatus { + case .sending: + return L10n.commonSending + case .sent: + return L10n.commonSent + } } } diff --git a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineItemStatusView.swift b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineItemStatusView.swift index c58febb39..d864e7a48 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineItemStatusView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineItemStatusView.swift @@ -59,6 +59,8 @@ struct TimelineItemStatusView: View { .onTapGesture { context.sendFailedConfirmationDialogInfo = .init(itemID: timelineItem.id) } + .accessibilityLabel(L10n.commonSendingFailed) + .accessibilityHint(L10n.actionTapForOptions) } } } diff --git a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift index 1932fa9be..e33717e90 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift @@ -40,12 +40,40 @@ struct TimelineReadReceiptsView: View { } } if timelineItem.properties.orderedReadReceipts.count > displayNumber { - let remaining = timelineItem.properties.orderedReadReceipts.count - displayNumber Text("+\(remaining)") .font(.compound.bodySM) .foregroundColor(.compound.textPrimary) } } + .accessibilityElement(children: .ignore) + .accessibilityLabel(accessibilityLabel) + } + + private var remaining: Int { + timelineItem.properties.orderedReadReceipts.count - displayNumber + } + + private var accessibilityLabel: String { + if timelineItem.properties.orderedReadReceipts.count == 1 { + return L10n.a11yReadReceiptsSingle(displayName(at: 0)) + } else if timelineItem.properties.orderedReadReceipts.count <= displayNumber { + let limit = timelineItem.properties.orderedReadReceipts.count - 1 + let list = (0.. displayNumber { + let list = (0.. String { + let userID = timelineItem.properties.orderedReadReceipts[index].userID + return context.viewState.members[userID]?.displayName ?? userID } } diff --git a/changelog.d/1139.bugfix b/changelog.d/1139.bugfix new file mode 100644 index 000000000..2ee0ff513 --- /dev/null +++ b/changelog.d/1139.bugfix @@ -0,0 +1 @@ +Added proper accessibility to read receipts. \ No newline at end of file