Proper threading UI

This commit is contained in:
Thomas Ricouard 2023-02-16 17:07:52 +01:00
parent 0b5ad32a92
commit ac32410200
3 changed files with 26 additions and 18 deletions

View File

@ -45,6 +45,7 @@ public protocol AnyStatus {
var emojis: [Emoji] { get } var emojis: [Emoji] { get }
var url: String? { get } var url: String? { get }
var application: Application? { get } var application: Application? { get }
var inReplyToId: String? { get }
var inReplyToAccountId: String? { get } var inReplyToAccountId: String? { get }
var visibility: Visibility { get } var visibility: Visibility { get }
var poll: Poll? { get } var poll: Poll? { get }
@ -97,6 +98,7 @@ public struct Status: AnyStatus, Codable, Identifiable, Equatable, Hashable, Sta
public let emojis: [Emoji] public let emojis: [Emoji]
public let url: String? public let url: String?
public let application: Application? public let application: Application?
public let inReplyToId: String?
public let inReplyToAccountId: String? public let inReplyToAccountId: String?
public let visibility: Visibility public let visibility: Visibility
public let poll: Poll? public let poll: Poll?
@ -126,6 +128,7 @@ public struct Status: AnyStatus, Codable, Identifiable, Equatable, Hashable, Sta
emojis: [], emojis: [],
url: "https://example.com", url: "https://example.com",
application: nil, application: nil,
inReplyToId: nil,
inReplyToAccountId: nil, inReplyToAccountId: nil,
visibility: .pub, visibility: .pub,
poll: nil, poll: nil,
@ -160,6 +163,7 @@ public struct Status: AnyStatus, Codable, Identifiable, Equatable, Hashable, Sta
emojis: reblog.emojis, emojis: reblog.emojis,
url: reblog.url, url: reblog.url,
application: reblog.application, application: reblog.application,
inReplyToId: reblog.inReplyToId,
inReplyToAccountId: reblog.inReplyToAccountId, inReplyToAccountId: reblog.inReplyToAccountId,
visibility: reblog.visibility, visibility: reblog.visibility,
poll: reblog.poll, poll: reblog.poll,
@ -199,6 +203,7 @@ public struct ReblogStatus: AnyStatus, Codable, Identifiable, Equatable, Hashabl
public let emojis: [Emoji] public let emojis: [Emoji]
public let url: String? public let url: String?
public var application: Application? public var application: Application?
public let inReplyToId: String?
public let inReplyToAccountId: String? public let inReplyToAccountId: String?
public let visibility: Visibility public let visibility: Visibility
public let poll: Poll? public let poll: Poll?

View File

@ -40,17 +40,8 @@ public struct StatusDetailView: View {
case .loading: case .loading:
loadingDetailView loadingDetailView
case let .display(status, context, date): case let .display(statuses):
if !context.ancestors.isEmpty { makeStatusesListView(statuses: statuses)
makeStatusesListView(statuses: context.ancestors)
}
makeCurrentStatusView(status: status)
.id(date)
if !context.descendants.isEmpty {
makeStatusesListView(statuses: context.descendants)
}
if !isLoaded { if !isLoaded {
loadingContextView loadingContextView
@ -107,7 +98,7 @@ public struct StatusDetailView: View {
var isReplyToPrevious: Bool = false var isReplyToPrevious: Bool = false
if let index = statuses.firstIndex(where: { $0.id == status.id }), if let index = statuses.firstIndex(where: { $0.id == status.id }),
index > 0, index > 0,
statuses[index - 1].inReplyToAccountId == status.account.id { statuses[index - 1].id == status.inReplyToId {
isReplyToPrevious = true isReplyToPrevious = true
} }
let viewModel: StatusRowViewModel = .init(status: status, let viewModel: StatusRowViewModel = .init(status: status,
@ -120,7 +111,12 @@ public struct StatusDetailView: View {
.fill(theme.tintColor) .fill(theme.tintColor)
.frame(width: 2) .frame(width: 2)
} }
StatusRowView(viewModel: viewModel) if self.viewModel.statusId == status.id {
makeCurrentStatusView(status: status)
.id(Date())
} else {
StatusRowView(viewModel: viewModel)
}
} }
.listRowBackground(viewModel.highlightRowColor) .listRowBackground(viewModel.highlightRowColor)
.listRowInsets(.init(top: 12, .listRowInsets(.init(top: 12,
@ -135,7 +131,7 @@ public struct StatusDetailView: View {
client: client, client: client,
routerPath: routerPath, routerPath: routerPath,
isCompact: false, isCompact: false,
isFocused: true)) isFocused: !viewModel.isLoadingContext))
.overlay { .overlay {
GeometryReader { reader in GeometryReader { reader in
VStack {} VStack {}

View File

@ -11,10 +11,11 @@ class StatusDetailViewModel: ObservableObject {
var client: Client? var client: Client?
enum State { enum State {
case loading, display(status: Status, context: StatusContext, date: Date), error(error: Error) case loading, display(statuses: [Status]), error(error: Error)
} }
@Published var state: State = .loading @Published var state: State = .loading
@Published var isLoadingContext = false
@Published var title: LocalizedStringKey = "" @Published var title: LocalizedStringKey = ""
@Published var scrollToId: String? @Published var scrollToId: String?
@ -25,7 +26,7 @@ class StatusDetailViewModel: ObservableObject {
} }
init(status: Status) { init(status: Status) {
state = .display(status: status, context: .empty(), date: Date()) state = .display(statuses: [status])
title = "status.post-from-\(status.account.displayNameWithoutEmojis)" title = "status.post-from-\(status.account.displayNameWithoutEmojis)"
statusId = status.id statusId = status.id
remoteStatusURL = nil remoteStatusURL = nil
@ -71,14 +72,20 @@ class StatusDetailViewModel: ObservableObject {
private func fetchStatusDetail(animate: Bool) async { private func fetchStatusDetail(animate: Bool) async {
guard let client, let statusId else { return } guard let client, let statusId else { return }
do { do {
isLoadingContext = true
let data = try await fetchContextData(client: client, statusId: statusId) let data = try await fetchContextData(client: client, statusId: statusId)
title = "status.post-from-\(data.status.account.displayNameWithoutEmojis)" title = "status.post-from-\(data.status.account.displayNameWithoutEmojis)"
var statuses = data.context.ancestors
statuses.append(data.status)
statuses.append(contentsOf: data.context.descendants)
if animate { if animate {
withAnimation { withAnimation {
state = .display(status: data.status, context: data.context, date: Date()) isLoadingContext = false
state = .display(statuses: statuses)
} }
} else { } else {
state = .display(status: data.status, context: data.context, date: Date()) isLoadingContext = false
state = .display(statuses: statuses)
scrollToId = statusId scrollToId = statusId
} }
} catch { } catch {