New pending statuses behavior

This commit is contained in:
Thomas Ricouard 2024-09-13 11:27:01 +02:00
parent 7c5f2aea81
commit 491090e373
3 changed files with 29 additions and 21 deletions

View file

@ -10,7 +10,6 @@ import SwiftUI
var pendingStatusesCount: Int = 0 var pendingStatusesCount: Int = 0
var disableUpdate: Bool = false var disableUpdate: Bool = false
var scrollToIndex: ((Int) -> Void)?
var isLoadingNewStatuses: Bool = false var isLoadingNewStatuses: Bool = false
@ -39,9 +38,7 @@ struct TimelineUnreadStatusesView: View {
var body: some View { var body: some View {
if observer.pendingStatusesCount > 0 || observer.isLoadingNewStatuses { if observer.pendingStatusesCount > 0 || observer.isLoadingNewStatuses {
Button { Button { } label: {
observer.scrollToIndex?(observer.pendingStatusesCount)
} label: {
HStack(spacing: 8) { HStack(spacing: 8) {
if observer.isLoadingNewStatuses { if observer.isLoadingNewStatuses {
ProgressView() ProgressView()

View file

@ -99,8 +99,7 @@ public struct TimelineView: View {
{ {
collectionView.scrollToItem(at: .init(row: newValue, section: 0), collectionView.scrollToItem(at: .init(row: newValue, section: 0),
at: .top, at: .top,
animated: viewModel.scrollToIndexAnimated) animated: false)
viewModel.scrollToIndexAnimated = false
viewModel.scrollToIndex = nil viewModel.scrollToIndex = nil
} }
} }

View file

@ -80,13 +80,7 @@ import SwiftUI
} }
} }
var scrollToTopVisible: Bool = false { var scrollToTopVisible: Bool = false
didSet {
if scrollToTopVisible {
pendingStatusesObserver.pendingStatuses = []
}
}
}
var serverName: String { var serverName: String {
client?.server ?? "Error" client?.server ?? "Error"
@ -94,15 +88,10 @@ import SwiftUI
var isTimelineVisible: Bool = false var isTimelineVisible: Bool = false
let pendingStatusesObserver: TimelineUnreadStatusesObserver = .init() let pendingStatusesObserver: TimelineUnreadStatusesObserver = .init()
var scrollToIndexAnimated: Bool = false
var marker: Marker.Content? var marker: Marker.Content?
init(statusFetcher: TimelineStatusFetching = TimelineStatusFetcher()) { init(statusFetcher: TimelineStatusFetching = TimelineStatusFetcher()) {
self.statusFetcher = statusFetcher self.statusFetcher = statusFetcher
pendingStatusesObserver.scrollToIndex = { [weak self] index in
self?.scrollToIndexAnimated = true
self?.scrollToIndex = index
}
} }
private func fetchTag(id: String) async { private func fetchTag(id: String) async {
@ -159,6 +148,12 @@ extension TimelineViewModel {
extension TimelineViewModel: StatusesFetcher { extension TimelineViewModel: StatusesFetcher {
func pullToRefresh() async { func pullToRefresh() async {
timelineTask?.cancel() timelineTask?.cancel()
if !pendingStatusesObserver.pendingStatuses.isEmpty {
await dequeuePendingStatuses()
return
}
if !timeline.supportNewestPagination || UserPreferences.shared.fastRefreshEnabled { if !timeline.supportNewestPagination || UserPreferences.shared.fastRefreshEnabled {
await reset() await reset()
} }
@ -245,7 +240,6 @@ extension TimelineViewModel: StatusesFetcher {
{ {
// Restore cache and scroll to latest seen status. // Restore cache and scroll to latest seen status.
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage) statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
scrollToIndexAnimated = false
scrollToIndex = index + 1 scrollToIndex = index + 1
} else { } else {
// Restore cache and scroll to top. // Restore cache and scroll to top.
@ -309,6 +303,23 @@ extension TimelineViewModel: StatusesFetcher {
return newStatuses return newStatuses
} }
private func dequeuePendingStatuses() async {
canStreamEvents = false
pendingStatusesObserver.disableUpdate = true
let statuses = await datasource.getFiltered()
let newStatuses = pendingStatusesObserver.pendingStatuses.count + 1
statusesState = .display(statuses: statuses, nextPageState: .hasNextPage)
try? await Task.sleep(for: .milliseconds(0.005))
scrollToIndex = newStatuses
DispatchQueue.main.async { [weak self] in
self?.pendingStatusesObserver.disableUpdate = false
self?.canStreamEvents = true
}
if pendingStatusesObserver.pendingStatuses.count <= 2 {
pendingStatusesObserver.pendingStatuses = []
}
}
private func updateTimelineWithNewStatuses(_ newStatuses: [Status]) async { private func updateTimelineWithNewStatuses(_ newStatuses: [Status]) async {
let topStatus = await datasource.getFiltered().first let topStatus = await datasource.getFiltered().first
await datasource.insert(contentOf: newStatuses, at: 0) await datasource.insert(contentOf: newStatuses, at: 0)
@ -332,7 +343,6 @@ extension TimelineViewModel: StatusesFetcher {
private func updateTimelineWithScrollToTop(newStatuses: [Status], statuses: [Status], nextPageState: StatusesState.PagingState) { private func updateTimelineWithScrollToTop(newStatuses: [Status], statuses: [Status], nextPageState: StatusesState.PagingState) {
pendingStatusesObserver.disableUpdate = true pendingStatusesObserver.disableUpdate = true
statusesState = .display(statuses: statuses, nextPageState: nextPageState) statusesState = .display(statuses: statuses, nextPageState: nextPageState)
scrollToIndexAnimated = false
scrollToIndex = newStatuses.count + 1 scrollToIndex = newStatuses.count + 1
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
@ -437,8 +447,10 @@ extension TimelineViewModel {
await datasource.insert(event.status, at: 0) await datasource.insert(event.status, at: 0)
await cache() await cache()
StatusDataControllerProvider.shared.updateDataControllers(for: [event.status], client: client) StatusDataControllerProvider.shared.updateDataControllers(for: [event.status], client: client)
if scrollToTopVisible, pendingStatusesObserver.pendingStatuses.isEmpty {
await updateStatusesState() await updateStatusesState()
} }
}
private func handleDeleteEvent(_ event: StreamEventDelete) async { private func handleDeleteEvent(_ event: StreamEventDelete) async {
await datasource.remove(event.status) await datasource.remove(event.status)