diff --git a/IceCubesApp/Resources/Localization/be.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/be.lproj/Localizable.strings index 32fba5b7..2d75adad 100644 --- a/IceCubesApp/Resources/Localization/be.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/be.lproj/Localizable.strings @@ -582,6 +582,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Дадатковая інфармацыя"; diff --git a/IceCubesApp/Resources/Localization/ca.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/ca.lproj/Localizable.strings index cb073c04..25953e94 100644 --- a/IceCubesApp/Resources/Localization/ca.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/ca.lproj/Localizable.strings @@ -576,6 +576,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Additional Info"; diff --git a/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings index 77c4478e..88511bda 100644 --- a/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings @@ -564,6 +564,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report diff --git a/IceCubesApp/Resources/Localization/en-GB.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/en-GB.lproj/Localizable.strings index a2bd4ac8..3aa5689f 100644 --- a/IceCubesApp/Resources/Localization/en-GB.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/en-GB.lproj/Localizable.strings @@ -577,6 +577,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Additional Info"; diff --git a/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings index b2d4597b..e9a4e116 100644 --- a/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings @@ -578,6 +578,8 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; + // MARK: Report "report.comment.placeholder" = "Additional Info"; diff --git a/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings index d86bcd1c..0087a70c 100644 --- a/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings @@ -578,6 +578,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Información adicional"; diff --git a/IceCubesApp/Resources/Localization/eu.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/eu.lproj/Localizable.strings index 4a9a7a68..2b126c3d 100644 --- a/IceCubesApp/Resources/Localization/eu.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/eu.lproj/Localizable.strings @@ -566,6 +566,7 @@ "accessibility.media.supported-type.audio.label" = "Audioa"; "accessibility.status.contains-media.label-%@" = "%@ dauka"; "accessibility.status.application.label" = "Aplikazioa"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Informazio gehigarria"; diff --git a/IceCubesApp/Resources/Localization/fr.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/fr.lproj/Localizable.strings index 0801c19e..7802d381 100644 --- a/IceCubesApp/Resources/Localization/fr.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/fr.lproj/Localizable.strings @@ -573,6 +573,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Information supplémentaire"; diff --git a/IceCubesApp/Resources/Localization/it.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/it.lproj/Localizable.strings index 869271f0..d5304806 100644 --- a/IceCubesApp/Resources/Localization/it.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/it.lproj/Localizable.strings @@ -577,6 +577,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Informazioni aggiuntive"; diff --git a/IceCubesApp/Resources/Localization/ja.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/ja.lproj/Localizable.strings index 08581f2a..e416535a 100644 --- a/IceCubesApp/Resources/Localization/ja.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/ja.lproj/Localizable.strings @@ -577,6 +577,7 @@ "accessibility.media.supported-type.audio.label" = "オーディオ"; "accessibility.status.contains-media.label-%@" = "%@ を含む"; "accessibility.status.application.label" = "アプリ"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "追加情報"; diff --git a/IceCubesApp/Resources/Localization/ko.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/ko.lproj/Localizable.strings index f48e0ef1..e64a2cf4 100644 --- a/IceCubesApp/Resources/Localization/ko.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/ko.lproj/Localizable.strings @@ -579,6 +579,7 @@ "accessibility.media.supported-type.audio.label" = "오디오"; "accessibility.status.contains-media.label-%@" = "%@ 첨부됨"; "accessibility.status.application.label" = "글 작성에 사용한 앱"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "추가 정보"; diff --git a/IceCubesApp/Resources/Localization/nb.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/nb.lproj/Localizable.strings index d8e335f0..70579ef1 100644 --- a/IceCubesApp/Resources/Localization/nb.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/nb.lproj/Localizable.strings @@ -577,6 +577,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Additional Info"; diff --git a/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings index a844d10a..8b27a7cf 100644 --- a/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings @@ -574,6 +574,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Aanvullende informatie"; diff --git a/IceCubesApp/Resources/Localization/pl.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/pl.lproj/Localizable.strings index 9fe116a2..2017253f 100644 --- a/IceCubesApp/Resources/Localization/pl.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/pl.lproj/Localizable.strings @@ -568,6 +568,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Zawiera %@"; "accessibility.status.application.label" = "Aplikacja"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Informacja dodatkowa"; diff --git a/IceCubesApp/Resources/Localization/pt-BR.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/pt-BR.lproj/Localizable.strings index 7ea448b2..de265bf9 100644 --- a/IceCubesApp/Resources/Localization/pt-BR.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/pt-BR.lproj/Localizable.strings @@ -577,6 +577,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Informação Adicional"; diff --git a/IceCubesApp/Resources/Localization/tr.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/tr.lproj/Localizable.strings index 7e09a364..035a122c 100644 --- a/IceCubesApp/Resources/Localization/tr.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/tr.lproj/Localizable.strings @@ -577,6 +577,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Additional Info"; diff --git a/IceCubesApp/Resources/Localization/uk.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/uk.lproj/Localizable.strings index f310d1a5..07f605c4 100644 --- a/IceCubesApp/Resources/Localization/uk.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/uk.lproj/Localizable.strings @@ -578,6 +578,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "Додаткова інформація"; diff --git a/IceCubesApp/Resources/Localization/zh-Hans.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/zh-Hans.lproj/Localizable.strings index 878a1406..553a93df 100644 --- a/IceCubesApp/Resources/Localization/zh-Hans.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/zh-Hans.lproj/Localizable.strings @@ -577,6 +577,7 @@ "accessibility.media.supported-type.audio.label" = "音频"; "accessibility.status.contains-media.label-%@" = "包含 %@"; "accessibility.status.application.label" = "应用"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "附加信息"; diff --git a/IceCubesApp/Resources/Localization/zh-Hant.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/zh-Hant.lproj/Localizable.strings index dd846a5c..a7eaf33f 100644 --- a/IceCubesApp/Resources/Localization/zh-Hant.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/zh-Hant.lproj/Localizable.strings @@ -578,6 +578,7 @@ "accessibility.media.supported-type.audio.label" = "Audio"; "accessibility.status.contains-media.label-%@" = "Contains %@"; "accessibility.status.application.label" = "App"; +"accessibility.status.media-viewer-action.label" = "Open media viewer"; // MARK: Report "report.comment.placeholder" = "附加資訊"; diff --git a/Packages/Models/Sources/Models/Filter.swift b/Packages/Models/Sources/Models/Filter.swift index 4dd32f89..cf49a13d 100644 --- a/Packages/Models/Sources/Models/Filter.swift +++ b/Packages/Models/Sources/Models/Filter.swift @@ -6,7 +6,7 @@ public struct Filtered: Codable, Equatable, Hashable { } public struct Filter: Codable, Identifiable, Equatable, Hashable { - public enum Action: String, Codable { + public enum Action: String, Codable, Equatable { case warn, hide } diff --git a/Packages/Status/Sources/Status/Detail/StatusDetailView.swift b/Packages/Status/Sources/Status/Detail/StatusDetailView.swift index 7452aeeb..cd9d0ab8 100644 --- a/Packages/Status/Sources/Status/Detail/StatusDetailView.swift +++ b/Packages/Status/Sources/Status/Detail/StatusDetailView.swift @@ -17,6 +17,10 @@ public struct StatusDetailView: View { @State private var isLoaded: Bool = false @State private var statusHeight: CGFloat = 0 + /// April 4th, 2023: Without explicit focus being set, VoiceOver will skip over a seemingly random number of elements on this screen when pushing in from the main timeline. + /// By using ``@AccessibilityFocusState`` and setting focus once, we work around this issue. + @AccessibilityFocusState private var initialFocusBugWorkaround: Bool + public init(statusId: String) { _viewModel = StateObject(wrappedValue: .init(statusId: statusId)) } @@ -145,6 +149,7 @@ public struct StatusDetailView: View { client: client, routerPath: routerPath, isFocused: true) }) + .accessibilityFocused($initialFocusBugWorkaround, equals: true) .overlay { GeometryReader { reader in VStack {} @@ -154,6 +159,10 @@ public struct StatusDetailView: View { } } .id(status.id) + // VoiceOver / Switch Control focus workaround + .onAppear { + self.initialFocusBugWorkaround = true + } } private var errorView: some View { diff --git a/Packages/Status/Sources/Status/Row/StatusRowView.swift b/Packages/Status/Sources/Status/Row/StatusRowView.swift index 95abbdf0..73d20413 100644 --- a/Packages/Status/Sources/Status/Row/StatusRowView.swift +++ b/Packages/Status/Sources/Status/Row/StatusRowView.swift @@ -13,6 +13,7 @@ public struct StatusRowView: View { @Environment(\.isCompact) private var isCompact: Bool @Environment(\.accessibilityEnabled) private var accessibilityEnabled + @EnvironmentObject private var quickLook: QuickLook @EnvironmentObject private var theme: Theme @StateObject var viewModel: StatusRowViewModel @@ -119,6 +120,7 @@ public struct StatusRowView: View { .accessibilityElement(children: viewModel.isFocused ? .contain : .combine) .accessibilityLabel(viewModel.isFocused == false && accessibilityEnabled ? CombinedAccessibilityLabel(viewModel: viewModel).finalLabel() : Text("")) + .accessibilityHidden(viewModel.filter?.filter.filterAction == .hide) .accessibilityAction { viewModel.navigateToDetail() } @@ -175,6 +177,16 @@ public struct StatusRowView: View { viewModel.routerPath.presentedSheet = .quoteStatusEditor(status: viewModel.status) } + if viewModel.finalStatus.mediaAttachments.isEmpty == false { + Button("accessibility.status.media-viewer-action.label") { + HapticManager.shared.fireHaptic(of: .notification(.success)) + Task { + let attachments = viewModel.finalStatus.mediaAttachments + await quickLook.prepareFor(urls: attachments.compactMap { $0.url }, selectedURL: attachments[0].url!) + } + } + } + Button(viewModel.displaySpoiler ? "status.show-more" : "status.show-less") { withAnimation { viewModel.displaySpoiler.toggle() @@ -229,6 +241,9 @@ public struct StatusRowView: View { Text("status.filter.show-anyway") } } + .accessibilityAction { + viewModel.isFiltered = false + } } private var remoteContentLoadingView: some View { @@ -268,8 +283,23 @@ private struct CombinedAccessibilityLabel { viewModel.status.reblog != nil } + var filter: Filter? { + guard viewModel.isFiltered else { + return nil + } + return viewModel.filter?.filter + } + func finalLabel() -> Text { - userNamePreamble() + + if let filter { + switch filter.filterAction { + case .warn: + return Text("status.filter.filtered-by-\(filter.title)") + case .hide: + return Text("") + } + } else { + return userNamePreamble() + Text(hasSpoiler ? viewModel.finalStatus.spoilerText.asRawText : viewModel.finalStatus.content.asRawText @@ -284,6 +314,8 @@ private struct CombinedAccessibilityLabel { Text("status.summary.n-replies \(viewModel.finalStatus.repliesCount)") + Text(", ") + Text("status.summary.n-boosts \(viewModel.finalStatus.reblogsCount)") + Text(", ") + Text("status.summary.n-favorites \(viewModel.finalStatus.favouritesCount)") + + } } func userNamePreamble() -> Text {