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