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 3d1266ca25
commit 08b333839a
24 changed files with 54 additions and 72 deletions
@@ -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 {