mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
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>
This commit is contained in:
parent
0831c898ef
commit
93c6aa684b
@ -1,4 +1,5 @@
|
|||||||
"Notification" = "Notification";
|
"Notification" = "Notification";
|
||||||
|
"a11_read_receipts_multiple" = "Read by %1$@ and %2$@";
|
||||||
"a11y_delete" = "Delete";
|
"a11y_delete" = "Delete";
|
||||||
"a11y_hide_password" = "Hide password";
|
"a11y_hide_password" = "Hide password";
|
||||||
"a11y_notifications_mentions_only" = "Mentions only";
|
"a11y_notifications_mentions_only" = "Mentions only";
|
||||||
@ -8,6 +9,7 @@
|
|||||||
"a11y_play" = "Play";
|
"a11y_play" = "Play";
|
||||||
"a11y_poll" = "Poll";
|
"a11y_poll" = "Poll";
|
||||||
"a11y_poll_end" = "Ended poll";
|
"a11y_poll_end" = "Ended poll";
|
||||||
|
"a11y_read_receipts_single" = "Read by %1$@";
|
||||||
"a11y_send_files" = "Send files";
|
"a11y_send_files" = "Send files";
|
||||||
"a11y_show_password" = "Show password";
|
"a11y_show_password" = "Show password";
|
||||||
"a11y_start_call" = "Start a call";
|
"a11y_start_call" = "Start a call";
|
||||||
@ -80,6 +82,7 @@
|
|||||||
"action_start_verification" = "Start verification";
|
"action_start_verification" = "Start verification";
|
||||||
"action_static_map_load" = "Tap to load map";
|
"action_static_map_load" = "Tap to load map";
|
||||||
"action_take_photo" = "Take photo";
|
"action_take_photo" = "Take photo";
|
||||||
|
"action_tap_for_options" = "Tap for options";
|
||||||
"action_try_again" = "Try again";
|
"action_try_again" = "Try again";
|
||||||
"action_view_source" = "View source";
|
"action_view_source" = "View source";
|
||||||
"action_yes" = "Yes";
|
"action_yes" = "Yes";
|
||||||
@ -149,6 +152,8 @@
|
|||||||
"common_search_results" = "Search results";
|
"common_search_results" = "Search results";
|
||||||
"common_security" = "Security";
|
"common_security" = "Security";
|
||||||
"common_sending" = "Sending…";
|
"common_sending" = "Sending…";
|
||||||
|
"common_sending_failed" = "Sending failed";
|
||||||
|
"common_sent" = "Sent";
|
||||||
"common_server_not_supported" = "Server not supported";
|
"common_server_not_supported" = "Server not supported";
|
||||||
"common_server_url" = "Server URL";
|
"common_server_url" = "Server URL";
|
||||||
"common_settings" = "Settings";
|
"common_settings" = "Settings";
|
||||||
|
@ -18,6 +18,22 @@
|
|||||||
<string>%1$d digits entered</string>
|
<string>%1$d digits entered</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>a11y_read_receipts_multiple_with_others</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
<string>%#@COUNT@</string>
|
||||||
|
<key>COUNT</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSStringFormatSpecTypeKey</key>
|
||||||
|
<string>NSStringPluralRuleType</string>
|
||||||
|
<key>NSStringFormatValueTypeKey</key>
|
||||||
|
<string>d</string>
|
||||||
|
<key>one</key>
|
||||||
|
<string>Read by %1$@ and %2$d other</string>
|
||||||
|
<key>other</key>
|
||||||
|
<string>Read by %1$@ and %2$d others</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
<key>common_member_count</key>
|
<key>common_member_count</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NSStringLocalizedFormatKey</key>
|
<key>NSStringLocalizedFormatKey</key>
|
||||||
|
@ -10,6 +10,10 @@ import Foundation
|
|||||||
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
|
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
|
||||||
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
public enum L10n {
|
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
|
/// Delete
|
||||||
public static var a11yDelete: String { return L10n.tr("Localizable", "a11y_delete") }
|
public static var a11yDelete: String { return L10n.tr("Localizable", "a11y_delete") }
|
||||||
/// Plural format key: "%#@COUNT@"
|
/// Plural format key: "%#@COUNT@"
|
||||||
@ -32,6 +36,14 @@ public enum L10n {
|
|||||||
public static var a11yPoll: String { return L10n.tr("Localizable", "a11y_poll") }
|
public static var a11yPoll: String { return L10n.tr("Localizable", "a11y_poll") }
|
||||||
/// Ended poll
|
/// Ended poll
|
||||||
public static var a11yPollEnd: String { return L10n.tr("Localizable", "a11y_poll_end") }
|
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
|
/// Send files
|
||||||
public static var a11ySendFiles: String { return L10n.tr("Localizable", "a11y_send_files") }
|
public static var a11ySendFiles: String { return L10n.tr("Localizable", "a11y_send_files") }
|
||||||
/// Show password
|
/// Show password
|
||||||
@ -180,6 +192,8 @@ public enum L10n {
|
|||||||
public static var actionStaticMapLoad: String { return L10n.tr("Localizable", "action_static_map_load") }
|
public static var actionStaticMapLoad: String { return L10n.tr("Localizable", "action_static_map_load") }
|
||||||
/// Take photo
|
/// Take photo
|
||||||
public static var actionTakePhoto: String { return L10n.tr("Localizable", "action_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
|
/// Try again
|
||||||
public static var actionTryAgain: String { return L10n.tr("Localizable", "action_try_again") }
|
public static var actionTryAgain: String { return L10n.tr("Localizable", "action_try_again") }
|
||||||
/// View source
|
/// View source
|
||||||
@ -338,6 +352,10 @@ public enum L10n {
|
|||||||
public static var commonSecurity: String { return L10n.tr("Localizable", "common_security") }
|
public static var commonSecurity: String { return L10n.tr("Localizable", "common_security") }
|
||||||
/// Sending…
|
/// Sending…
|
||||||
public static var commonSending: String { return L10n.tr("Localizable", "common_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
|
/// Server not supported
|
||||||
public static var commonServerNotSupported: String { return L10n.tr("Localizable", "common_server_not_supported") }
|
public static var commonServerNotSupported: String { return L10n.tr("Localizable", "common_server_not_supported") }
|
||||||
/// Server URL
|
/// Server URL
|
||||||
|
@ -100,7 +100,11 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
|||||||
VStack(alignment: alignment, spacing: -3) {
|
VStack(alignment: alignment, spacing: -3) {
|
||||||
messageBubble
|
messageBubble
|
||||||
.timelineItemAccessibility(timelineItem) {
|
.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 {
|
if !timelineItem.properties.reactions.isEmpty {
|
||||||
@ -206,6 +210,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
|||||||
|
|
||||||
if adjustedDeliveryStatus == .sendingFailed {
|
if adjustedDeliveryStatus == .sendingFailed {
|
||||||
CompoundIcon(\.error, size: .xSmall, relativeTo: .compound.bodyXS)
|
CompoundIcon(\.error, size: .xSmall, relativeTo: .compound.bodyXS)
|
||||||
|
.accessibilityLabel(L10n.commonSendingFailed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.font(.compound.bodyXS)
|
.font(.compound.bodyXS)
|
||||||
|
@ -37,6 +37,16 @@ struct TimelineDeliveryStatusView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
icon
|
icon
|
||||||
.foregroundColor(.compound.iconSecondary)
|
.foregroundColor(.compound.iconSecondary)
|
||||||
|
.accessibilityLabel(accessibilityLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var accessibilityLabel: String {
|
||||||
|
switch deliveryStatus {
|
||||||
|
case .sending:
|
||||||
|
return L10n.commonSending
|
||||||
|
case .sent:
|
||||||
|
return L10n.commonSent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +59,8 @@ struct TimelineItemStatusView: View {
|
|||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
context.sendFailedConfirmationDialogInfo = .init(itemID: timelineItem.id)
|
context.sendFailedConfirmationDialogInfo = .init(itemID: timelineItem.id)
|
||||||
}
|
}
|
||||||
|
.accessibilityLabel(L10n.commonSendingFailed)
|
||||||
|
.accessibilityHint(L10n.actionTapForOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,12 +40,40 @@ struct TimelineReadReceiptsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if timelineItem.properties.orderedReadReceipts.count > displayNumber {
|
if timelineItem.properties.orderedReadReceipts.count > displayNumber {
|
||||||
let remaining = timelineItem.properties.orderedReadReceipts.count - displayNumber
|
|
||||||
Text("+\(remaining)")
|
Text("+\(remaining)")
|
||||||
.font(.compound.bodySM)
|
.font(.compound.bodySM)
|
||||||
.foregroundColor(.compound.textPrimary)
|
.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..<limit).map { displayName(at: $0) }.formatted(.list(type: .and, width: .narrow))
|
||||||
|
let last = displayName(at: limit)
|
||||||
|
return L10n.a11ReadReceiptsMultiple(list, last)
|
||||||
|
} else if timelineItem.properties.orderedReadReceipts.count > displayNumber {
|
||||||
|
let list = (0..<displayNumber).map { displayName(at: $0) }.formatted(.list(type: .and, width: .narrow))
|
||||||
|
|
||||||
|
// Plurals with string arguments aren't generated correctly so we need to use this
|
||||||
|
// https://github.com/SwiftGen/SwiftGen/issues/1089
|
||||||
|
return L10n.tr("Localizable", "a11y_read_receipts_multiple_with_others", list, remaining)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
private func displayName(at index: Int) -> String {
|
||||||
|
let userID = timelineItem.properties.orderedReadReceipts[index].userID
|
||||||
|
return context.viewState.members[userID]?.displayName ?? userID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
changelog.d/1139.bugfix
Normal file
1
changelog.d/1139.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Added proper accessibility to read receipts.
|
Loading…
x
Reference in New Issue
Block a user