improved a11y in CollapsibleRoomTimelineView

This commit is contained in:
Mauro Romito
2025-06-16 18:23:20 +02:00
committed by Mauro
parent 7e801c3e58
commit 6c5813b534
4 changed files with 45 additions and 5 deletions
@@ -225,7 +225,6 @@
"common_reason" = "Reason"; "common_reason" = "Reason";
"common_recovery_key" = "Recovery key"; "common_recovery_key" = "Recovery key";
"common_refreshing" = "Refreshing…"; "common_refreshing" = "Refreshing…";
"common_replies" = "%1$d replies";
"common_replying_to" = "Replying to %1$@"; "common_replying_to" = "Replying to %1$@";
"common_report_a_bug" = "Report a bug"; "common_report_a_bug" = "Report a bug";
"common_report_a_problem" = "Report a problem"; "common_report_a_problem" = "Report a problem";
@@ -518,6 +517,8 @@
"screen_resolve_send_failure_you_unsigned_device_subtitle" = "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices."; "screen_resolve_send_failure_you_unsigned_device_subtitle" = "One or more of your devices are unverified. You can send the message anyway, or you can cancel for now and try again later after you have verified all of your devices.";
"screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices"; "screen_resolve_send_failure_you_unsigned_device_title" = "Your message was not sent because you have not verified one or more of your devices";
"screen_room_event_pill" = "Message in %1$@"; "screen_room_event_pill" = "Message in %1$@";
"screen_room_grouped_view_states_expand" = "Expand";
"screen_room_grouped_view_states_reduce" = "Reduce";
"screen_room_mentions_at_room_subtitle" = "Notify the whole room"; "screen_room_mentions_at_room_subtitle" = "Notify the whole room";
"screen_room_multiple_knock_requests_view_all_button_title" = "View all"; "screen_room_multiple_knock_requests_view_all_button_title" = "View all";
"screen_room_pinned_banner_indicator" = "%1$@ of %2$@"; "screen_room_pinned_banner_indicator" = "%1$@ of %2$@";
@@ -98,6 +98,22 @@
<string>%d votes</string> <string>%d votes</string>
</dict> </dict>
</dict> </dict>
<key>common_replies</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>%1$d reply</string>
<key>other</key>
<string>%1$d replies</string>
</dict>
</dict>
<key>notification_compat_summary_line_for_room</key> <key>notification_compat_summary_line_for_room</key>
<dict> <dict>
<key>NSStringLocalizedFormatKey</key> <key>NSStringLocalizedFormatKey</key>
+5 -1
View File
@@ -522,7 +522,7 @@ internal enum L10n {
internal static var commonRecoveryKey: String { return L10n.tr("Localizable", "common_recovery_key") } internal static var commonRecoveryKey: String { return L10n.tr("Localizable", "common_recovery_key") }
/// Refreshing… /// Refreshing…
internal static var commonRefreshing: String { return L10n.tr("Localizable", "common_refreshing") } internal static var commonRefreshing: String { return L10n.tr("Localizable", "common_refreshing") }
/// %1$d replies /// Plural format key: "%#@COUNT@"
internal static func commonReplies(_ p1: Int) -> String { internal static func commonReplies(_ p1: Int) -> String {
return L10n.tr("Localizable", "common_replies", p1) return L10n.tr("Localizable", "common_replies", p1)
} }
@@ -2182,6 +2182,10 @@ internal enum L10n {
internal static func screenRoomEventPill(_ p1: Any) -> String { internal static func screenRoomEventPill(_ p1: Any) -> String {
return L10n.tr("Localizable", "screen_room_event_pill", String(describing: p1)) return L10n.tr("Localizable", "screen_room_event_pill", String(describing: p1))
} }
/// Expand
internal static var screenRoomGroupedViewStatesExpand: String { return L10n.tr("Localizable", "screen_room_grouped_view_states_expand") }
/// Reduce
internal static var screenRoomGroupedViewStatesReduce: String { return L10n.tr("Localizable", "screen_room_grouped_view_states_reduce") }
/// Would you like to invite them back? /// Would you like to invite them back?
internal static var screenRoomInviteAgainAlertMessage: String { return L10n.tr("Localizable", "screen_room_invite_again_alert_message") } internal static var screenRoomInviteAgainAlertMessage: String { return L10n.tr("Localizable", "screen_room_invite_again_alert_message") }
/// You are alone in this chat /// You are alone in this chat
@@ -8,10 +8,16 @@
import SwiftUI import SwiftUI
struct CollapsibleRoomTimelineView: View { struct CollapsibleRoomTimelineView: View {
enum AccessibilityFocus {
case disclosure
case events
}
private let timelineItem: CollapsibleTimelineItem private let timelineItem: CollapsibleTimelineItem
private let groupedViewStates: [RoomTimelineItemViewState] private let groupedViewStates: [RoomTimelineItemViewState]
@State private var isExpanded = false @State private var isExpanded = false
@AccessibilityFocusState private var accessibilityFocusState: AccessibilityFocus?
init(timelineItem: CollapsibleTimelineItem) { init(timelineItem: CollapsibleTimelineItem) {
self.timelineItem = timelineItem self.timelineItem = timelineItem
@@ -21,13 +27,18 @@ struct CollapsibleRoomTimelineView: View {
var body: some View { var body: some View {
VStack(spacing: 0.0) { VStack(spacing: 0.0) {
Button { Button {
withElementAnimation { if !UIAccessibility.isVoiceOverRunning {
withElementAnimation {
isExpanded.toggle()
}
} else {
isExpanded.toggle() isExpanded.toggle()
} }
} label: { } label: {
HStack(alignment: .center, spacing: 8) { HStack(alignment: .center, spacing: 8) {
Text(L10n.screenRoomTimelineStateChanges(timelineItem.items.count)) Text(L10n.screenRoomTimelineStateChanges(timelineItem.items.count))
Text(Image(systemName: "chevron.forward")) Text(Image(systemName: "chevron.forward"))
.accessibilityLabel(isExpanded ? L10n.screenRoomGroupedViewStatesReduce : L10n.screenRoomGroupedViewStatesExpand)
.rotationEffect(.degrees(isExpanded ? 90 : 0)) .rotationEffect(.degrees(isExpanded ? 90 : 0))
.animation(.elementDefault, value: isExpanded) .animation(.elementDefault, value: isExpanded)
} }
@@ -40,13 +51,21 @@ struct CollapsibleRoomTimelineView: View {
.buttonStyle(.plain) .buttonStyle(.plain)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.padding(.top, 8.0) .padding(.top, 8.0)
.accessibilityFocused($accessibilityFocusState, equals: .disclosure)
if isExpanded { if isExpanded {
ForEach(groupedViewStates) { viewState in VStack(spacing: 0.0) {
RoomTimelineItemView(viewState: viewState) ForEach(groupedViewStates) { viewState in
RoomTimelineItemView(viewState: viewState)
}
} }
.accessibilityElement(children: .contain)
.accessibilityFocused($accessibilityFocusState, equals: .events)
} }
} }
.onChange(of: isExpanded) { _, newValue in
accessibilityFocusState = newValue ? .events : .disclosure
}
} }
} }