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:
Doug
2023-01-11 09:11:36 +00:00
committed by GitHub
parent 5672fef5a5
commit 19e0052fc4
24 changed files with 54 additions and 72 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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 }

View File

@@ -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
}

View File

@@ -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> {

View File

@@ -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)

View File

@@ -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>

View File

@@ -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)
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6018b19cf7b412b3a74080a4b3b4bf35ce86a4ef187f9b4945f68b9744760dd6
size 314107
oid sha256:c8b51971fbfc26b12f82e6b653a62062563564643cfd246f4ee9c3ac559ed7f0
size 314028

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5d507e3246043e67cc67873a83f9cb2a82e200f6debecfbf2cc4dd2e1fab86e9
size 321969
oid sha256:fea82166624c56da8239158cf04ad9e62bdacb4d7feebf4895fa75ff8a6df621
size 322678

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6aef1da13173322dbd51e933df69733a48c02b89cfdf564c41ac1933c7a4f9b8
size 303249
oid sha256:e09a2caa99abebc8728bc81d5786e0b9a320348067a684646f653b88145badb1
size 293804

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ff71a79bf353a284c2c8afa92ce2562a45c25c64486fb132d795a07bf0c4b6f0
size 335617
oid sha256:6e19e5caa62c85d0d39177e981961f8356172849bdd564522575762ab44c40dd
size 340523

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a466639c4f320f4d684fe485255fd217d960665d8d9ea6cd1dd0e1f425b8cac0
size 335663
oid sha256:8ebdcd4e127c2187bc1f58a4569de526151f6b1b9633d28c543d6391fc69f71a
size 338537

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:01a5fa9431bdfa0dffc6ed4ec5816db6eb923b8048f3af3124eb8701dad59ba1
size 283965
oid sha256:95fbe5f1ab035fbd354b3f27094ee0c4e37a9965bbadb370919a5476a64c3995
size 284468

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f5bf2982f09c57a980df0dc3d3dba3d3bafa67269704aa342a2dd5d169e9971b
size 313966
oid sha256:88beeea3e68ad96cae62801499a232d9f2390ff7419a7a21ee03bbc96755833a
size 313817

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d9ae251abae3f358ac0c2aae41ff55ff9eb209696d2dff43154679cfb2ba30ea
size 319312
oid sha256:5987f0e456429925cbd96f562bb0bd6a2de281d8e16c04e3607709f4a826aa81
size 324059

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3e2192501b67459b5d4f85353976e7d3a7b5cf55d5b1f712b41c736579a919be
size 303063
oid sha256:e591b331dbb4531b1f6c772b23d0dd64887b9969c08bf2f5df9eed5e96e6cc71
size 293606

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e17cbe7c08f8feddfeaec82d64d4c7ba001c91ef3ee5a38efd4e9eafe73e9d9b
size 335662
oid sha256:bcbb504e80defdb0a8540aa2088a6820bf5327325a28c783f3ea12ec8276a79a
size 340504

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:90f0175794dfebcc609105207e345e34c8d272cbd896f4bff0786ee4ecbf9c0c
size 335528
oid sha256:a7c7103f6194cd4d4f51ef67a2b4d40ebc54dcc0336ffdd8fcdbed760f2991a2
size 338396

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e9b41ae6543ffcc994c2468f48641149d1932065600f94af29379d0fbb5d1297
size 283690
oid sha256:96de084f6f0fd999b82f4c8c577bfb2c0675b65434a76ac9b01dc5a49db3d27a
size 284193

View File

@@ -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

View File

@@ -0,0 +1 @@
Use pagination indicators and start of room timeline items to update the view's pagination state.