StatusRow: Flatify the hierarchy to work around iOS 16.4 issues

This commit is contained in:
Thomas Ricouard 2023-03-01 17:34:03 +01:00
parent 87ef2f2a39
commit 43a4551d9b

View file

@ -10,35 +10,32 @@ public struct StatusRowView: View {
@Environment(\.isInCaptureMode) private var isInCaptureMode: Bool @Environment(\.isInCaptureMode) private var isInCaptureMode: Bool
@Environment(\.redactionReasons) private var reasons @Environment(\.redactionReasons) private var reasons
@Environment(\.isCompact) private var isCompact: Bool @Environment(\.isCompact) private var isCompact: Bool
@EnvironmentObject private var theme: Theme @EnvironmentObject private var theme: Theme
@EnvironmentObject private var client: Client @EnvironmentObject private var client: Client
@StateObject var viewModel: StatusRowViewModel @StateObject var viewModel: StatusRowViewModel
// StateObject accepts an @autoclosure which only allocates the view model once when the view gets on screen. // StateObject accepts an @autoclosure which only allocates the view model once when the view gets on screen.
public init(viewModel: @escaping () -> StatusRowViewModel) { public init(viewModel: @escaping () -> StatusRowViewModel) {
_viewModel = StateObject(wrappedValue: viewModel()) _viewModel = StateObject(wrappedValue: viewModel())
} }
var contextMenu: some View { var contextMenu: some View {
StatusRowContextMenu(viewModel: viewModel) StatusRowContextMenu(viewModel: viewModel)
} }
public var body: some View { public var body: some View {
if viewModel.isFiltered, let filter = viewModel.filter { VStack(alignment: .leading) {
switch filter.filter.filterAction {
case .warn:
makeFilterView(filter: filter.filter)
.listRowBackground(viewModel.highlightRowColor)
case .hide:
EmptyView()
.listRowSeparator(.hidden)
.listRowInsets(.init())
}
} else {
let status: AnyStatus = viewModel.status.reblog ?? viewModel.status let status: AnyStatus = viewModel.status.reblog ?? viewModel.status
VStack(alignment: .leading) { if viewModel.isFiltered, let filter = viewModel.filter {
switch filter.filter.filterAction {
case .warn:
makeFilterView(filter: filter.filter)
case .hide:
EmptyView()
}
} else {
if !isCompact, theme.avatarPosition == .leading { if !isCompact, theme.avatarPosition == .leading {
StatusRowReblogView(viewModel: viewModel) StatusRowReblogView(viewModel: viewModel)
StatusRowReplyView(viewModel: viewModel) StatusRowReplyView(viewModel: viewModel)
@ -85,76 +82,76 @@ public struct StatusRowView: View {
} }
} }
} }
.onAppear {
viewModel.markSeen()
if reasons.isEmpty {
if !isCompact, viewModel.embeddedStatus == nil {
Task {
await viewModel.loadEmbeddedStatus()
}
}
}
}
.contextMenu {
contextMenu
}
.swipeActions(edge: .trailing) {
if !isCompact {
StatusRowSwipeView(viewModel: viewModel, mode: .trailing)
}
}
.swipeActions(edge: .leading) {
if !isCompact {
StatusRowSwipeView(viewModel: viewModel, mode: .leading)
}
}
.listRowBackground(viewModel.highlightRowColor)
.listRowInsets(.init(top: 12,
leading: .layoutPadding,
bottom: 12,
trailing: .layoutPadding))
.accessibilityElement(children: viewModel.isFocused ? .contain : .combine)
.accessibilityActions {
if UIAccessibility.isVoiceOverRunning {
accesibilityActions
}
}
.background {
Color.clear
.contentShape(Rectangle())
.onTapGesture {
viewModel.navigateToDetail()
}
}
.overlay {
if viewModel.isLoadingRemoteContent {
remoteContentLoadingView
}
}
.alert(isPresented: $viewModel.showDeleteAlert, content: {
Alert(
title: Text("status.action.delete.confirm.title"),
message: Text("status.action.delete.confirm.message"),
primaryButton: .destructive(
Text("status.action.delete"))
{
Task {
await viewModel.delete()
}
},
secondaryButton: .cancel()
)
})
.alignmentGuide(.listRowSeparatorLeading) { _ in
-100
}
.environmentObject(
StatusDataControllerProvider.shared.dataController(for: viewModel.status.reblog ?? viewModel.status,
client: client)
)
} }
.onAppear {
viewModel.markSeen()
if reasons.isEmpty {
if !isCompact, viewModel.embeddedStatus == nil {
Task {
await viewModel.loadEmbeddedStatus()
}
}
}
}
.contextMenu {
contextMenu
}
.swipeActions(edge: .trailing) {
if !isCompact {
StatusRowSwipeView(viewModel: viewModel, mode: .trailing)
}
}
.swipeActions(edge: .leading) {
if !isCompact {
StatusRowSwipeView(viewModel: viewModel, mode: .leading)
}
}
.listRowBackground(viewModel.highlightRowColor)
.listRowInsets(.init(top: 12,
leading: .layoutPadding,
bottom: 12,
trailing: .layoutPadding))
.accessibilityElement(children: viewModel.isFocused ? .contain : .combine)
.accessibilityActions {
if UIAccessibility.isVoiceOverRunning {
accesibilityActions
}
}
.background {
Color.clear
.contentShape(Rectangle())
.onTapGesture {
viewModel.navigateToDetail()
}
}
.overlay {
if viewModel.isLoadingRemoteContent {
remoteContentLoadingView
}
}
.alert(isPresented: $viewModel.showDeleteAlert, content: {
Alert(
title: Text("status.action.delete.confirm.title"),
message: Text("status.action.delete.confirm.message"),
primaryButton: .destructive(
Text("status.action.delete"))
{
Task {
await viewModel.delete()
}
},
secondaryButton: .cancel()
)
})
.alignmentGuide(.listRowSeparatorLeading) { _ in
-100
}
.environmentObject(
StatusDataControllerProvider.shared.dataController(for: viewModel.status.reblog ?? viewModel.status,
client: client)
)
} }
@ViewBuilder @ViewBuilder
private var accesibilityActions: some View { private var accesibilityActions: some View {
// Add the individual mentions as accessibility actions // Add the individual mentions as accessibility actions
@ -163,20 +160,20 @@ public struct StatusRowView: View {
viewModel.routerPath.navigate(to: .accountDetail(id: mention.id)) viewModel.routerPath.navigate(to: .accountDetail(id: mention.id))
} }
} }
Button(viewModel.displaySpoiler ? "status.show-more" : "status.show-less") { Button(viewModel.displaySpoiler ? "status.show-more" : "status.show-less") {
withAnimation { withAnimation {
viewModel.displaySpoiler.toggle() viewModel.displaySpoiler.toggle()
} }
} }
Button("@\(viewModel.status.account.username)") { Button("@\(viewModel.status.account.username)") {
viewModel.routerPath.navigate(to: .accountDetail(id: viewModel.status.account.id)) viewModel.routerPath.navigate(to: .accountDetail(id: viewModel.status.account.id))
} }
contextMenu contextMenu
} }
private func makeFilterView(filter: Filter) -> some View { private func makeFilterView(filter: Filter) -> some View {
HStack { HStack {
Text("status.filter.filtered-by-\(filter.title)") Text("status.filter.filtered-by-\(filter.title)")
@ -189,7 +186,7 @@ public struct StatusRowView: View {
} }
} }
} }
private var remoteContentLoadingView: some View { private var remoteContentLoadingView: some View {
ZStack(alignment: .center) { ZStack(alignment: .center) {
VStack { VStack {