Isolate pending statuses observer for smoother scrolling
This commit is contained in:
parent
0f98337a13
commit
0695fd5733
|
@ -20,3 +20,27 @@ class PendingStatusesObserver: ObservableObject {
|
|||
|
||||
init() { }
|
||||
}
|
||||
|
||||
struct PendingStatusesObserverView: View {
|
||||
@ObservedObject var observer: PendingStatusesObserver
|
||||
@State var proxy: ScrollViewProxy
|
||||
|
||||
var body: some View {
|
||||
if observer.pendingStatusesCount > 0 {
|
||||
HStack(spacing: 6) {
|
||||
Spacer()
|
||||
Button {
|
||||
withAnimation {
|
||||
proxy.scrollTo(observer.pendingStatuses.last, anchor: .bottom)
|
||||
}
|
||||
} label: {
|
||||
Text("\(observer.pendingStatusesCount)")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.background(.thinMaterial)
|
||||
.cornerRadius(8)
|
||||
}
|
||||
.padding(12)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,7 @@ public struct TimelineView: View {
|
|||
@EnvironmentObject private var routerPath: RouterPath
|
||||
|
||||
@StateObject private var viewModel = TimelineViewModel()
|
||||
@StateObject private var pendingStatusesObserver = PendingStatusesObserver()
|
||||
|
||||
|
||||
@State private var scrollProxy: ScrollViewProxy?
|
||||
|
||||
@Binding var timeline: TimelineFilter
|
||||
|
@ -64,7 +63,6 @@ public struct TimelineView: View {
|
|||
.navigationTitle(timeline.localizedTitle())
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.onAppear {
|
||||
viewModel.pendingStatusesObserver = pendingStatusesObserver
|
||||
if viewModel.client == nil {
|
||||
viewModel.client = client
|
||||
viewModel.timeline = timeline
|
||||
|
@ -112,22 +110,7 @@ public struct TimelineView: View {
|
|||
|
||||
@ViewBuilder
|
||||
private func makePendingNewPostsView(proxy: ScrollViewProxy) -> some View {
|
||||
if pendingStatusesObserver.pendingStatusesCount > 0 {
|
||||
HStack(spacing: 6) {
|
||||
Spacer()
|
||||
Button {
|
||||
withAnimation {
|
||||
proxy.scrollTo(pendingStatusesObserver.pendingStatuses.last, anchor: .bottom)
|
||||
}
|
||||
} label: {
|
||||
Text("\(pendingStatusesObserver.pendingStatusesCount)")
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.background(.thinMaterial)
|
||||
.cornerRadius(8)
|
||||
}
|
||||
.padding(12)
|
||||
}
|
||||
PendingStatusesObserverView(observer: viewModel.pendingStatusesObserver, proxy: proxy)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
|
|
@ -17,7 +17,7 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
// Internal source of truth for a timeline.
|
||||
private var statuses: [Status] = []
|
||||
|
||||
var pendingStatusesObserver: PendingStatusesObserver?
|
||||
var pendingStatusesObserver: PendingStatusesObserver = .init()
|
||||
|
||||
@Published var statusesState: StatusesState = .loading
|
||||
@Published var timeline: TimelineFilter = .federated {
|
||||
|
@ -25,7 +25,7 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
Task {
|
||||
if oldValue != timeline {
|
||||
statuses = []
|
||||
pendingStatusesObserver?.pendingStatuses = []
|
||||
pendingStatusesObserver.pendingStatuses = []
|
||||
tag = nil
|
||||
}
|
||||
await fetchStatuses(userIntent: false)
|
||||
|
@ -58,7 +58,7 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
guard let client else { return }
|
||||
do {
|
||||
if statuses.isEmpty {
|
||||
pendingStatusesObserver?.pendingStatuses = []
|
||||
pendingStatusesObserver.pendingStatuses = []
|
||||
statusesState = .loading
|
||||
statuses = try await client.get(endpoint: timeline.endpoint(sinceId: nil,
|
||||
maxId: nil,
|
||||
|
@ -71,7 +71,7 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
var newStatuses: [Status] = await fetchNewPages(minId: first.id, maxPages: 20)
|
||||
if userIntent || !pendingStatusesEnabled {
|
||||
statuses.insert(contentsOf: newStatuses, at: 0)
|
||||
pendingStatusesObserver?.pendingStatuses = []
|
||||
pendingStatusesObserver.pendingStatuses = []
|
||||
withAnimation {
|
||||
statusesState = .display(statuses: statuses, nextPageState: statuses.count < 20 ? .none : .hasNextPage)
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
newStatuses = newStatuses.filter { status in
|
||||
!statuses.contains(where: { $0.id == status.id })
|
||||
}
|
||||
pendingStatusesObserver?.pendingStatuses.insert(contentsOf: newStatuses.map{ $0.id }, at: 0)
|
||||
pendingStatusesObserver.pendingStatuses.insert(contentsOf: newStatuses.map{ $0.id }, at: 0)
|
||||
statuses.insert(contentsOf: newStatuses, at: 0)
|
||||
withAnimation {
|
||||
statusesState = .display(statuses: statuses, nextPageState: statuses.count < 20 ? .none : .hasNextPage)
|
||||
|
@ -143,7 +143,7 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
pendingStatusesEnabled,
|
||||
!statuses.contains(where: { $0.id == event.status.id })
|
||||
{
|
||||
pendingStatusesObserver?.pendingStatuses.insert(event.status.id, at: 0)
|
||||
pendingStatusesObserver.pendingStatuses.insert(event.status.id, at: 0)
|
||||
statuses.insert(event.status, at: 0)
|
||||
withAnimation {
|
||||
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
|
||||
|
@ -162,6 +162,6 @@ class TimelineViewModel: ObservableObject, StatusesFetcher {
|
|||
}
|
||||
|
||||
func statusDidAppear(status: Status) {
|
||||
pendingStatusesObserver?.removeStatus(status: status)
|
||||
pendingStatusesObserver.removeStatus(status: status)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue