mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
RoomTimeline… refactor (drop the Room). (#3728)
* Add the timeline controller factory to the timeline view model. In preparation for building a timeline to swipe through media in QuickLook. * Refactor RoomTimelineControllerFactory. * Refactor RoomTimelineController. * Refactor RoomTimelineProvider.
This commit is contained in:
+9
-9
@@ -11,7 +11,7 @@ import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
class MockTimelineController: TimelineControllerProtocol {
|
||||
/// An array of timeline item arrays that will be inserted in order for each back pagination request.
|
||||
var backPaginationResponses: [[RoomTimelineItemProtocol]] = []
|
||||
/// An array of timeline items that will be appended in order when ``simulateIncomingItems()`` is called.
|
||||
@@ -21,7 +21,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
var roomID: String { roomProxy?.id ?? "MockRoomIdentifier" }
|
||||
var timelineKind: TimelineKind
|
||||
|
||||
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
|
||||
let callbacks = PassthroughSubject<TimelineControllerCallback, Never>()
|
||||
|
||||
var paginationState: PaginationState = .initial {
|
||||
didSet {
|
||||
@@ -34,14 +34,14 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
|
||||
private var client: UITestsSignalling.Client?
|
||||
|
||||
static var mediaGallery: MockRoomTimelineController {
|
||||
MockRoomTimelineController(timelineKind: .media(.mediaFilesScreen), timelineItems: (0..<5).reduce([]) { partialResult, _ in
|
||||
static var mediaGallery: MockTimelineController {
|
||||
MockTimelineController(timelineKind: .media(.mediaFilesScreen), timelineItems: (0..<5).reduce([]) { partialResult, _ in
|
||||
partialResult + [RoomTimelineItemFixtures.separator] + RoomTimelineItemFixtures.mediaChunk
|
||||
})
|
||||
}
|
||||
|
||||
static var emptyMediaGallery: MockRoomTimelineController {
|
||||
let mock = MockRoomTimelineController(timelineKind: .media(.mediaFilesScreen))
|
||||
static var emptyMediaGallery: MockTimelineController {
|
||||
let mock = MockTimelineController(timelineKind: .media(.mediaFilesScreen))
|
||||
mock.paginationState = PaginationState(backward: .timelineEndReached, forward: .timelineEndReached)
|
||||
return mock
|
||||
}
|
||||
@@ -63,7 +63,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
}
|
||||
|
||||
private(set) var focusOnEventCallCount = 0
|
||||
func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
|
||||
func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result<Void, TimelineControllerError> {
|
||||
focusOnEventCallCount += 1
|
||||
callbacks.send(.isLive(false))
|
||||
return .success(())
|
||||
@@ -75,7 +75,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
callbacks.send(.isLive(true))
|
||||
}
|
||||
|
||||
func paginateBackwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
|
||||
func paginateBackwards(requestSize: UInt16) async -> Result<Void, TimelineControllerError> {
|
||||
paginationState = PaginationState(backward: .paginating, forward: .timelineEndReached)
|
||||
|
||||
if client == nil {
|
||||
@@ -85,7 +85,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
return .success(())
|
||||
}
|
||||
|
||||
func paginateForwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
|
||||
func paginateForwards(requestSize: UInt16) async -> Result<Void, TimelineControllerError> {
|
||||
// try? await simulateForwardPagination()
|
||||
.success(())
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
//
|
||||
// Copyright 2022-2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
struct RoomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol {
|
||||
func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
initialFocussedEventID: String?,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) -> RoomTimelineControllerProtocol {
|
||||
RoomTimelineController(roomProxy: roomProxy,
|
||||
timelineProxy: roomProxy.timeline,
|
||||
initialFocussedEventID: initialFocussedEventID,
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
mediaProvider: mediaProvider,
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}
|
||||
|
||||
func buildPinnedEventsRoomTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) async -> RoomTimelineControllerProtocol? {
|
||||
guard let pinnedEventsTimeline = await roomProxy.pinnedEventsTimeline else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return RoomTimelineController(roomProxy: roomProxy,
|
||||
timelineProxy: pinnedEventsTimeline,
|
||||
initialFocussedEventID: nil,
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
mediaProvider: mediaProvider,
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}
|
||||
|
||||
func buildMessageFilteredRoomTimelineController(allowedMessageTypes: [RoomMessageEventMessageType],
|
||||
roomProxy: JoinedRoomProxyProtocol,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) async -> Result<RoomTimelineControllerProtocol, RoomTimelineFactoryControllerError> {
|
||||
switch await roomProxy.messageFilteredTimeline(allowedMessageTypes: allowedMessageTypes) {
|
||||
case .success(let timelineProxy):
|
||||
return .success(RoomTimelineController(roomProxy: roomProxy,
|
||||
timelineProxy: timelineProxy,
|
||||
initialFocussedEventID: nil,
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
mediaProvider: mediaProvider,
|
||||
appSettings: ServiceLocator.shared.settings))
|
||||
case .failure(let error):
|
||||
return .failure(.roomProxyError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// Copyright 2022-2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
enum RoomTimelineFactoryControllerError: Error {
|
||||
case roomProxyError(RoomProxyError)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
protocol RoomTimelineControllerFactoryProtocol {
|
||||
func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
initialFocussedEventID: String?,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) -> RoomTimelineControllerProtocol
|
||||
|
||||
func buildPinnedEventsRoomTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) async -> RoomTimelineControllerProtocol?
|
||||
|
||||
func buildMessageFilteredRoomTimelineController(allowedMessageTypes: [RoomMessageEventMessageType],
|
||||
roomProxy: JoinedRoomProxyProtocol,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) async -> Result<RoomTimelineControllerProtocol, RoomTimelineFactoryControllerError>
|
||||
}
|
||||
|
||||
// sourcery: AutoMockable
|
||||
extension RoomTimelineControllerFactoryProtocol { }
|
||||
+8
-8
@@ -11,19 +11,19 @@ import IntentsUI
|
||||
import MatrixRustSDK
|
||||
import UIKit
|
||||
|
||||
class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
class TimelineController: TimelineControllerProtocol {
|
||||
private let roomProxy: JoinedRoomProxyProtocol
|
||||
private let liveTimelineProvider: RoomTimelineProviderProtocol
|
||||
private let liveTimelineProvider: TimelineProviderProtocol
|
||||
private let timelineItemFactory: RoomTimelineItemFactoryProtocol
|
||||
private let mediaProvider: MediaProviderProtocol
|
||||
private let appSettings: AppSettings
|
||||
|
||||
private let serialDispatchQueue: DispatchQueue
|
||||
|
||||
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
|
||||
let callbacks = PassthroughSubject<TimelineControllerCallback, Never>()
|
||||
|
||||
private var activeTimeline: TimelineProxyProtocol
|
||||
private var activeTimelineProvider: RoomTimelineProviderProtocol {
|
||||
private var activeTimelineProvider: TimelineProviderProtocol {
|
||||
didSet {
|
||||
configureActiveTimelineProvider()
|
||||
}
|
||||
@@ -57,7 +57,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
self.mediaProvider = mediaProvider
|
||||
self.appSettings = appSettings
|
||||
|
||||
serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility)
|
||||
serialDispatchQueue = DispatchQueue(label: "io.element.elementx.timelineprovider", qos: .utility)
|
||||
|
||||
activeTimeline = timelineProxy
|
||||
activeTimelineProvider = liveTimelineProvider
|
||||
@@ -82,7 +82,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
|
||||
func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result<Void, TimelineControllerError> {
|
||||
switch await roomProxy.timelineFocusedOnEvent(eventID: eventID, numberOfEvents: timelineSize) {
|
||||
case .success(let timeline):
|
||||
await timeline.subscribeForUpdates()
|
||||
@@ -103,7 +103,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
activeTimelineProvider = liveTimelineProvider
|
||||
}
|
||||
|
||||
func paginateBackwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
|
||||
func paginateBackwards(requestSize: UInt16) async -> Result<Void, TimelineControllerError> {
|
||||
MXLog.info("Started back pagination request")
|
||||
switch await activeTimeline.paginateBackwards(requestSize: requestSize) {
|
||||
case .success:
|
||||
@@ -115,7 +115,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
func paginateForwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError> {
|
||||
func paginateForwards(requestSize: UInt16) async -> Result<Void, TimelineControllerError> {
|
||||
MXLog.info("Started forward pagination request")
|
||||
switch await activeTimeline.paginateForwards(requestSize: requestSize) {
|
||||
case .success:
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Copyright 2022-2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
struct TimelineControllerFactory: TimelineControllerFactoryProtocol {
|
||||
func buildTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
initialFocussedEventID: String?,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) -> TimelineControllerProtocol {
|
||||
TimelineController(roomProxy: roomProxy,
|
||||
timelineProxy: roomProxy.timeline,
|
||||
initialFocussedEventID: initialFocussedEventID,
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
mediaProvider: mediaProvider,
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}
|
||||
|
||||
func buildPinnedEventsTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) async -> TimelineControllerProtocol? {
|
||||
guard let pinnedEventsTimeline = await roomProxy.pinnedEventsTimeline else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return TimelineController(roomProxy: roomProxy,
|
||||
timelineProxy: pinnedEventsTimeline,
|
||||
initialFocussedEventID: nil,
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
mediaProvider: mediaProvider,
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}
|
||||
|
||||
func buildMessageFilteredTimelineController(allowedMessageTypes: [RoomMessageEventMessageType],
|
||||
presentation: TimelineKind.MediaPresentation,
|
||||
roomProxy: JoinedRoomProxyProtocol,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) async -> Result<TimelineControllerProtocol, TimelineFactoryControllerError> {
|
||||
switch await roomProxy.messageFilteredTimeline(allowedMessageTypes: allowedMessageTypes, presentation: presentation) {
|
||||
case .success(let timelineProxy):
|
||||
return .success(TimelineController(roomProxy: roomProxy,
|
||||
timelineProxy: timelineProxy,
|
||||
initialFocussedEventID: nil,
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
mediaProvider: mediaProvider,
|
||||
appSettings: ServiceLocator.shared.settings))
|
||||
case .failure(let error):
|
||||
return .failure(.roomProxyError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Copyright 2022-2024 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
enum TimelineFactoryControllerError: Error {
|
||||
case roomProxyError(RoomProxyError)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
protocol TimelineControllerFactoryProtocol {
|
||||
func buildTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
initialFocussedEventID: String?,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) -> TimelineControllerProtocol
|
||||
|
||||
func buildPinnedEventsTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) async -> TimelineControllerProtocol?
|
||||
|
||||
func buildMessageFilteredTimelineController(allowedMessageTypes: [RoomMessageEventMessageType],
|
||||
presentation: TimelineKind.MediaPresentation,
|
||||
roomProxy: JoinedRoomProxyProtocol,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) async -> Result<TimelineControllerProtocol, TimelineFactoryControllerError>
|
||||
}
|
||||
|
||||
// sourcery: AutoMockable
|
||||
extension TimelineControllerFactoryProtocol { }
|
||||
+8
-8
@@ -9,25 +9,25 @@ import Combine
|
||||
import MatrixRustSDK
|
||||
import SwiftUI
|
||||
|
||||
enum RoomTimelineControllerCallback {
|
||||
enum TimelineControllerCallback {
|
||||
case updatedTimelineItems(timelineItems: [RoomTimelineItemProtocol], isSwitchingTimelines: Bool)
|
||||
case paginationState(PaginationState)
|
||||
case isLive(Bool)
|
||||
}
|
||||
|
||||
enum RoomTimelineControllerAction {
|
||||
enum TimelineControllerAction {
|
||||
case displayMediaFile(file: MediaFileHandleProxy, title: String?)
|
||||
case displayLocation(body: String, geoURI: GeoURI, description: String?)
|
||||
case none
|
||||
}
|
||||
|
||||
enum RoomTimelineControllerError: Error {
|
||||
enum TimelineControllerError: Error {
|
||||
case generic
|
||||
case eventNotFound
|
||||
}
|
||||
|
||||
@MainActor
|
||||
protocol RoomTimelineControllerProtocol {
|
||||
protocol TimelineControllerProtocol {
|
||||
var roomID: String { get }
|
||||
var timelineKind: TimelineKind { get }
|
||||
|
||||
@@ -37,17 +37,17 @@ protocol RoomTimelineControllerProtocol {
|
||||
/// The current pagination state, use only for setting up the intial state
|
||||
var paginationState: PaginationState { get }
|
||||
|
||||
var callbacks: PassthroughSubject<RoomTimelineControllerCallback, Never> { get }
|
||||
var callbacks: PassthroughSubject<TimelineControllerCallback, Never> { get }
|
||||
|
||||
func processItemAppearance(_ itemID: TimelineItemIdentifier) async
|
||||
|
||||
func processItemDisappearance(_ itemID: TimelineItemIdentifier) async
|
||||
|
||||
func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result<Void, RoomTimelineControllerError>
|
||||
func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result<Void, TimelineControllerError>
|
||||
func focusLive()
|
||||
|
||||
func paginateBackwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError>
|
||||
func paginateForwards(requestSize: UInt16) async -> Result<Void, RoomTimelineControllerError>
|
||||
func paginateBackwards(requestSize: UInt16) async -> Result<Void, TimelineControllerError>
|
||||
func paginateForwards(requestSize: UInt16) async -> Result<Void, TimelineControllerError>
|
||||
|
||||
func sendReadReceipt(for itemID: TimelineItemIdentifier) async
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
class RoomTimelineProvider: RoomTimelineProviderProtocol {
|
||||
class TimelineProvider: TimelineProviderProtocol {
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private let serialDispatchQueue: DispatchQueue
|
||||
|
||||
@@ -46,7 +46,7 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
|
||||
}
|
||||
|
||||
init(timeline: Timeline, kind: TimelineKind, paginationStatePublisher: AnyPublisher<PaginationState, Never>) {
|
||||
serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility)
|
||||
serialDispatchQueue = DispatchQueue(label: "io.element.elementx.timelineprovider", qos: .utility)
|
||||
itemProxiesSubject = CurrentValueSubject<[TimelineItemProxy], Never>([])
|
||||
self.kind = kind
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ struct PaginationState: Equatable {
|
||||
|
||||
@MainActor
|
||||
// sourcery: AutoMockable
|
||||
protocol RoomTimelineProviderProtocol {
|
||||
protocol TimelineProviderProtocol {
|
||||
/// 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.
|
||||
@@ -20,8 +20,8 @@ final class TimelineProxy: TimelineProxyProtocol {
|
||||
|
||||
private let kind: TimelineKind
|
||||
|
||||
private var innerTimelineProvider: RoomTimelineProviderProtocol!
|
||||
var timelineProvider: RoomTimelineProviderProtocol {
|
||||
private var innerTimelineProvider: TimelineProviderProtocol!
|
||||
var timelineProvider: TimelineProviderProtocol {
|
||||
innerTimelineProvider
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ final class TimelineProxy: TimelineProxyProtocol {
|
||||
|
||||
await subscribeToPagination()
|
||||
|
||||
let provider = await RoomTimelineProvider(timeline: timeline, kind: kind, paginationStatePublisher: paginationStatePublisher)
|
||||
let provider = await TimelineProvider(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()
|
||||
|
||||
@@ -27,7 +27,7 @@ enum TimelineProxyError: Error {
|
||||
|
||||
// sourcery: AutoMockable
|
||||
protocol TimelineProxyProtocol {
|
||||
var timelineProvider: RoomTimelineProviderProtocol { get }
|
||||
var timelineProvider: TimelineProviderProtocol { get }
|
||||
|
||||
func subscribeForUpdates() async
|
||||
|
||||
|
||||
Reference in New Issue
Block a user