Fix back pagination (#432)
* Use pagination and start items for view state. isBackPaginating and canBackPaginate are updated each time the timeline is rebuilt * Update some timeline snapshots The top section has gone, which has altered the layout slightly.
This commit is contained in:
@@ -49,6 +49,7 @@ struct RoomScreenViewState: BindableState {
|
||||
var roomTitle = ""
|
||||
var roomAvatar: UIImage?
|
||||
var items: [RoomTimelineViewProvider] = []
|
||||
var canBackPaginate = true
|
||||
var isBackPaginating = false
|
||||
var showLoading = false
|
||||
var bindings: RoomScreenViewStateBindings
|
||||
|
||||
@@ -61,10 +61,14 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
|
||||
self.state.items[viewIndex] = timelineViewFactory.buildTimelineViewFor(timelineItem: timelineItem)
|
||||
case .startedBackPaginating:
|
||||
self.state.isBackPaginating = true
|
||||
case .finishedBackPaginating:
|
||||
self.state.isBackPaginating = false
|
||||
case .canBackPaginate(let canBackPaginate):
|
||||
if self.state.canBackPaginate != canBackPaginate {
|
||||
self.state.canBackPaginate = canBackPaginate
|
||||
}
|
||||
case .isBackPaginating(let isBackPaginating):
|
||||
if self.state.isBackPaginating != isBackPaginating {
|
||||
self.state.isBackPaginating = isBackPaginating
|
||||
}
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
@@ -61,6 +61,9 @@ class TimelineTableViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether or not the timeline has more messages to back paginate.
|
||||
var canBackPaginate = true
|
||||
|
||||
/// Whether or not the timeline is waiting for more messages to be added to the top.
|
||||
var isBackPaginating = false {
|
||||
didSet {
|
||||
@@ -334,7 +337,8 @@ class TimelineTableViewController: UIViewController {
|
||||
///
|
||||
/// Prefer not to call this directly, instead using ``paginateBackwardsPublisher`` to throttle requests.
|
||||
private func paginateBackwardsIfNeeded() {
|
||||
guard !isBackPaginating,
|
||||
guard canBackPaginate,
|
||||
!isBackPaginating,
|
||||
!hasPendingUpdates,
|
||||
tableView.contentOffset.y < tableView.visibleSize.height * 2.0
|
||||
else { return }
|
||||
|
||||
@@ -59,6 +59,9 @@ struct TimelineView: UIViewControllerRepresentable {
|
||||
if tableViewController.timelineItems != context.viewState.items {
|
||||
tableViewController.timelineItems = context.viewState.items
|
||||
}
|
||||
if tableViewController.canBackPaginate != context.viewState.canBackPaginate {
|
||||
tableViewController.canBackPaginate = context.viewState.canBackPaginate
|
||||
}
|
||||
if tableViewController.isBackPaginating != context.viewState.isBackPaginating {
|
||||
tableViewController.isBackPaginating = context.viewState.isBackPaginating
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ import Combine
|
||||
struct MockRoomTimelineProvider: RoomTimelineProviderProtocol {
|
||||
var itemsPublisher = CurrentValueSubject<[TimelineItemProxy], Never>([])
|
||||
|
||||
var backPaginationPublisher = CurrentValueSubject<Bool, Never>(false)
|
||||
|
||||
private var itemProxies = [TimelineItemProxy]()
|
||||
|
||||
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineProviderError> {
|
||||
|
||||
@@ -32,15 +32,10 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
let itemsPublisher = CurrentValueSubject<[TimelineItemProxy], Never>([])
|
||||
let backPaginationPublisher = CurrentValueSubject<Bool, Never>(false)
|
||||
|
||||
private var itemProxies: [TimelineItemProxy] {
|
||||
didSet {
|
||||
itemsPublisher.send(itemProxies)
|
||||
|
||||
if backPaginationPublisher.value == true {
|
||||
backPaginationPublisher.send(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,9 +64,6 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
|
||||
}
|
||||
|
||||
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineProviderError> {
|
||||
// Set this back to false after actually updating the items or if failed
|
||||
backPaginationPublisher.send(true)
|
||||
|
||||
MXLog.info("Started back pagination request")
|
||||
switch await roomProxy.paginateBackwards(requestSize: requestSize, untilNumberOfItems: untilNumberOfItems) {
|
||||
case .success:
|
||||
@@ -79,7 +71,6 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
|
||||
return .success(())
|
||||
case .failure(let error):
|
||||
MXLog.error("Failed back pagination request with error: \(error)")
|
||||
backPaginationPublisher.send(false)
|
||||
|
||||
if error == .noMoreMessagesToBackPaginate {
|
||||
return .failure(.noMoreMessagesToBackPaginate)
|
||||
|
||||
@@ -29,8 +29,6 @@ enum RoomTimelineProviderError: Error {
|
||||
protocol RoomTimelineProviderProtocol {
|
||||
var itemsPublisher: CurrentValueSubject<[TimelineItemProxy], Never> { get }
|
||||
|
||||
var backPaginationPublisher: CurrentValueSubject<Bool, Never> { get }
|
||||
|
||||
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineProviderError>
|
||||
|
||||
func sendMessage(_ message: String, inReplyToItemId: String?) async -> Result<Void, RoomTimelineProviderError>
|
||||
|
||||
@@ -42,20 +42,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
}
|
||||
|
||||
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineControllerError> {
|
||||
callbacks.send(.startedBackPaginating)
|
||||
|
||||
guard !backPaginationResponses.isEmpty else {
|
||||
callbacks.send(.finishedBackPaginating)
|
||||
return .failure(.generic)
|
||||
}
|
||||
|
||||
let newItems = backPaginationResponses.removeFirst()
|
||||
|
||||
try? await Task.sleep(for: backPaginationDelay)
|
||||
timelineItems.insert(contentsOf: newItems, at: 0)
|
||||
callbacks.send(.updatedTimelineItems)
|
||||
callbacks.send(.finishedBackPaginating)
|
||||
|
||||
callbacks.send(.canBackPaginate(false))
|
||||
return .success(())
|
||||
}
|
||||
|
||||
@@ -125,11 +112,12 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
/// Prepends the next chunk of items to the `timelineItems` array.
|
||||
private func simulateBackPagination() async throws {
|
||||
guard !backPaginationResponses.isEmpty else { return }
|
||||
callbacks.send(.isBackPaginating(true))
|
||||
|
||||
let newItems = backPaginationResponses.removeFirst()
|
||||
timelineItems.insert(contentsOf: newItems, at: 0)
|
||||
callbacks.send(.updatedTimelineItems)
|
||||
callbacks.send(.finishedBackPaginating)
|
||||
callbacks.send(.isBackPaginating(false))
|
||||
|
||||
try? await connection?.send(.success)
|
||||
}
|
||||
|
||||
@@ -61,18 +61,6 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
self.timelineProvider
|
||||
.backPaginationPublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] value in
|
||||
if value {
|
||||
self?.callbacks.send(.startedBackPaginating)
|
||||
} else {
|
||||
self?.callbacks.send(.finishedBackPaginating)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
updateTimelineItems()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
@@ -230,6 +218,8 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
private func asyncUpdateTimelineItems() async {
|
||||
var newTimelineItems = [RoomTimelineItemProtocol]()
|
||||
var canBackPaginate = true
|
||||
var isBackPaginating = false
|
||||
|
||||
for (index, itemProxy) in timelineProvider.itemsPublisher.value.enumerated() {
|
||||
if Task.isCancelled {
|
||||
@@ -262,8 +252,10 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
}
|
||||
case .loadingIndicator:
|
||||
newTimelineItems.append(PaginationIndicatorRoomTimelineItem())
|
||||
isBackPaginating = true
|
||||
case .timelineStart:
|
||||
newTimelineItems.append(TimelineStartRoomTimelineItem(name: roomProxy.displayName ?? roomProxy.name))
|
||||
canBackPaginate = false
|
||||
}
|
||||
default:
|
||||
break
|
||||
@@ -277,6 +269,8 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
timelineItems = newTimelineItems
|
||||
|
||||
callbacks.send(.updatedTimelineItems)
|
||||
callbacks.send(.canBackPaginate(canBackPaginate))
|
||||
callbacks.send(.isBackPaginating(isBackPaginating))
|
||||
}
|
||||
|
||||
private func computeGroupState(for itemProxy: TimelineItemProxy,
|
||||
|
||||
@@ -21,8 +21,8 @@ import UIKit
|
||||
enum RoomTimelineControllerCallback {
|
||||
case updatedTimelineItems
|
||||
case updatedTimelineItem(_ itemId: String)
|
||||
case startedBackPaginating
|
||||
case finishedBackPaginating
|
||||
case canBackPaginate(Bool)
|
||||
case isBackPaginating(Bool)
|
||||
}
|
||||
|
||||
enum RoomTimelineControllerAction {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6018b19cf7b412b3a74080a4b3b4bf35ce86a4ef187f9b4945f68b9744760dd6
|
||||
size 314107
|
||||
oid sha256:c8b51971fbfc26b12f82e6b653a62062563564643cfd246f4ee9c3ac559ed7f0
|
||||
size 314028
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5d507e3246043e67cc67873a83f9cb2a82e200f6debecfbf2cc4dd2e1fab86e9
|
||||
size 321969
|
||||
oid sha256:fea82166624c56da8239158cf04ad9e62bdacb4d7feebf4895fa75ff8a6df621
|
||||
size 322678
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6aef1da13173322dbd51e933df69733a48c02b89cfdf564c41ac1933c7a4f9b8
|
||||
size 303249
|
||||
oid sha256:e09a2caa99abebc8728bc81d5786e0b9a320348067a684646f653b88145badb1
|
||||
size 293804
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:ff71a79bf353a284c2c8afa92ce2562a45c25c64486fb132d795a07bf0c4b6f0
|
||||
size 335617
|
||||
oid sha256:6e19e5caa62c85d0d39177e981961f8356172849bdd564522575762ab44c40dd
|
||||
size 340523
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a466639c4f320f4d684fe485255fd217d960665d8d9ea6cd1dd0e1f425b8cac0
|
||||
size 335663
|
||||
oid sha256:8ebdcd4e127c2187bc1f58a4569de526151f6b1b9633d28c543d6391fc69f71a
|
||||
size 338537
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:01a5fa9431bdfa0dffc6ed4ec5816db6eb923b8048f3af3124eb8701dad59ba1
|
||||
size 283965
|
||||
oid sha256:95fbe5f1ab035fbd354b3f27094ee0c4e37a9965bbadb370919a5476a64c3995
|
||||
size 284468
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:f5bf2982f09c57a980df0dc3d3dba3d3bafa67269704aa342a2dd5d169e9971b
|
||||
size 313966
|
||||
oid sha256:88beeea3e68ad96cae62801499a232d9f2390ff7419a7a21ee03bbc96755833a
|
||||
size 313817
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d9ae251abae3f358ac0c2aae41ff55ff9eb209696d2dff43154679cfb2ba30ea
|
||||
size 319312
|
||||
oid sha256:5987f0e456429925cbd96f562bb0bd6a2de281d8e16c04e3607709f4a826aa81
|
||||
size 324059
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3e2192501b67459b5d4f85353976e7d3a7b5cf55d5b1f712b41c736579a919be
|
||||
size 303063
|
||||
oid sha256:e591b331dbb4531b1f6c772b23d0dd64887b9969c08bf2f5df9eed5e96e6cc71
|
||||
size 293606
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e17cbe7c08f8feddfeaec82d64d4c7ba001c91ef3ee5a38efd4e9eafe73e9d9b
|
||||
size 335662
|
||||
oid sha256:bcbb504e80defdb0a8540aa2088a6820bf5327325a28c783f3ea12ec8276a79a
|
||||
size 340504
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:90f0175794dfebcc609105207e345e34c8d272cbd896f4bff0786ee4ecbf9c0c
|
||||
size 335528
|
||||
oid sha256:a7c7103f6194cd4d4f51ef67a2b4d40ebc54dcc0336ffdd8fcdbed760f2991a2
|
||||
size 338396
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e9b41ae6543ffcc994c2468f48641149d1932065600f94af29379d0fbb5d1297
|
||||
size 283690
|
||||
oid sha256:96de084f6f0fd999b82f4c8c577bfb2c0675b65434a76ac9b01dc5a49db3d27a
|
||||
size 284193
|
||||
|
||||
@@ -58,7 +58,7 @@ targets:
|
||||
- path: ../SupportingFiles
|
||||
- path: ../../Tools/Scripts/Templates/SimpleScreenExample/Tests/UI
|
||||
- path: ../../ElementX/Sources/UITests/UITestScreenIdentifier.swift
|
||||
- path: ../../ElementX/Sources/UITests/UITestSignalling.swift
|
||||
- path: ../../ElementX/Sources/UITests/UITestsSignalling.swift
|
||||
- path: ../../ElementX/Sources/Generated/Strings.swift
|
||||
- path: ../../ElementX/Sources/Generated/Strings+Untranslated.swift
|
||||
- path: ../../ElementX/Resources
|
||||
|
||||
1
changelog.d/pr-432.bugfix
Normal file
1
changelog.d/pr-432.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Use pagination indicators and start of room timeline items to update the view's pagination state.
|
||||
Reference in New Issue
Block a user