mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 20:09:04 +00:00
Fix room pickers not loading more than the first page of rooms
The forward, share, add-to-space and search pickers signal reaching the
bottom of the list with an empty `roomCount..<roomCount` range. The
prefetch sink added in 9dd9fd917 guards on `!range.isEmpty` to skip the
initial `0..<0`, which also dropped the pickers' bottom signal, so they
never loaded past the first 100 rooms.
Only skip `0..<0` instead, which leaves the home screen prefetch as it
was, and cover the bottom, prefetch and top cases in
RoomSummaryProviderTests.
This commit is contained in:
@@ -192,7 +192,8 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
|
|||||||
visibleItemRangePublisher
|
visibleItemRangePublisher
|
||||||
.sink { [weak self] range in
|
.sink { [weak self] range in
|
||||||
guard let self,
|
guard let self,
|
||||||
!range.isEmpty,
|
// Pickers send an empty `count..<count` range when reaching the bottom, so only ignore `0..<0` (the initial value or the top).
|
||||||
|
range.upperBound > 0,
|
||||||
range.upperBound >= rooms.count - Int(roomListPageSize) / 2,
|
range.upperBound >= rooms.count - Int(roomListPageSize) / 2,
|
||||||
rooms.count != roomCountOnLastPageAddRequest else {
|
rooms.count != roomCountOnLastPageAddRequest else {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ final class RoomSummaryProviderTests {
|
|||||||
var dynamicEntriesController: RoomListDynamicEntriesControllerSDKMock!
|
var dynamicEntriesController: RoomListDynamicEntriesControllerSDKMock!
|
||||||
var roomSummaryProvider: RoomSummaryProvider!
|
var roomSummaryProvider: RoomSummaryProvider!
|
||||||
|
|
||||||
|
enum TestError: Error {
|
||||||
|
case missingRoomInfo
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
func defaultRustFilters() async {
|
func defaultRustFilters() async {
|
||||||
// Given a new room provider.
|
// Given a new room provider.
|
||||||
@@ -119,9 +123,63 @@ final class RoomSummaryProviderTests {
|
|||||||
#expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: [.all(filters: [.favourite, .joined])] + baseFilters + [.identifiers(identifiers: ["SomeRoom"])]))
|
#expect(dynamicEntriesController.setFilterKindReceivedInvocations.last == .all(filters: [.all(filters: [.favourite, .joined])] + baseFilters + [.identifiers(identifiers: ["SomeRoom"])]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func reachingTheBottomAddsAPage() async throws {
|
||||||
|
// Given a provider showing its first page of rooms.
|
||||||
|
setup(roomListPageSize: 10)
|
||||||
|
try await loadRooms(count: 10)
|
||||||
|
|
||||||
|
// When a picker reports reaching the bottom with an empty range at the end of the list.
|
||||||
|
roomSummaryProvider.updateVisibleRange(10..<10)
|
||||||
|
|
||||||
|
// Then the next page should be requested.
|
||||||
|
#expect(dynamicEntriesController.addOnePageCallsCount == 1)
|
||||||
|
|
||||||
|
// When reaching the bottom again before that page has loaded.
|
||||||
|
roomSummaryProvider.updateVisibleRange(10..<10)
|
||||||
|
|
||||||
|
// Then the request shouldn't be repeated.
|
||||||
|
#expect(dynamicEntriesController.addOnePageCallsCount == 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func scrollingNearTheBottomPrefetchesAPage() async throws {
|
||||||
|
// Given a provider showing its first page of rooms.
|
||||||
|
setup(roomListPageSize: 10)
|
||||||
|
try await loadRooms(count: 10)
|
||||||
|
|
||||||
|
// When scrolling to more than half a page away from the bottom.
|
||||||
|
roomSummaryProvider.updateVisibleRange(0..<4)
|
||||||
|
|
||||||
|
// Then no page should be requested yet.
|
||||||
|
#expect(dynamicEntriesController.addOnePageCallsCount == 0)
|
||||||
|
|
||||||
|
// When scrolling to half a page away from the bottom.
|
||||||
|
roomSummaryProvider.updateVisibleRange(1..<5)
|
||||||
|
|
||||||
|
// Then the next page should be requested.
|
||||||
|
#expect(dynamicEntriesController.addOnePageCallsCount == 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
func reachingTheTopDoesNotAddAPage() async throws {
|
||||||
|
// Given a provider with fewer rooms than half a page.
|
||||||
|
setup(roomListPageSize: 10)
|
||||||
|
try await loadRooms(count: 4)
|
||||||
|
|
||||||
|
// When a picker reports reaching the top with an empty range at the start of the list.
|
||||||
|
roomSummaryProvider.updateVisibleRange(0..<0)
|
||||||
|
|
||||||
|
// Then no page should be requested.
|
||||||
|
#expect(dynamicEntriesController.addOnePageCallsCount == 0)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
private func setup(isLowPriorityFilterEnabled: Bool = false, includesOnlyJoinedRooms: Bool = false, setsRoomList: Bool = true) {
|
private func setup(isLowPriorityFilterEnabled: Bool = false,
|
||||||
|
includesOnlyJoinedRooms: Bool = false,
|
||||||
|
roomListPageSize: UInt32 = 100,
|
||||||
|
setsRoomList: Bool = true) {
|
||||||
userSettings = UserSettings.mock()
|
userSettings = UserSettings.mock()
|
||||||
userSettings.app.lowPriorityFilterEnabled = isLowPriorityFilterEnabled
|
userSettings.app.lowPriorityFilterEnabled = isLowPriorityFilterEnabled
|
||||||
|
|
||||||
@@ -136,6 +194,7 @@ final class RoomSummaryProviderTests {
|
|||||||
eventStringBuilder: eventStringBuilder,
|
eventStringBuilder: eventStringBuilder,
|
||||||
name: "Test",
|
name: "Test",
|
||||||
includesOnlyJoinedRooms: includesOnlyJoinedRooms,
|
includesOnlyJoinedRooms: includesOnlyJoinedRooms,
|
||||||
|
roomListPageSize: roomListPageSize,
|
||||||
notificationSettings: NotificationSettingsProxyMock(with: .init()),
|
notificationSettings: NotificationSettingsProxyMock(with: .init()),
|
||||||
userSettings: userSettings)
|
userSettings: userSettings)
|
||||||
|
|
||||||
@@ -151,4 +210,20 @@ final class RoomSummaryProviderTests {
|
|||||||
roomSummaryProvider.setRoomList(roomList)
|
roomSummaryProvider.setRoomList(roomList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func loadRooms(count: Int) async throws {
|
||||||
|
let listener = try #require(roomList.entriesWithDynamicAdaptersPageSizeListenerReceivedArguments?.listener)
|
||||||
|
let rooms = (0..<count).map { index in
|
||||||
|
let room = RoomSDKMock()
|
||||||
|
room.idReturnValue = "!room\(index):matrix.org"
|
||||||
|
room.latestEventReturnValue = LatestEventValue.none
|
||||||
|
// Only the room count matters, so placeholder summaries avoid mocking a whole RoomInfo.
|
||||||
|
room.roomInfoThrowableError = TestError.missingRoomInfo
|
||||||
|
return room
|
||||||
|
}
|
||||||
|
|
||||||
|
let deferred = deferFulfillment(roomSummaryProvider.roomListPublisher) { $0.count == count }
|
||||||
|
listener.onUpdate(roomEntriesUpdate: [.append(values: rooms)])
|
||||||
|
try await deferred.fulfill()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user