diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index e81e998ed..b262b8491 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -111,8 +111,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/matrix-org/matrix-rust-components-swift", "state" : { - "revision" : "6c11b548074f4bfedfe73a9724d7d8d1709b0ede", - "version" : "1.0.60-alpha" + "revision" : "1c1cd29d5720ecd0f6043b5d8f5a40d30dd5ec41", + "version" : "1.0.61-alpha" } }, { @@ -181,7 +181,7 @@ { "identity" : "swift-snapshot-testing", "kind" : "remoteSourceControl", - "location" : "https://github.com/pointfreeco/swift-snapshot-testing", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing.git", "state" : { "revision" : "cef5b3f6f11781dd4591bdd1dd0a3d22bd609334", "version" : "1.11.0" diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 5339fa9c3..fd8627eb4 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -60,19 +60,19 @@ class ClientProxy: ClientProxyProtocol { private var slidingSyncObserverToken: TaskHandle? private var slidingSync: SlidingSyncProtocol? - var visibleRoomsSlidingSyncView: SlidingSyncList? - var visibleRoomsViewProxy: SlidingSyncViewProxy? + var visibleRoomsListBuilder: SlidingSyncListBuilder? + var visibleRoomsListProxy: SlidingSyncListProxy? var visibleRoomsSummaryProvider: RoomSummaryProviderProtocol? - var allRoomsSlidingSyncView: SlidingSyncList? - var allRoomsViewProxy: SlidingSyncViewProxy? + var allRoomsListBuilder: SlidingSyncListBuilder? + var allRoomsListProxy: SlidingSyncListProxy? var allRoomsSummaryProvider: RoomSummaryProviderProtocol? - var invitesSlidingSyncView: SlidingSyncList? - var invitesViewProxy: SlidingSyncViewProxy? + var invitesListBuilder: SlidingSyncListBuilder? + var invitesListProxy: SlidingSyncListProxy? var invitesSummaryProvider: RoomSummaryProviderProtocol? - var notificationsSlidingSyncView: SlidingSyncList? + var notificationsListBuilder: SlidingSyncListBuilder? private var loadCachedAvatarURLTask: Task? private let avatarURLSubject = CurrentValueSubject(nil) @@ -84,7 +84,7 @@ class ClientProxy: ClientProxyProtocol { } private var cancellables = Set() - private var visibleRoomsViewProxyStateObservationToken: AnyCancellable? + private var visibleRoomsListProxyStateObservationToken: AnyCancellable? deinit { // These need to be inlined instead of using stopSync() @@ -303,13 +303,7 @@ class ClientProxy: ClientProxyProtocol { self.avatarURLSubject.value = urlString.flatMap(URL.init) } } - - private func restartSync() { - MXLog.info("Restarting sync") - stopSync() - startSync() - } - + private func configureSlidingSync() { guard slidingSync == nil else { fatalError("This shouldn't be called more than once") @@ -320,34 +314,34 @@ class ClientProxy: ClientProxyProtocol { // List observers need to be setup before calling build() on the SlidingSyncBuilder otherwise // cold cache state and count updates will be lost - buildAndConfigureVisibleRoomsSlidingSyncView() - buildAndConfigureAllRoomsSlidingSyncView() - buildAndConfigureInvitesSlidingSyncView() - buildAndConfigureNotificationSyncView() + buildAndConfigureVisibleRoomsSlidingSyncList() + buildAndConfigureAllRoomsSlidingSyncList() + buildAndConfigureInvitesSlidingSyncList() + buildAndConfigureNotificationsSlidingSyncList() - guard let visibleRoomsSlidingSyncView else { + guard let visibleRoomsListBuilder else { MXLog.error("Visible rooms sliding sync view unavailable") return } let roomListRecencyOrderingAllowedEventTypes = ["m.room.message", "m.room.encrypted", "m.sticker"] - // Add the visibleRoomsSlidingSyncView here so that it can take advantage of the SS builder cold cache - // We will still register the allRoomsSlidingSyncView later, and than will have no cache + // Add the visibleRoomsSlidingSyncList here so that it can take advantage of the SS builder cold cache + // We will still register the allRoomsSlidingSyncList later, and than will have no cache let slidingSync = try slidingSyncBuilder - .addList(v: visibleRoomsSlidingSyncView) + .addList(listBuilder: visibleRoomsListBuilder) .withCommonExtensions() .bumpEventTypes(bumpEventTypes: roomListRecencyOrderingAllowedEventTypes) .storageKey(name: "ElementX") .build() // Don't forget to update the view proxies after building the slidingSync - visibleRoomsViewProxy?.setSlidingSync(slidingSync: slidingSync) - allRoomsViewProxy?.setSlidingSync(slidingSync: slidingSync) - invitesViewProxy?.setSlidingSync(slidingSync: slidingSync) + visibleRoomsListProxy?.setSlidingSync(slidingSync: slidingSync) + allRoomsListProxy?.setSlidingSync(slidingSync: slidingSync) + invitesListProxy?.setSlidingSync(slidingSync: slidingSync) // Build the room summary providers later so the sliding sync view proxies are up to date and the - // currentRoomList is populate with the data from the cold cache + // currentRoomList is populated with the data from the cold cache buildRoomSummaryProviders() slidingSync.setObserver(observer: WeakClientProxyWrapper(clientProxy: self)) @@ -358,90 +352,93 @@ class ClientProxy: ClientProxyProtocol { } } - private func buildAndConfigureVisibleRoomsSlidingSyncView() { - guard visibleRoomsSlidingSyncView == nil else { + private func buildAndConfigureVisibleRoomsSlidingSyncList() { + guard visibleRoomsListBuilder == nil else { fatalError("This shouldn't be called more than once") } - let visibleRoomsSlidingSyncView = SlidingSyncListBuilder(name: "CurrentlyVisibleRooms") + let listName = "CurrentlyVisibleRooms" + + let visibleRoomsListProxy = SlidingSyncListProxy(name: listName) + + let visibleRoomsListBuilder = SlidingSyncListBuilder(name: listName) .timelineLimit(limit: UInt32(SlidingSyncConstants.initialTimelineLimit)) // Starts off with zero to quickly load rooms, then goes to 1 while scrolling to quickly load last messages and 20 when the scrolling stops to load room history .requiredState(requiredState: slidingSyncRequiredState) .filters(filters: slidingSyncFilters) .syncMode(mode: .selective) .addRange(from: 0, to: 20) - .build() + .onceBuilt(callback: visibleRoomsListProxy) - let visibleRoomsViewProxy = SlidingSyncViewProxy(slidingSyncView: visibleRoomsSlidingSyncView, name: "Visible rooms") + self.visibleRoomsListBuilder = visibleRoomsListBuilder + self.visibleRoomsListProxy = visibleRoomsListProxy - self.visibleRoomsSlidingSyncView = visibleRoomsSlidingSyncView - self.visibleRoomsViewProxy = visibleRoomsViewProxy - - // Changes to the visibleRoomsSlidingSyncView range need to restart the connection to be applied - visibleRoomsViewProxy.visibleRangeUpdatePublisher.sink { [weak self] in - self?.restartSync() - } - .store(in: &cancellables) - - // The allRoomsSlidingSyncView will be registered as soon as the visibleRoomsSlidingSyncView receives its first update - visibleRoomsViewProxyStateObservationToken = visibleRoomsViewProxy.statePublisher.sink { [weak self] state in + // The allRoomsSlidingSyncList will be registered as soon as the visibleRoomsSlidingSyncList receives its first update + visibleRoomsListProxyStateObservationToken = visibleRoomsListProxy.statePublisher.sink { [weak self] state in guard state == .fullyLoaded else { return } - + MXLog.info("Visible rooms view received first update, configuring views post initial sync") self?.configureViewsPostInitialSync() - self?.visibleRoomsViewProxyStateObservationToken = nil + self?.visibleRoomsListProxyStateObservationToken = nil } } - private func buildAndConfigureAllRoomsSlidingSyncView() { - guard allRoomsSlidingSyncView == nil else { + private func buildAndConfigureAllRoomsSlidingSyncList() { + guard allRoomsListBuilder == nil else { fatalError("This shouldn't be called more than once") } - let allRoomsSlidingSyncView = SlidingSyncListBuilder(name: "AllRooms") + let listName = "AllRooms" + + let allRoomsListProxy = SlidingSyncListProxy(name: listName) + + let allRoomsListBuilder = SlidingSyncListBuilder(name: listName) .noTimelineLimit() .requiredState(requiredState: slidingSyncRequiredState) .filters(filters: slidingSyncFilters) .syncMode(mode: .growing) .batchSize(batchSize: 100) - .build() + .onceBuilt(callback: allRoomsListProxy) - self.allRoomsSlidingSyncView = allRoomsSlidingSyncView - allRoomsViewProxy = SlidingSyncViewProxy(slidingSyncView: allRoomsSlidingSyncView, name: "All rooms") + self.allRoomsListBuilder = allRoomsListBuilder + self.allRoomsListProxy = allRoomsListProxy } - - private func buildAndConfigureInvitesSlidingSyncView() { - guard invitesSlidingSyncView == nil else { + + private func buildAndConfigureInvitesSlidingSyncList() { + guard invitesListBuilder == nil else { fatalError("This shouldn't be called more than once") } - let invitesView = SlidingSyncListBuilder(name: "Invites") + let listName = "Invites" + + let invitesListProxy = SlidingSyncListProxy(name: "Invites") + + let invitesListBuilder = SlidingSyncListBuilder(name: listName) .noTimelineLimit() .requiredState(requiredState: slidingSyncInvitesRequiredState) .filters(filters: slidingSyncInviteFilters) .syncMode(mode: .growing) .batchSize(batchSize: 100) - .build() + .onceBuilt(callback: invitesListProxy) - invitesSlidingSyncView = invitesView - invitesViewProxy = SlidingSyncViewProxy(slidingSyncView: invitesView, name: "Invites") + self.invitesListBuilder = invitesListBuilder + self.invitesListProxy = invitesListProxy } - private func buildAndConfigureNotificationSyncView() { - guard notificationsSlidingSyncView == nil else { + private func buildAndConfigureNotificationsSlidingSyncList() { + guard notificationsListBuilder == nil else { fatalError("This shouldn't be called more than once") } - - let notificationsSlidingSyncView = SlidingSyncListBuilder(name: "Notifications") + + let notificationsListBuilder = SlidingSyncListBuilder(name: "Notifications") .noTimelineLimit() .requiredState(requiredState: slidingSyncNotificationsRequiredState) .filters(filters: slidingSyncNotificationsFilters) .syncMode(mode: .growing) .batchSize(batchSize: 100) - .build() - - self.notificationsSlidingSyncView = notificationsSlidingSyncView + + self.notificationsListBuilder = notificationsListBuilder } private func buildRoomSummaryProviders() { @@ -449,18 +446,18 @@ class ClientProxy: ClientProxyProtocol { fatalError("This shouldn't be called more than once") } - guard let visibleRoomsViewProxy, let allRoomsViewProxy, let invitesViewProxy else { + guard let visibleRoomsListProxy, let allRoomsListProxy, let invitesListProxy else { MXLog.error("Sliding sync view proxies unavailable") return } - visibleRoomsSummaryProvider = RoomSummaryProvider(slidingSyncViewProxy: visibleRoomsViewProxy, + visibleRoomsSummaryProvider = RoomSummaryProvider(slidingSyncListProxy: visibleRoomsListProxy, eventStringBuilder: RoomEventStringBuilder(stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID))) - allRoomsSummaryProvider = RoomSummaryProvider(slidingSyncViewProxy: allRoomsViewProxy, + allRoomsSummaryProvider = RoomSummaryProvider(slidingSyncListProxy: allRoomsListProxy, eventStringBuilder: RoomEventStringBuilder(stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID))) - invitesSummaryProvider = RoomSummaryProvider(slidingSyncViewProxy: invitesViewProxy, + invitesSummaryProvider = RoomSummaryProvider(slidingSyncListProxy: invitesListProxy, eventStringBuilder: RoomEventStringBuilder(stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID))) } @@ -510,35 +507,33 @@ class ClientProxy: ClientProxyProtocol { notTags: []) private func configureViewsPostInitialSync() { - if let visibleRoomsSlidingSyncView { + if let visibleRoomsListProxy { MXLog.info("Setting visible rooms view timeline limit to \(SlidingSyncConstants.lastMessageTimelineLimit)") - visibleRoomsSlidingSyncView.setTimelineLimit(value: UInt32(SlidingSyncConstants.lastMessageTimelineLimit)) + visibleRoomsListProxy.updateVisibleRange(nil, timelineLimit: SlidingSyncConstants.lastMessageTimelineLimit) } else { MXLog.error("Visible rooms sliding sync view unavailable") } - - if let allRoomsSlidingSyncView { + + if let allRoomsListBuilder { MXLog.info("Registering all rooms view") - _ = slidingSync?.addList(list: allRoomsSlidingSyncView) + _ = slidingSync?.addList(listBuilder: allRoomsListBuilder) } else { MXLog.error("All rooms sliding sync view unavailable") } - - if let invitesSlidingSyncView { + + if let invitesListBuilder { MXLog.info("Registering invites view") - _ = slidingSync?.addList(list: invitesSlidingSyncView) + _ = slidingSync?.addList(listBuilder: invitesListBuilder) } else { MXLog.error("Invites sliding sync view unavailable") } - if let notificationsSlidingSyncView { + if let notificationsListBuilder { MXLog.info("Registering notifications view") - _ = slidingSync?.addList(list: notificationsSlidingSyncView) + _ = slidingSync?.addList(listBuilder: notificationsListBuilder) } else { MXLog.error("Notifications sliding sync view unavailable") } - - restartSync() } private func roomTupleForIdentifier(_ identifier: String) -> (SlidingSyncRoom?, Room?) { diff --git a/ElementX/Sources/Services/Client/SlidingSyncViewProxy.swift b/ElementX/Sources/Services/Client/SlidingSyncListProxy.swift similarity index 64% rename from ElementX/Sources/Services/Client/SlidingSyncViewProxy.swift rename to ElementX/Sources/Services/Client/SlidingSyncListProxy.swift index 35f6314c3..9e5b7ae4f 100644 --- a/ElementX/Sources/Services/Client/SlidingSyncViewProxy.swift +++ b/ElementX/Sources/Services/Client/SlidingSyncListProxy.swift @@ -18,7 +18,7 @@ import Combine import Foundation import MatrixRustSDK -private class SlidingSyncViewObserver: SlidingSyncListRoomListObserver, SlidingSyncListStateObserver, SlidingSyncListRoomsCountObserver { +private class SlidingSyncListObserver: SlidingSyncListRoomListObserver, SlidingSyncListStateObserver, SlidingSyncListRoomsCountObserver { /// Publishes room list diffs as they come in through sliding sync let roomListDiffPublisher = PassthroughSubject() @@ -33,7 +33,7 @@ private class SlidingSyncViewObserver: SlidingSyncListRoomListObserver, SlidingS init(name: String) { self.name = name } - + // MARK: - SlidingSyncListRoomListObserver func didReceiveUpdate(diff: SlidingSyncListRoomsListDiff) { @@ -56,9 +56,10 @@ private class SlidingSyncViewObserver: SlidingSyncListRoomListObserver, SlidingS } } -class SlidingSyncViewProxy { - private var slidingSync: SlidingSyncProtocol! - private let slidingSyncView: SlidingSyncListProtocol +class SlidingSyncListProxy: SlidingSyncListOnceBuilt { + private let name: String + private var slidingSync: SlidingSyncProtocol? + private var slidingSyncList: SlidingSyncListProtocol? private var listUpdateObserverToken: TaskHandle? private var stateUpdateObserverToken: TaskHandle? @@ -69,7 +70,6 @@ class SlidingSyncViewProxy { let diffPublisher = PassthroughSubject() let statePublisher = PassthroughSubject() let countPublisher = PassthroughSubject() - let visibleRangeUpdatePublisher = PassthroughSubject() deinit { listUpdateObserverToken?.cancel() @@ -77,26 +77,8 @@ class SlidingSyncViewProxy { countUpdateObserverToken?.cancel() } - init(slidingSyncView: SlidingSyncListProtocol, name: String) { - self.slidingSyncView = slidingSyncView - - let slidingSyncViewObserver = SlidingSyncViewObserver(name: name) - - slidingSyncViewObserver.stateUpdatePublisher - .subscribe(statePublisher) - .store(in: &cancellables) - - slidingSyncViewObserver.countUpdatePublisher - .subscribe(countPublisher) - .store(in: &cancellables) - - slidingSyncViewObserver.roomListDiffPublisher - .subscribe(diffPublisher) - .store(in: &cancellables) - - listUpdateObserverToken = slidingSyncView.observeRoomList(observer: slidingSyncViewObserver) - stateUpdateObserverToken = slidingSyncView.observeState(observer: slidingSyncViewObserver) - countUpdateObserverToken = slidingSyncView.observeRoomsCount(observer: slidingSyncViewObserver) + init(name: String) { + self.name = name } func setSlidingSync(slidingSync: SlidingSyncProtocol) { @@ -104,23 +86,60 @@ class SlidingSyncViewProxy { } func currentRoomsList() -> [RoomListEntry] { - slidingSyncView.currentRoomList() + guard let slidingSyncList else { + return [] + } + + return slidingSyncList.currentRoomList() } func roomForIdentifier(_ identifier: String) throws -> SlidingSyncRoomProtocol? { - try slidingSync.getRoom(roomId: identifier) + guard let slidingSync else { + return nil + } + + return try slidingSync.getRoom(roomId: identifier) } - func updateVisibleRange(_ range: Range, timelineLimit: UInt) { - MXLog.info("Setting sliding sync view range to \(range), timeline limit: \(timelineLimit)") - + func updateVisibleRange(_ range: Range?, timelineLimit: UInt?) { do { - try slidingSyncView.setRange(start: UInt32(range.lowerBound), end: UInt32(range.upperBound)) - slidingSyncView.setTimelineLimit(value: UInt32(timelineLimit)) + if let range { + MXLog.info("Setting '\(name)' list range to \(range)") + try slidingSyncList?.setRange(start: UInt32(range.lowerBound), end: UInt32(range.upperBound)) + } - visibleRangeUpdatePublisher.send(()) + if let timelineLimit { + MXLog.info("Setting '\(name)' list timeline limit to \(timelineLimit)") + slidingSyncList?.setTimelineLimit(value: UInt32(timelineLimit)) + } } catch { MXLog.error("Failed setting sliding sync list range with error: \(error)") } } + + // MARK: - SlidingSyncListOnceBuilt + + func updateList(list: MatrixRustSDK.SlidingSyncList) -> MatrixRustSDK.SlidingSyncList { + slidingSyncList = list + + let slidingSyncListObserver = SlidingSyncListObserver(name: name) + + slidingSyncListObserver.stateUpdatePublisher + .subscribe(statePublisher) + .store(in: &cancellables) + + slidingSyncListObserver.countUpdatePublisher + .subscribe(countPublisher) + .store(in: &cancellables) + + slidingSyncListObserver.roomListDiffPublisher + .subscribe(diffPublisher) + .store(in: &cancellables) + + listUpdateObserverToken = list.observeRoomList(observer: slidingSyncListObserver) + stateUpdateObserverToken = list.observeState(observer: slidingSyncListObserver) + countUpdateObserverToken = list.observeRoomsCount(observer: slidingSyncListObserver) + + return list + } } diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift index 1c2ccc88b..51cfdfe8b 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift @@ -19,7 +19,7 @@ import Foundation import MatrixRustSDK class RoomSummaryProvider: RoomSummaryProviderProtocol { - private let slidingSyncViewProxy: SlidingSyncViewProxy + private let slidingSyncListProxy: SlidingSyncListProxy private let serialDispatchQueue: DispatchQueue private let eventStringBuilder: RoomEventStringBuilder @@ -47,34 +47,34 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { } } - init(slidingSyncViewProxy: SlidingSyncViewProxy, eventStringBuilder: RoomEventStringBuilder) { - self.slidingSyncViewProxy = slidingSyncViewProxy + init(slidingSyncListProxy: SlidingSyncListProxy, eventStringBuilder: RoomEventStringBuilder) { + self.slidingSyncListProxy = slidingSyncListProxy serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomsummaryprovider", qos: .utility) self.eventStringBuilder = eventStringBuilder - rooms = slidingSyncViewProxy.currentRoomsList().map { roomListEntry in + rooms = slidingSyncListProxy.currentRoomsList().map { roomListEntry in buildSummaryForRoomListEntry(roomListEntry) } roomListSubject.send(rooms) // didSet not called from initialisers - slidingSyncViewProxy.statePublisher + slidingSyncListProxy.statePublisher .map(RoomSummaryProviderState.init) .subscribe(stateSubject) .store(in: &cancellables) - slidingSyncViewProxy.countPublisher + slidingSyncListProxy.countPublisher .subscribe(countSubject) .store(in: &cancellables) - slidingSyncViewProxy.diffPublisher + slidingSyncListProxy.diffPublisher .collect(.byTime(serialDispatchQueue, 0.025)) .sink { [weak self] in self?.updateRoomsWithDiffs($0) } .store(in: &cancellables) } func updateVisibleRange(_ range: Range, timelineLimit: UInt) { - slidingSyncViewProxy.updateVisibleRange(range, timelineLimit: timelineLimit) + slidingSyncListProxy.updateVisibleRange(range, timelineLimit: timelineLimit) } // MARK: - Private @@ -111,7 +111,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { } private func buildRoomSummaryForIdentifier(_ identifier: String, invalidated: Bool) -> RoomSummary { - guard let room = try? slidingSyncViewProxy.roomForIdentifier(identifier) else { + guard let room = try? slidingSyncListProxy.roomForIdentifier(identifier) else { MXLog.error("Failed finding room with id: \(identifier)") return .empty } diff --git a/project.yml b/project.yml index 3be5771ec..847151719 100644 --- a/project.yml +++ b/project.yml @@ -42,7 +42,7 @@ include: packages: MatrixRustSDK: url: https://github.com/matrix-org/matrix-rust-components-swift - exactVersion: 1.0.60-alpha + exactVersion: 1.0.61-alpha # path: ../matrix-rust-sdk DesignKit: path: DesignKit