StatusRow: Light cleanup
This commit is contained in:
parent
0c13cbd61f
commit
c4b85679a2
|
@ -0,0 +1,122 @@
|
|||
import SwiftUI
|
||||
import Models
|
||||
|
||||
/// A utility that creates a suitable combined accessibility label for a `StatusRowView` that is not focused.
|
||||
@MainActor
|
||||
struct StatusRowAccessibilityLabel {
|
||||
let viewModel: StatusRowViewModel
|
||||
|
||||
var hasSpoiler: Bool {
|
||||
viewModel.displaySpoiler && viewModel.finalStatus.spoilerText.asRawText.isEmpty == false
|
||||
}
|
||||
|
||||
var isReply: Bool {
|
||||
if let accountId = viewModel.status.inReplyToAccountId, viewModel.status.mentions.contains(where: { $0.id == accountId }) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var isBoost: Bool {
|
||||
viewModel.status.reblog != nil
|
||||
}
|
||||
|
||||
var filter: Filter? {
|
||||
guard viewModel.isFiltered else {
|
||||
return nil
|
||||
}
|
||||
return viewModel.filter?.filter
|
||||
}
|
||||
|
||||
func finalLabel() -> Text {
|
||||
if let filter {
|
||||
switch filter.filterAction {
|
||||
case .warn:
|
||||
Text("status.filter.filtered-by-\(filter.title)")
|
||||
case .hide:
|
||||
Text("")
|
||||
}
|
||||
} else {
|
||||
userNamePreamble() +
|
||||
Text(hasSpoiler
|
||||
? viewModel.finalStatus.spoilerText.asRawText
|
||||
: viewModel.finalStatus.content.asRawText
|
||||
) +
|
||||
Text(hasSpoiler
|
||||
? "status.editor.spoiler"
|
||||
: ""
|
||||
) + Text(", ") +
|
||||
pollText() +
|
||||
imageAltText() +
|
||||
Text(viewModel.finalStatus.createdAt.relativeFormatted) + Text(", ") +
|
||||
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 {
|
||||
switch (isReply, isBoost) {
|
||||
case (true, false):
|
||||
Text("accessibility.status.a-replied-to-\(finalUserDisplayName())") + Text(" ")
|
||||
case (_, true):
|
||||
Text("accessibility.status.a-boosted-b-\(userDisplayName())-\(finalUserDisplayName())") + Text(", ")
|
||||
default:
|
||||
Text(userDisplayName()) + Text(", ")
|
||||
}
|
||||
}
|
||||
|
||||
func userDisplayName() -> String {
|
||||
viewModel.status.account.displayNameWithoutEmojis.count < 4
|
||||
? viewModel.status.account.safeDisplayName
|
||||
: viewModel.status.account.displayNameWithoutEmojis
|
||||
}
|
||||
|
||||
func finalUserDisplayName() -> String {
|
||||
viewModel.finalStatus.account.displayNameWithoutEmojis.count < 4
|
||||
? viewModel.finalStatus.account.safeDisplayName
|
||||
: viewModel.finalStatus.account.displayNameWithoutEmojis
|
||||
}
|
||||
|
||||
func imageAltText() -> Text {
|
||||
let descriptions = viewModel.finalStatus.mediaAttachments
|
||||
.compactMap(\.description)
|
||||
|
||||
if descriptions.count == 1 {
|
||||
return Text("accessibility.image.alt-text-\(descriptions[0])") + Text(", ")
|
||||
} else if descriptions.count > 1 {
|
||||
return Text("accessibility.image.alt-text-\(descriptions[0])") + Text(", ") + Text("accessibility.image.alt-text-more.label") + Text(", ")
|
||||
} else if viewModel.finalStatus.mediaAttachments.isEmpty == false {
|
||||
let differentTypes = Set(viewModel.finalStatus.mediaAttachments.compactMap(\.localizedTypeDescription)).sorted()
|
||||
return Text("accessibility.status.contains-media.label-\(ListFormatter.localizedString(byJoining: differentTypes))") + Text(", ")
|
||||
} else {
|
||||
return Text("")
|
||||
}
|
||||
}
|
||||
|
||||
func pollText() -> Text {
|
||||
if let poll = viewModel.finalStatus.poll {
|
||||
let showPercentage = poll.expired || poll.voted ?? false
|
||||
let title: LocalizedStringKey = poll.expired
|
||||
? "accessibility.status.poll.finished.label"
|
||||
: "accessibility.status.poll.active.label"
|
||||
|
||||
return poll.options.enumerated().reduce(into: Text(title)) { text, pair in
|
||||
let (index, option) = pair
|
||||
let selected = poll.ownVotes?.contains(index) ?? false
|
||||
let percentage = poll.safeVotersCount > 0 && option.votesCount != nil
|
||||
? Int(round(Double(option.votesCount!) / Double(poll.safeVotersCount) * 100))
|
||||
: 0
|
||||
|
||||
text = text +
|
||||
Text(selected ? "accessibility.status.poll.selected.label" : "") +
|
||||
Text(", ") +
|
||||
Text("accessibility.status.poll.option-prefix-\(index + 1)-of-\(poll.options.count)") +
|
||||
Text(", ") +
|
||||
Text(option.title) +
|
||||
Text(showPercentage ? ", \(percentage)%. " : ". ")
|
||||
}
|
||||
}
|
||||
return Text("")
|
||||
}
|
||||
}
|
|
@ -149,7 +149,7 @@ public struct StatusRowView: View {
|
|||
trailing: .layoutPadding))
|
||||
.accessibilityElement(children: isFocused ? .contain : .combine)
|
||||
.accessibilityLabel(isFocused == false && accessibilityVoiceOverEnabled
|
||||
? CombinedAccessibilityLabel(viewModel: viewModel).finalLabel() : Text(""))
|
||||
? StatusRowAccessibilityLabel(viewModel: viewModel).finalLabel() : Text(""))
|
||||
.accessibilityHidden(viewModel.filter?.filter.filterAction == .hide)
|
||||
.accessibilityAction {
|
||||
guard !isFocused else { return }
|
||||
|
@ -312,122 +312,3 @@ public struct StatusRowView: View {
|
|||
}
|
||||
}
|
||||
|
||||
/// A utility that creates a suitable combined accessibility label for a `StatusRowView` that is not focused.
|
||||
@MainActor
|
||||
private struct CombinedAccessibilityLabel {
|
||||
let viewModel: StatusRowViewModel
|
||||
|
||||
var hasSpoiler: Bool {
|
||||
viewModel.displaySpoiler && viewModel.finalStatus.spoilerText.asRawText.isEmpty == false
|
||||
}
|
||||
|
||||
var isReply: Bool {
|
||||
if let accountId = viewModel.status.inReplyToAccountId, viewModel.status.mentions.contains(where: { $0.id == accountId }) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var isBoost: Bool {
|
||||
viewModel.status.reblog != nil
|
||||
}
|
||||
|
||||
var filter: Filter? {
|
||||
guard viewModel.isFiltered else {
|
||||
return nil
|
||||
}
|
||||
return viewModel.filter?.filter
|
||||
}
|
||||
|
||||
func finalLabel() -> Text {
|
||||
if let filter {
|
||||
switch filter.filterAction {
|
||||
case .warn:
|
||||
Text("status.filter.filtered-by-\(filter.title)")
|
||||
case .hide:
|
||||
Text("")
|
||||
}
|
||||
} else {
|
||||
userNamePreamble() +
|
||||
Text(hasSpoiler
|
||||
? viewModel.finalStatus.spoilerText.asRawText
|
||||
: viewModel.finalStatus.content.asRawText
|
||||
) +
|
||||
Text(hasSpoiler
|
||||
? "status.editor.spoiler"
|
||||
: ""
|
||||
) + Text(", ") +
|
||||
pollText() +
|
||||
imageAltText() +
|
||||
Text(viewModel.finalStatus.createdAt.relativeFormatted) + Text(", ") +
|
||||
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 {
|
||||
switch (isReply, isBoost) {
|
||||
case (true, false):
|
||||
Text("accessibility.status.a-replied-to-\(finalUserDisplayName())") + Text(" ")
|
||||
case (_, true):
|
||||
Text("accessibility.status.a-boosted-b-\(userDisplayName())-\(finalUserDisplayName())") + Text(", ")
|
||||
default:
|
||||
Text(userDisplayName()) + Text(", ")
|
||||
}
|
||||
}
|
||||
|
||||
func userDisplayName() -> String {
|
||||
viewModel.status.account.displayNameWithoutEmojis.count < 4
|
||||
? viewModel.status.account.safeDisplayName
|
||||
: viewModel.status.account.displayNameWithoutEmojis
|
||||
}
|
||||
|
||||
func finalUserDisplayName() -> String {
|
||||
viewModel.finalStatus.account.displayNameWithoutEmojis.count < 4
|
||||
? viewModel.finalStatus.account.safeDisplayName
|
||||
: viewModel.finalStatus.account.displayNameWithoutEmojis
|
||||
}
|
||||
|
||||
func imageAltText() -> Text {
|
||||
let descriptions = viewModel.finalStatus.mediaAttachments
|
||||
.compactMap(\.description)
|
||||
|
||||
if descriptions.count == 1 {
|
||||
return Text("accessibility.image.alt-text-\(descriptions[0])") + Text(", ")
|
||||
} else if descriptions.count > 1 {
|
||||
return Text("accessibility.image.alt-text-\(descriptions[0])") + Text(", ") + Text("accessibility.image.alt-text-more.label") + Text(", ")
|
||||
} else if viewModel.finalStatus.mediaAttachments.isEmpty == false {
|
||||
let differentTypes = Set(viewModel.finalStatus.mediaAttachments.compactMap(\.localizedTypeDescription)).sorted()
|
||||
return Text("accessibility.status.contains-media.label-\(ListFormatter.localizedString(byJoining: differentTypes))") + Text(", ")
|
||||
} else {
|
||||
return Text("")
|
||||
}
|
||||
}
|
||||
|
||||
func pollText() -> Text {
|
||||
if let poll = viewModel.finalStatus.poll {
|
||||
let showPercentage = poll.expired || poll.voted ?? false
|
||||
let title: LocalizedStringKey = poll.expired
|
||||
? "accessibility.status.poll.finished.label"
|
||||
: "accessibility.status.poll.active.label"
|
||||
|
||||
return poll.options.enumerated().reduce(into: Text(title)) { text, pair in
|
||||
let (index, option) = pair
|
||||
let selected = poll.ownVotes?.contains(index) ?? false
|
||||
let percentage = poll.safeVotersCount > 0 && option.votesCount != nil
|
||||
? Int(round(Double(option.votesCount!) / Double(poll.safeVotersCount) * 100))
|
||||
: 0
|
||||
|
||||
text = text +
|
||||
Text(selected ? "accessibility.status.poll.selected.label" : "") +
|
||||
Text(", ") +
|
||||
Text("accessibility.status.poll.option-prefix-\(index + 1)-of-\(poll.options.count)") +
|
||||
Text(", ") +
|
||||
Text(option.title) +
|
||||
Text(showPercentage ? ", \(percentage)%. " : ". ")
|
||||
}
|
||||
}
|
||||
return Text("")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -186,20 +186,6 @@ import SwiftUI
|
|||
}
|
||||
}
|
||||
|
||||
func navigateToMention(mention: Mention) {
|
||||
if isRemote {
|
||||
withAnimation {
|
||||
isLoadingRemoteContent = true
|
||||
}
|
||||
Task {
|
||||
await routerPath.navigateToAccountFrom(url: mention.url)
|
||||
isLoadingRemoteContent = false
|
||||
}
|
||||
} else {
|
||||
routerPath.navigate(to: .accountDetail(id: mention.id))
|
||||
}
|
||||
}
|
||||
|
||||
func goToParent() {
|
||||
guard let id = status.inReplyToId else { return }
|
||||
if let _ = scrollToId {
|
||||
|
|
Loading…
Reference in New Issue