Prepare for event focus and forward pagination (#2745)

Neither are available yet.
This commit is contained in:
Doug
2024-04-25 18:32:33 +01:00
committed by GitHub
parent 8d66572cc9
commit 76e7de40b5
254 changed files with 1461 additions and 845 deletions
@@ -22,9 +22,9 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
private var cancellables = Set<AnyCancellable>()
private let serialDispatchQueue: DispatchQueue
private let backPaginationStateSubject = CurrentValueSubject<BackPaginationStatus, Never>(.idle)
var backPaginationState: BackPaginationStatus {
backPaginationStateSubject.value
private let paginationStateSubject = CurrentValueSubject<PaginationState, Never>(.default)
var paginationState: PaginationState {
paginationStateSubject.value
}
private let itemProxiesSubject: CurrentValueSubject<[TimelineItemProxy], Never>
@@ -32,13 +32,14 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
itemProxiesSubject.value
}
var updatePublisher: AnyPublisher<Void, Never> {
var updatePublisher: AnyPublisher<([TimelineItemProxy], PaginationState), Never> {
itemProxiesSubject
.combineLatest(backPaginationStateSubject)
.map { _, _ in () }
.combineLatest(paginationStateSubject)
.eraseToAnyPublisher()
}
private(set) var isLive: Bool
private let membershipChangeSubject = PassthroughSubject<Void, Never>()
var membershipChangePublisher: AnyPublisher<Void, Never> {
membershipChangeSubject
@@ -46,10 +47,12 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
}
init(currentItems: [TimelineItem],
isLive: Bool,
updatePublisher: AnyPublisher<[TimelineDiff], Never>,
backPaginationStatePublisher: AnyPublisher<BackPaginationStatus, Never>) {
paginationStatePublisher: AnyPublisher<PaginationState, Never>) {
serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility)
itemProxiesSubject = CurrentValueSubject<[TimelineItemProxy], Never>(currentItems.map(TimelineItemProxy.init))
self.isLive = isLive
// Manually call it here as the didSet doesn't work from constructors
itemProxiesSubject.send(itemProxies)
@@ -59,8 +62,10 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
.sink { [weak self] in self?.updateItemsWithDiffs($0) }
.store(in: &cancellables)
backPaginationStatePublisher
.sink { [weak self] in self?.backPaginationStateSubject.send($0) }
paginationStatePublisher
.sink { [weak self] in
self?.paginationStateSubject.send($0)
}
.store(in: &cancellables)
}
@@ -16,18 +16,25 @@
import Combine
import Foundation
import MatrixRustSDK
struct PaginationState: Equatable {
static var `default` = PaginationState(backward: .idle, forward: .timelineStartReached)
let backward: BackPaginationStatus
let forward: BackPaginationStatus
}
@MainActor
// sourcery: AutoMockable
protocol RoomTimelineProviderProtocol {
/// A publisher that signals when ``itemProxies`` or ``backPaginationState`` are changed.
var updatePublisher: AnyPublisher<Void, Never> { get }
/// A publisher that signals when ``itemProxies`` or ``paginationState`` are changed.
var updatePublisher: AnyPublisher<([TimelineItemProxy], PaginationState), Never> { get }
/// The current set of items in the timeline.
var itemProxies: [TimelineItemProxy] { get }
/// Whether the timeline is back paginating or not (or has reached the start of the room).
var backPaginationState: BackPaginationStatus { get }
/// Whether the timeline is back/forward paginating or not (or has reached the start/end of the room).
var paginationState: PaginationState { get }
/// Whether or not the provider is for a live timeline.
var isLive: Bool { get }
/// A publisher that signals when changes to the room's membership have occurred through `/sync`.
///
/// This is temporary and will be replace by a subscription on the room itself.
@@ -36,6 +36,9 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
private var client: UITestsSignalling.Client?
init(listenForSignals: Bool = false) {
callbacks.send(.paginationState(PaginationState(backward: .idle, forward: .timelineStartReached)))
callbacks.send(.isLive(true))
guard listenForSignals else { return }
do {
@@ -44,17 +47,35 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
fatalError("Failure setting up signalling: \(error)")
}
}
func paginateBackwards(requestSize: UInt) async -> Result<Void, RoomTimelineControllerError> {
try? await simulateBackPagination()
private(set) var focusOnEventCallCount = 0
func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
focusOnEventCallCount += 1
callbacks.send(.isLive(false))
return .success(())
}
private(set) var focusLiveCallCount = 0
func focusLive() {
focusLiveCallCount += 1
callbacks.send(.isLive(true))
}
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineControllerError> {
callbacks.send(.canBackPaginate(false))
func paginateBackwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
callbacks.send(.paginationState(PaginationState(backward: .paginating, forward: .timelineStartReached)))
if client == nil {
try? await simulateBackPagination()
}
return .success(())
}
func paginateForwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
// try? await simulateForwardPagination()
.success(())
}
func sendReadReceipt(for itemID: TimelineItemIdentifier) async {
guard let roomProxy, let eventID = itemID.eventID else { return }
_ = await roomProxy.timeline.sendReadReceipt(for: eventID, type: .read)
@@ -151,14 +172,16 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
/// Prepends the next chunk of items to the `timelineItems` array.
private func simulateBackPagination() async throws {
defer {
callbacks.send(.paginationState(PaginationState(backward: backPaginationResponses.isEmpty ? .timelineStartReached : .idle,
forward: .timelineStartReached)))
}
guard !backPaginationResponses.isEmpty else { return }
callbacks.send(.isBackPaginating(true))
let newItems = backPaginationResponses.removeFirst()
timelineItems.insert(contentsOf: newItems, at: 0)
callbacks.send(.updatedTimelineItems)
callbacks.send(.isBackPaginating(false))
callbacks.send(.canBackPaginate(!backPaginationResponses.isEmpty))
try client?.send(.success)
}
@@ -21,7 +21,7 @@ import UIKit
class RoomTimelineController: RoomTimelineControllerProtocol {
private let roomProxy: RoomProxyProtocol
private let timelineProvider: RoomTimelineProviderProtocol
private let liveTimelineProvider: RoomTimelineProviderProtocol
private let timelineItemFactory: RoomTimelineItemFactoryProtocol
private let appSettings: AppSettings
private let serialDispatchQueue: DispatchQueue
@@ -30,6 +30,13 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
private var activeTimeline: TimelineProxyProtocol
private var activeTimelineProvider: RoomTimelineProviderProtocol {
didSet {
configureActiveTimelineProvider(clearExistingItems: true)
}
}
private(set) var timelineItems = [RoomTimelineItemProtocol]()
var roomID: String {
@@ -40,34 +47,39 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
timelineItemFactory: RoomTimelineItemFactoryProtocol,
appSettings: AppSettings) {
self.roomProxy = roomProxy
timelineProvider = roomProxy.timeline.timelineProvider
liveTimelineProvider = roomProxy.timeline.timelineProvider
self.timelineItemFactory = timelineItemFactory
self.appSettings = appSettings
serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility)
timelineProvider
.updatePublisher
.receive(on: serialDispatchQueue)
.sink { [weak self] _ in
guard let self else { return }
self.updateTimelineItems()
}
.store(in: &cancellables)
// Inform the world that the initial items are loading from the store
callbacks.send(.isBackPaginating(true))
serialDispatchQueue.async {
self.updateTimelineItems()
self.callbacks.send(.isBackPaginating(false))
}
activeTimeline = roomProxy.timeline
activeTimelineProvider = liveTimelineProvider
configureActiveTimelineProvider()
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil)
}
func paginateBackwards(requestSize: UInt) async -> Result<Void, RoomTimelineControllerError> {
func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
switch await roomProxy.timelineFocusedOnEvent(eventID: eventID, numberOfEvents: timelineSize) {
case .success(let timeline):
await timeline.subscribeForUpdates()
activeTimeline = timeline
activeTimelineProvider = timeline.timelineProvider
return .success(())
case .failure:
return .failure(.generic)
}
}
func focusLive() {
activeTimeline = roomProxy.timeline
activeTimelineProvider = liveTimelineProvider
callbacks.send(.isLive(true))
}
func paginateBackwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
MXLog.info("Started back pagination request")
switch await roomProxy.timeline.paginateBackwards(requestSize: requestSize) {
switch await activeTimeline.paginateBackwards(requestSize: requestSize) {
case .success:
MXLog.info("Finished back pagination request")
return .success(())
@@ -77,14 +89,14 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
}
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineControllerError> {
MXLog.info("Started back pagination request")
switch await roomProxy.timeline.paginateBackwards(requestSize: requestSize, untilNumberOfItems: untilNumberOfItems) {
func paginateForwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
MXLog.info("Started forward pagination request")
switch await activeTimeline.paginateForwards(requestSize: requestSize) {
case .success:
MXLog.info("Finished back pagination request")
MXLog.info("Finished forward pagination request")
return .success(())
case .failure(let error):
MXLog.error("Failed back pagination request with error: \(error)")
MXLog.error("Failed forward pagination request with error: \(error)")
return .failure(.generic)
}
}
@@ -149,7 +161,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
return
}
switch await roomProxy.timeline.toggleReaction(reaction, to: eventID) {
switch await activeTimeline.toggleReaction(reaction, to: eventID) {
case .success:
MXLog.info("Finished toggling reaction")
case .failure(let error):
@@ -169,10 +181,10 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
await cancelSending(itemID: itemID)
await sendMessage(newMessage, html: html, intentionalMentions: intentionalMentions)
} else if let eventID = itemID.eventID {
switch await roomProxy.timeline.editMessage(newMessage,
html: html,
original: eventID,
intentionalMentions: intentionalMentions) {
switch await activeTimeline.editMessage(newMessage,
html: html,
original: eventID,
intentionalMentions: intentionalMentions) {
case .success:
MXLog.info("Finished editing message")
case .failure(let error):
@@ -199,7 +211,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
// Handle this parallel to the timeline items so we're not forced
// to bundle the Rust side objects within them
func debugInfo(for itemID: TimelineItemIdentifier) -> TimelineItemDebugInfo {
for timelineItemProxy in timelineProvider.itemProxies {
for timelineItemProxy in activeTimelineProvider.itemProxies {
switch timelineItemProxy {
case .event(let item):
if item.id == itemID {
@@ -214,7 +226,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
func retryDecryption(for sessionID: String) async {
await roomProxy.timeline.retryDecryption(for: sessionID)
await activeTimeline.retryDecryption(for: sessionID)
}
func retrySending(itemID: TimelineItemIdentifier) async {
@@ -239,19 +251,46 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
// MARK: - Private
@objc private func contentSizeCategoryDidChange() {
// Recompute all attributed strings on content size changes -> DynamicType support
serialDispatchQueue.async {
self.updateTimelineItems()
/// The cancellable used to update the timeline items.
private var updateTimelineItemsCancellable: AnyCancellable?
/// Configures the controller to listen to `activeTimeline` for events.
/// - Parameter clearExistingItems: Whether or not to clear any existing items before loading the timeline's contents.
private func configureActiveTimelineProvider(clearExistingItems: Bool = false) {
updateTimelineItemsCancellable = activeTimelineProvider
.updatePublisher
.receive(on: serialDispatchQueue)
.sink { [weak self] items, paginationState in
self?.updateTimelineItems(itemProxies: items, paginationState: paginationState)
}
// Inform the world that the initial items are loading from the store
callbacks.send(.paginationState(.init(backward: .paginating, forward: .paginating)))
callbacks.send(.isLive(activeTimelineProvider.isLive))
if clearExistingItems {
// Transition through empty to prevent animations.
timelineItems.removeAll()
callbacks.send(.updatedTimelineItems)
}
serialDispatchQueue.async { [activeTimelineProvider] in
self.updateTimelineItems(itemProxies: activeTimelineProvider.itemProxies, paginationState: activeTimelineProvider.paginationState)
self.callbacks.send(.paginationState(.init(backward: .idle, forward: .idle)))
}
}
private func updateTimelineItems() {
@objc private func contentSizeCategoryDidChange() {
// Recompute all attributed strings on content size changes -> DynamicType support
serialDispatchQueue.async { [activeTimelineProvider] in
self.updateTimelineItems(itemProxies: activeTimelineProvider.itemProxies, paginationState: activeTimelineProvider.paginationState)
}
}
private func updateTimelineItems(itemProxies: [TimelineItemProxy], paginationState: PaginationState) {
var newTimelineItems = [RoomTimelineItemProtocol]()
var canBackPaginate = !roomProxy.timeline.timelineStartReached
var isBackPaginating = false
let collapsibleChunks = timelineProvider.itemProxies.groupBy { isItemCollapsible($0) }
let collapsibleChunks = itemProxies.groupBy { isItemCollapsible($0) }
for (index, collapsibleChunk) in collapsibleChunks.enumerated() {
let isLastItem = index == collapsibleChunks.indices.last
@@ -281,27 +320,31 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
// Check if we need to add anything to the top of the timeline.
switch timelineProvider.backPaginationState {
switch paginationState.backward {
case .timelineStartReached:
if !roomProxy.isEncryptedOneToOneRoom {
let timelineStart = TimelineStartRoomTimelineItem(name: roomProxy.name)
newTimelineItems.insert(timelineStart, at: 0)
}
canBackPaginate = false
case .paginating:
newTimelineItems.insert(PaginationIndicatorRoomTimelineItem(), at: 0)
isBackPaginating = true
newTimelineItems.insert(PaginationIndicatorRoomTimelineItem(position: .start), at: 0)
case .idle:
break
}
switch paginationState.forward {
case .paginating:
newTimelineItems.insert(PaginationIndicatorRoomTimelineItem(position: .end), at: newTimelineItems.count)
case .idle, .timelineStartReached:
break
}
DispatchQueue.main.sync {
timelineItems = newTimelineItems
}
callbacks.send(.updatedTimelineItems)
callbacks.send(.canBackPaginate(canBackPaginate))
callbacks.send(.isBackPaginating(isBackPaginating))
callbacks.send(.paginationState(paginationState))
}
private func buildTimelineItem(for itemProxy: TimelineItemProxy) -> RoomTimelineItemProtocol? {
@@ -355,10 +398,10 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
switch timelineItem.replyDetails {
case .notLoaded:
roomProxy.timeline.fetchDetails(for: eventID)
activeTimeline.fetchDetails(for: eventID)
case .error:
if refetchOnError {
roomProxy.timeline.fetchDetails(for: eventID)
activeTimeline.fetchDetails(for: eventID)
}
default:
break
@@ -366,7 +409,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
}
func eventTimestamp(for itemID: TimelineItemIdentifier) -> Date? {
for itemProxy in roomProxy.timeline.timelineProvider.itemProxies {
for itemProxy in activeTimelineProvider.itemProxies {
switch itemProxy {
case .event(let eventTimelineItemProxy):
if eventTimelineItemProxy.id == itemID {
@@ -20,8 +20,8 @@ import UIKit
enum RoomTimelineControllerCallback {
case updatedTimelineItems
case canBackPaginate(Bool)
case isBackPaginating(Bool)
case paginationState(PaginationState)
case isLive(Bool)
}
enum RoomTimelineControllerAction {
@@ -45,9 +45,11 @@ protocol RoomTimelineControllerProtocol {
func processItemDisappearance(_ itemID: TimelineItemIdentifier) async
func paginateBackwards(requestSize: UInt) async -> Result<Void, RoomTimelineControllerError>
func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result<Void, RoomTimelineControllerError>
func focusLive()
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, RoomTimelineControllerError>
func paginateBackwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError>
func paginateForwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError>
func sendReadReceipt(for itemID: TimelineItemIdentifier) async
@@ -46,7 +46,7 @@ enum TimelineItemProxy {
init(item: MatrixRustSDK.TimelineItem) {
if let eventItem = item.asEvent() {
self = .event(EventTimelineItemProxy(item: eventItem, id: item.uniqueId()))
self = .event(EventTimelineItemProxy(item: eventItem, id: String(item.uniqueId())))
} else if let virtualItem = item.asVirtual() {
self = .virtual(virtualItem, timelineID: String(item.uniqueId()))
} else {
@@ -67,9 +67,9 @@ class EventTimelineItemProxy {
let item: MatrixRustSDK.EventTimelineItem
let id: TimelineItemIdentifier
init(item: MatrixRustSDK.EventTimelineItem, id: UInt64) {
init(item: MatrixRustSDK.EventTimelineItem, id: String) {
self.item = item
self.id = TimelineItemIdentifier(timelineID: String(id), eventID: item.eventId(), transactionID: item.transactionId())
self.id = TimelineItemIdentifier(timelineID: id, eventID: item.eventId(), transactionID: item.transactionId())
}
lazy var deliveryStatus: TimelineItemDeliveryStatus? = {
@@ -17,5 +17,20 @@
import Foundation
struct PaginationIndicatorRoomTimelineItem: DecorationTimelineItemProtocol, Equatable {
let id = TimelineItemIdentifier(timelineID: "paginationIndicatorTimelineItemIdentifier")
let id: TimelineItemIdentifier
enum Position {
case start, end
var id: String {
switch self {
case .start: "backwardPaginationIndicatorTimelineItemIdentifier"
case .end: "forwardPaginationIndicatorTimelineItemIdentifier"
}
}
}
init(position: Position) {
id = TimelineItemIdentifier(timelineID: position.id)
}
}
@@ -25,21 +25,22 @@ final class TimelineProxy: TimelineProxyProtocol {
private let messageSendingDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.message_sending", qos: .userInitiated)
private let userInitiatedDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.user_initiated", qos: .userInitiated)
private var backPaginationStateObservationToken: TaskHandle?
private var backPaginationStatusObservationToken: TaskHandle?
private var roomTimelineObservationToken: TaskHandle?
// periphery:ignore - retaining purpose
private var timelineListener: RoomTimelineListener?
private let backPaginationStateSubject = PassthroughSubject<BackPaginationStatus, Never>()
private let backPaginationStatusSubject = CurrentValueSubject<BackPaginationStatus, Never>(.idle)
private let forwardPaginationStatusSubject = CurrentValueSubject<BackPaginationStatus, Never>(.timelineStartReached)
private let timelineUpdatesSubject = PassthroughSubject<[TimelineDiff], Never>()
private(set) var timelineStartReached = false
private let actionsSubject = PassthroughSubject<TimelineProxyAction, Never>()
var actions: AnyPublisher<TimelineProxyAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
let isLive: Bool
private var innerTimelineProvider: RoomTimelineProviderProtocol!
var timelineProvider: RoomTimelineProviderProtocol {
@@ -47,12 +48,13 @@ final class TimelineProxy: TimelineProxyProtocol {
}
deinit {
backPaginationStateObservationToken?.cancel()
backPaginationStatusObservationToken?.cancel()
roomTimelineObservationToken?.cancel()
}
init(timeline: Timeline) {
init(timeline: Timeline, isLive: Bool) {
self.timeline = timeline
self.isLive = isLive
}
func subscribeForUpdates() async {
@@ -70,11 +72,17 @@ final class TimelineProxy: TimelineProxyProtocol {
let result = await timeline.addListener(listener: timelineListener)
roomTimelineObservationToken = result.itemsStream
subscribeToBackpagination()
let paginationStatePublisher = backPaginationStatusSubject
.combineLatest(forwardPaginationStatusSubject)
.map { PaginationState(backward: $0.0, forward: $0.1) }
.eraseToAnyPublisher()
subscribeToPagination()
innerTimelineProvider = await RoomTimelineProvider(currentItems: result.items,
isLive: isLive,
updatePublisher: timelineUpdatesSubject.eraseToAnyPublisher(),
backPaginationStatePublisher: backPaginationStateSubject.eraseToAnyPublisher())
paginationStatePublisher: paginationStatePublisher)
}
func cancelSend(transactionID: String) async {
@@ -138,38 +146,43 @@ final class TimelineProxy: TimelineProxyProtocol {
}
}
func paginateBackwards(requestSize: UInt) async -> Result<Void, TimelineProxyError> {
MXLog.info("Paginating backwards with requestSize: \(requestSize)")
func paginateBackwards(requestSize: UInt16) async -> Result<Void, TimelineProxyError> {
MXLog.info("Paginating backwards")
do {
try await Task.dispatch(on: .global()) {
try self.timeline.paginateBackwards(opts: .simpleRequest(eventLimit: UInt16(requestSize), waitForToken: true))
}
MXLog.info("Finished paginating backwards with requestSize: \(requestSize)")
_ = try timeline.paginateBackwards(opts: .simpleRequest(eventLimit: requestSize, waitForToken: true))
MXLog.info("Finished paginating backwards")
return .success(())
} catch {
MXLog.error("Failed paginating backwards with requestSize: \(requestSize) with error: \(error)")
MXLog.error("Failed paginating backwards with error: \(error)")
return .failure(.failedPaginatingBackwards)
}
}
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, TimelineProxyError> {
MXLog.info("Paginating backwards with requestSize: \(requestSize), untilNumberOfItems: \(untilNumberOfItems)")
do {
try await Task.dispatch(on: .global()) {
try self.timeline.paginateBackwards(opts: .untilNumItems(eventLimit: UInt16(requestSize), items: UInt16(untilNumberOfItems), waitForToken: true))
}
MXLog.info("Finished paginating backwards with requestSize: \(requestSize), untilNumberOfItems: \(untilNumberOfItems)")
return .success(())
} catch {
MXLog.error("Finished paginating backwards with requestSize: \(requestSize), untilNumberOfItems: \(untilNumberOfItems) with error: \(error)")
return .failure(.failedPaginatingBackwards)
}
func paginateForwards(requestSize: UInt16) async -> Result<Void, TimelineProxyError> {
.failure(.failedPaginatingBackwards)
// This extra check is necessary as forwards pagination status doesn't support subscribing.
// We need it to make sure we send a valid status after a failure.
// guard forwardPaginationStatusSubject.value == .idle else {
// MXLog.error("Attempting to paginate forwards when already at the end.")
// return .failure(.failedPaginatingBackwards)
// }
//
// MXLog.info("Paginating forwards")
// forwardPaginationStatusSubject.send(.paginating)
//
// do {
// let timelineEndReached = try await timeline.paginateForwards(numEvents: requestSize)
// MXLog.info("Finished paginating forwards")
//
// forwardPaginationStatusSubject.send(timelineEndReached ? .timelineEndReached : .idle)
// return .success(())
// } catch {
// MXLog.error("Failed paginating forwards with error: \(error)")
// forwardPaginationStatusSubject.send(.idle)
// return .failure(.failedPaginatingBackwards)
// }
}
func retryDecryption(for sessionID: String) async {
@@ -536,18 +549,18 @@ final class TimelineProxy: TimelineProxyProtocol {
}
}
private func subscribeToBackpagination() {
let listener = RoomBackpaginationStatusListener { [weak self] status in
if status == .timelineStartReached {
self?.timelineStartReached = true
}
self?.backPaginationStateSubject.send(status)
private func subscribeToPagination() {
let backPaginationListener = RoomPaginationStatusListener { [weak self] status in
self?.backPaginationStatusSubject.send(status)
}
do {
backPaginationStateObservationToken = try timeline.subscribeToBackPaginationStatus(listener: listener)
backPaginationStatusObservationToken = try timeline.subscribeToBackPaginationStatus(listener: backPaginationListener)
} catch {
MXLog.error("Failed to subscribe to back pagination state with error: \(error)")
MXLog.error("Failed to subscribe to back pagination status with error: \(error)")
}
// Forward pagination doesn't support observation, set the initial state ourself.
forwardPaginationStatusSubject.send(isLive ? .timelineStartReached : .idle)
}
}
@@ -563,7 +576,7 @@ private final class RoomTimelineListener: TimelineListener {
}
}
private final class RoomBackpaginationStatusListener: BackPaginationStatusListener {
private final class RoomPaginationStatusListener: BackPaginationStatusListener {
private let onUpdateClosure: (BackPaginationStatus) -> Void
init(_ onUpdateClosure: @escaping (BackPaginationStatus) -> Void) {
@@ -41,9 +41,9 @@ enum TimelineProxyAction {
protocol TimelineProxyProtocol {
var actions: AnyPublisher<TimelineProxyAction, Never> { get }
var timelineProvider: RoomTimelineProviderProtocol { get }
var isLive: Bool { get }
var timelineStartReached: Bool { get }
var timelineProvider: RoomTimelineProviderProtocol { get }
func subscribeForUpdates() async
@@ -64,9 +64,8 @@ protocol TimelineProxyProtocol {
/// Retries sending a failed message given its transaction ID
func retrySend(transactionID: String) async
func paginateBackwards(requestSize: UInt) async -> Result<Void, TimelineProxyError>
func paginateBackwards(requestSize: UInt, untilNumberOfItems: UInt) async -> Result<Void, TimelineProxyError>
func paginateBackwards(requestSize: UInt16) async -> Result<Void, TimelineProxyError>
func paginateForwards(requestSize: UInt16) async -> Result<Void, TimelineProxyError>
func sendAudio(url: URL,
audioInfo: AudioInfo,