mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
TimelineKind refactor (#3193)
This commit is contained in:
@@ -42,7 +42,7 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
private(set) var isLive: Bool
|
||||
let kind: TimelineKind
|
||||
|
||||
private let membershipChangeSubject = PassthroughSubject<Void, Never>()
|
||||
var membershipChangePublisher: AnyPublisher<Void, Never> {
|
||||
@@ -54,10 +54,10 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
|
||||
roomTimelineObservationToken?.cancel()
|
||||
}
|
||||
|
||||
init(timeline: Timeline, isLive: Bool, paginationStatePublisher: AnyPublisher<PaginationState, Never>) {
|
||||
init(timeline: Timeline, kind: TimelineKind, paginationStatePublisher: AnyPublisher<PaginationState, Never>) {
|
||||
serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility)
|
||||
itemProxiesSubject = CurrentValueSubject<[TimelineItemProxy], Never>([])
|
||||
self.isLive = isLive
|
||||
self.kind = kind
|
||||
|
||||
paginationStatePublisher
|
||||
.sink { [weak self] in
|
||||
|
||||
@@ -45,8 +45,8 @@ protocol RoomTimelineProviderProtocol {
|
||||
var itemProxies: [TimelineItemProxy] { 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 }
|
||||
/// The kind of the timeline
|
||||
var kind: TimelineKind { 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.
|
||||
|
||||
+3
-1
@@ -28,6 +28,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
|
||||
var roomProxy: JoinedRoomProxyProtocol?
|
||||
var roomID: String { roomProxy?.id ?? "MockRoomIdentifier" }
|
||||
var timelineKind: TimelineKind
|
||||
|
||||
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
|
||||
|
||||
@@ -36,7 +37,8 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
|
||||
private var client: UITestsSignalling.Client?
|
||||
|
||||
init(listenForSignals: Bool = false) {
|
||||
init(timelineKind: TimelineKind = .live, listenForSignals: Bool = false) {
|
||||
self.timelineKind = timelineKind
|
||||
callbacks.send(.paginationState(PaginationState(backward: .idle, forward: .timelineEndReached)))
|
||||
callbacks.send(.isLive(true))
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
private let timelineItemFactory: RoomTimelineItemFactoryProtocol
|
||||
private let appSettings: AppSettings
|
||||
private let serialDispatchQueue: DispatchQueue
|
||||
private let shouldHideStart: Bool
|
||||
|
||||
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
|
||||
|
||||
@@ -42,17 +41,19 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
roomProxy.id
|
||||
}
|
||||
|
||||
var timelineKind: TimelineKind {
|
||||
liveTimelineProvider.kind
|
||||
}
|
||||
|
||||
init(roomProxy: JoinedRoomProxyProtocol,
|
||||
timelineProxy: TimelineProxyProtocol,
|
||||
initialFocussedEventID: String?,
|
||||
shouldHideStart: Bool,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
appSettings: AppSettings) {
|
||||
self.roomProxy = roomProxy
|
||||
liveTimelineProvider = timelineProxy.timelineProvider
|
||||
self.timelineItemFactory = timelineItemFactory
|
||||
self.appSettings = appSettings
|
||||
self.shouldHideStart = shouldHideStart
|
||||
serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility)
|
||||
|
||||
activeTimeline = timelineProxy
|
||||
@@ -312,7 +313,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
|
||||
// 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))
|
||||
callbacks.send(.isLive(activeTimelineProvider.kind == .live))
|
||||
|
||||
updateTimelineItemsCancellable = activeTimelineProvider
|
||||
.updatePublisher
|
||||
@@ -367,7 +368,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
// Check if we need to add anything to the top of the timeline.
|
||||
switch paginationState.backward {
|
||||
case .timelineEndReached:
|
||||
if !shouldHideStart, !roomProxy.isEncryptedOneToOneRoom {
|
||||
if timelineKind != .pinned, !roomProxy.isEncryptedOneToOneRoom {
|
||||
let timelineStart = TimelineStartRoomTimelineItem(name: roomProxy.name)
|
||||
newTimelineItems.insert(timelineStart, at: 0)
|
||||
}
|
||||
|
||||
-2
@@ -23,7 +23,6 @@ struct RoomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol {
|
||||
RoomTimelineController(roomProxy: roomProxy,
|
||||
timelineProxy: roomProxy.timeline,
|
||||
initialFocussedEventID: initialFocussedEventID,
|
||||
shouldHideStart: false,
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}
|
||||
@@ -36,7 +35,6 @@ struct RoomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol {
|
||||
return RoomTimelineController(roomProxy: roomProxy,
|
||||
timelineProxy: pinnedEventsTimeline,
|
||||
initialFocussedEventID: nil,
|
||||
shouldHideStart: true,
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}
|
||||
|
||||
+1
@@ -38,6 +38,7 @@ enum RoomTimelineControllerError: Error {
|
||||
@MainActor
|
||||
protocol RoomTimelineControllerProtocol {
|
||||
var roomID: String { get }
|
||||
var timelineKind: TimelineKind { get }
|
||||
|
||||
var timelineItems: [RoomTimelineItemProtocol] { get }
|
||||
var callbacks: PassthroughSubject<RoomTimelineControllerCallback, Never> { get }
|
||||
|
||||
@@ -27,7 +27,7 @@ final class TimelineProxy: TimelineProxyProtocol {
|
||||
private let backPaginationStatusSubject = CurrentValueSubject<PaginationStatus, Never>(.timelineEndReached)
|
||||
private let forwardPaginationStatusSubject = CurrentValueSubject<PaginationStatus, Never>(.timelineEndReached)
|
||||
|
||||
let isLive: Bool
|
||||
private let kind: TimelineKind
|
||||
|
||||
private var innerTimelineProvider: RoomTimelineProviderProtocol!
|
||||
var timelineProvider: RoomTimelineProviderProtocol {
|
||||
@@ -38,9 +38,9 @@ final class TimelineProxy: TimelineProxyProtocol {
|
||||
backPaginationStatusObservationToken?.cancel()
|
||||
}
|
||||
|
||||
init(timeline: Timeline, isLive: Bool) {
|
||||
init(timeline: Timeline, kind: TimelineKind) {
|
||||
self.timeline = timeline
|
||||
self.isLive = isLive
|
||||
self.kind = kind
|
||||
}
|
||||
|
||||
func subscribeForUpdates() async {
|
||||
@@ -56,7 +56,7 @@ final class TimelineProxy: TimelineProxyProtocol {
|
||||
|
||||
await subscribeToPagination()
|
||||
|
||||
let provider = await RoomTimelineProvider(timeline: timeline, isLive: isLive, paginationStatePublisher: paginationStatePublisher)
|
||||
let provider = await RoomTimelineProvider(timeline: timeline, kind: kind, paginationStatePublisher: paginationStatePublisher)
|
||||
// Make sure the existing items are built so that we have content in the timeline before
|
||||
// determining whether or not the timeline should paginate to load more items.
|
||||
await provider.waitForInitialItems()
|
||||
@@ -84,15 +84,21 @@ final class TimelineProxy: TimelineProxyProtocol {
|
||||
// We can't subscribe to back pagination on detached timelines and as live timelines
|
||||
// can be shared between multiple instances of the same room on the stack, it is
|
||||
// safer to still use the subscription logic for back pagination when live.
|
||||
await if isLive {
|
||||
paginateBackwardsOnLive(requestSize: requestSize)
|
||||
} else {
|
||||
focussedPaginate(.backwards, requestSize: requestSize)
|
||||
switch kind {
|
||||
case .live:
|
||||
return await paginateBackwardsOnLive(requestSize: requestSize)
|
||||
case .detached:
|
||||
return await focussedPaginate(.backwards, requestSize: requestSize)
|
||||
case .pinned:
|
||||
return .success(())
|
||||
}
|
||||
}
|
||||
|
||||
func paginateForwards(requestSize: UInt16) async -> Result<Void, TimelineProxyError> {
|
||||
await focussedPaginate(.forwards, requestSize: requestSize)
|
||||
guard kind != .pinned else {
|
||||
return .success(())
|
||||
}
|
||||
return await focussedPaginate(.forwards, requestSize: requestSize)
|
||||
}
|
||||
|
||||
/// Paginate backwards using the subscription from Rust to drive the pagination state.
|
||||
@@ -538,7 +544,8 @@ final class TimelineProxy: TimelineProxyProtocol {
|
||||
}
|
||||
|
||||
private func subscribeToPagination() async {
|
||||
if isLive {
|
||||
switch kind {
|
||||
case .live:
|
||||
let backPaginationListener = RoomPaginationStatusListener { [weak self] status in
|
||||
guard let self else {
|
||||
return
|
||||
@@ -557,13 +564,15 @@ final class TimelineProxy: TimelineProxyProtocol {
|
||||
} catch {
|
||||
MXLog.error("Failed to subscribe to back pagination status with error: \(error)")
|
||||
}
|
||||
} else {
|
||||
forwardPaginationStatusSubject.send(.timelineEndReached)
|
||||
case .detached:
|
||||
// Detached timelines don't support observation, set the initial state ourself.
|
||||
backPaginationStatusSubject.send(.idle)
|
||||
forwardPaginationStatusSubject.send(.idle)
|
||||
case .pinned:
|
||||
backPaginationStatusSubject.send(.timelineEndReached)
|
||||
forwardPaginationStatusSubject.send(.timelineEndReached)
|
||||
}
|
||||
|
||||
// Detached timelines don't support observation, set the initial state ourself.
|
||||
forwardPaginationStatusSubject.send(isLive ? .timelineEndReached : .idle)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,12 @@ import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
enum TimelineKind {
|
||||
case live
|
||||
case detached
|
||||
case pinned
|
||||
}
|
||||
|
||||
enum TimelineProxyError: Error {
|
||||
case sdkError(Error)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user