mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 08:15:24 +00:00
Prefetch the next room list page before reaching the bottom
Growing the list only once the last row was already visible, on top of the half a second visible range throttle, meant hitting and bouncing off the end of the list while the next page loaded. Ask for the next page half a page early instead, so the growth happens while the user is still scrolling towards the boundary. The threshold is clamped so that it stays positive for accounts with fewer rooms than half a page, where it would otherwise always match and leave the list unable to shrink back to a single page.
This commit is contained in:
@@ -68,7 +68,10 @@ struct HomeScreenContent: View {
|
||||
scrollViewAdapter.scrollView = scrollView
|
||||
}
|
||||
.onReceive(scrollViewAdapter.didScroll) { _ in
|
||||
updateVisibleRange()
|
||||
// Live, undelayed updates while scrolling: pagination needs to see the
|
||||
// range early enough to grow the list before the user reaches the bottom,
|
||||
// and the scroll geometry is valid mid-scroll.
|
||||
sendVisibleRange()
|
||||
}
|
||||
.onReceive(scrollViewAdapter.isScrolling) { _ in
|
||||
updateVisibleRange()
|
||||
@@ -133,13 +136,21 @@ struct HomeScreenContent: View {
|
||||
/// Often times the scroll view's content size isn't correct yet when this method is called e.g. when cancelling a search
|
||||
/// Dispatch it with a delay to allow the UI to update and the computations to be correct
|
||||
/// Once we move to iOS 17 we should remove all of this and use scroll anchors instead
|
||||
/// Update: We're on iOS 26 now and the scroll achors still don't work properly.
|
||||
private func updateVisibleRange() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { delayedUpdateVisibleRange() }
|
||||
}
|
||||
|
||||
private func delayedUpdateVisibleRange() {
|
||||
guard scrollViewAdapter.isScrolling.value == false else {
|
||||
// Scrolling reports live through didScroll
|
||||
return
|
||||
}
|
||||
sendVisibleRange()
|
||||
}
|
||||
|
||||
private func sendVisibleRange() {
|
||||
guard let scrollView = scrollViewAdapter.scrollView,
|
||||
scrollViewAdapter.isScrolling.value == false, // Ignore while scrolling
|
||||
context.searchQuery.isEmpty == true, // Ignore while filtering
|
||||
!context.viewState.visibleRooms.isEmpty else {
|
||||
return
|
||||
|
||||
@@ -19,6 +19,8 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
|
||||
private let appSettings: AppSettings
|
||||
|
||||
private let roomListPageSize: UInt32
|
||||
/// Remember how many rooms we had on the previous requests so we can deduplicate
|
||||
private var roomCountOnLastPageAddRequest = -1
|
||||
|
||||
private let visibleItemRangePublisher = CurrentValueSubject<Range<Int>, Never>(0..<0)
|
||||
|
||||
@@ -182,6 +184,24 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
|
||||
// MARK: - Private
|
||||
|
||||
private func setupVisibleRangeObservers() {
|
||||
// Unthrottled to add another page half way through the last one
|
||||
visibleItemRangePublisher
|
||||
.sink { [weak self] range in
|
||||
guard let self,
|
||||
!range.isEmpty,
|
||||
range.upperBound >= rooms.count - Int(roomListPageSize) / 2,
|
||||
rooms.count != roomCountOnLastPageAddRequest else {
|
||||
return
|
||||
}
|
||||
|
||||
roomCountOnLastPageAddRequest = rooms.count
|
||||
|
||||
MXLog.info("\(self.name): Adding a page at \(rooms.count) rooms, visible range: \(range)")
|
||||
|
||||
listUpdatesSubscriptionResult?.controller().addOnePage()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
visibleItemRangePublisher
|
||||
.throttle(for: 0.5, scheduler: DispatchQueue.main, latest: true)
|
||||
.removeDuplicates()
|
||||
@@ -190,9 +210,8 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
|
||||
|
||||
MXLog.info("\(self.name): Updating visible range: \(range)")
|
||||
|
||||
if range.upperBound >= rooms.count {
|
||||
listUpdatesSubscriptionResult?.controller().addOnePage()
|
||||
} else if range.lowerBound == 0 {
|
||||
if range.lowerBound == 0, range.upperBound < rooms.count {
|
||||
roomCountOnLastPageAddRequest = -1
|
||||
listUpdatesSubscriptionResult?.controller().resetToOnePage()
|
||||
}
|
||||
}
|
||||
@@ -215,6 +234,8 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
|
||||
range = range.lowerBound..<upperBound
|
||||
}
|
||||
|
||||
MXLog.info("\(self.name): Subscribing to rooms in range: \(range)")
|
||||
|
||||
return range
|
||||
.filter { $0 < self.rooms.count }
|
||||
.map { self.rooms[$0].id }
|
||||
|
||||
Reference in New Issue
Block a user