mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 20:39:24 +00:00
Add support for the new RoomList API (#1092)
* Add support for the new RoomList API Squashed commits: [8c2625a] Re-enable invites [339e59b5] Add automatic syncing restarts after entering the terminated state for whatever reason [b5498e96] Reimplemented room list state management and usage [8154b0cf] Fix visible rooms range setting after scrolling stops [5155c44d] Hook up initial loading idicator and session verification banner to room list service states [700a5a2e] Enable back room subscriptions, timeline listeners and unread notifications / counts [9df383bc] Enable last messages and timestamps, they don't crash on release builds [7c4da9da] Add back certain fields now that the "full room" is available [9f6534a7] Adopt interfaces to new RoomList API, untested as requests don't go to the right URL * Tweaks following code review * Bump the RustSDK to v1.0.76-alpha
This commit is contained in:
@@ -19,7 +19,7 @@ import Foundation
|
||||
import MatrixRustSDK
|
||||
import UIKit
|
||||
|
||||
private class WeakClientProxyWrapper: ClientDelegate, SlidingSyncObserver {
|
||||
private class WeakClientProxyWrapper: ClientDelegate {
|
||||
private weak var clientProxy: ClientProxy?
|
||||
|
||||
init(clientProxy: ClientProxy) {
|
||||
@@ -37,13 +37,6 @@ private class WeakClientProxyWrapper: ClientDelegate, SlidingSyncObserver {
|
||||
MXLog.info("The session has updated tokens.")
|
||||
clientProxy?.updateRestorationToken()
|
||||
}
|
||||
|
||||
// MARK: - SlidingSyncDelegate
|
||||
|
||||
func didReceiveSyncUpdate(summary: UpdateSummary) {
|
||||
MXLog.info("Received sliding sync update")
|
||||
clientProxy?.didReceiveSlidingSyncUpdate(summary: summary)
|
||||
}
|
||||
}
|
||||
|
||||
class ClientProxy: ClientProxyProtocol {
|
||||
@@ -52,22 +45,13 @@ class ClientProxy: ClientProxyProtocol {
|
||||
private var sessionVerificationControllerProxy: SessionVerificationControllerProxy?
|
||||
private let mediaLoader: MediaLoaderProtocol
|
||||
private let clientQueue: DispatchQueue
|
||||
|
||||
private var roomListService: RoomList?
|
||||
private var roomListSyncTaskHandle: TaskHandle?
|
||||
private var roomListStateUpdateTaskHandle: TaskHandle?
|
||||
|
||||
private var slidingSyncObserverToken: TaskHandle?
|
||||
private var slidingSync: SlidingSyncProtocol?
|
||||
private var slidingSyncTasks = [TaskHandle?]()
|
||||
|
||||
var visibleRoomsListBuilder: SlidingSyncListBuilder?
|
||||
var visibleRoomsListProxy: SlidingSyncListProxy?
|
||||
var visibleRoomsSummaryProvider: RoomSummaryProviderProtocol?
|
||||
|
||||
var allRoomsListBuilder: SlidingSyncListBuilder?
|
||||
var allRoomsListProxy: SlidingSyncListProxy?
|
||||
var allRoomsSummaryProvider: RoomSummaryProviderProtocol?
|
||||
|
||||
var invitesListBuilder: SlidingSyncListBuilder?
|
||||
var invitesListProxy: SlidingSyncListProxy?
|
||||
var invitesSummaryProvider: RoomSummaryProviderProtocol?
|
||||
var roomSummaryProvider: RoomSummaryProviderProtocol?
|
||||
var inviteSummaryProvider: RoomSummaryProviderProtocol?
|
||||
|
||||
private let roomListRecencyOrderingAllowedEventTypes = ["m.room.message", "m.room.encrypted", "m.sticker"]
|
||||
|
||||
@@ -87,8 +71,7 @@ class ClientProxy: ClientProxyProtocol {
|
||||
// These need to be inlined instead of using stopSync()
|
||||
// as we can't call async methods safely from deinit
|
||||
client.setDelegate(delegate: nil)
|
||||
slidingSyncObserverToken?.cancel()
|
||||
slidingSync?.setObserver(observer: nil)
|
||||
roomListSyncTaskHandle?.cancel()
|
||||
}
|
||||
|
||||
let callbacks = PassthroughSubject<ClientProxyCallback, Never>()
|
||||
@@ -103,7 +86,7 @@ class ClientProxy: ClientProxyProtocol {
|
||||
let delegate = WeakClientProxyWrapper(clientProxy: self)
|
||||
client.setDelegate(delegate: delegate)
|
||||
|
||||
configureSlidingSync()
|
||||
await configureRoomListService()
|
||||
|
||||
loadUserAvatarURLFromCache()
|
||||
}
|
||||
@@ -140,7 +123,7 @@ class ClientProxy: ClientProxyProtocol {
|
||||
}
|
||||
|
||||
var isSyncing: Bool {
|
||||
slidingSyncObserverToken != nil
|
||||
roomListSyncTaskHandle != nil
|
||||
}
|
||||
|
||||
func startSync() {
|
||||
@@ -149,18 +132,13 @@ class ClientProxy: ClientProxyProtocol {
|
||||
return
|
||||
}
|
||||
|
||||
slidingSyncObserverToken = slidingSync?.sync()
|
||||
roomListSyncTaskHandle = roomListService?.sync()
|
||||
}
|
||||
|
||||
func stopSync() {
|
||||
MXLog.info("Stopping sync")
|
||||
slidingSyncObserverToken = nil
|
||||
|
||||
do {
|
||||
try slidingSync?.stopSync()
|
||||
} catch {
|
||||
MXLog.error("Failed stopping sync with error: \(error). Ignore this error if ran just before the app being suspended")
|
||||
}
|
||||
roomListSyncTaskHandle?.cancel()
|
||||
roomListSyncTaskHandle = nil
|
||||
}
|
||||
|
||||
func directRoomForUserID(_ userID: String) async -> Result<String?, ClientProxyError> {
|
||||
@@ -229,7 +207,7 @@ class ClientProxy: ClientProxyProtocol {
|
||||
private func waitForRoomSummary(with result: Result<String, ClientProxyError>, name: String?) async -> Result<String, ClientProxyError> {
|
||||
guard case .success(let roomId) = result else { return result }
|
||||
let runner = ExpiringTaskRunner { [weak self] in
|
||||
guard let roomLists = self?.allRoomsSummaryProvider?.roomListPublisher.values else {
|
||||
guard let roomLists = self?.roomSummaryProvider?.roomListPublisher.values else {
|
||||
return
|
||||
}
|
||||
// for every list of summaries, we check if we have a room summary with matching ID and name (if present)
|
||||
@@ -249,42 +227,42 @@ class ClientProxy: ClientProxyProtocol {
|
||||
|
||||
func roomForIdentifier(_ identifier: String) async -> RoomProxyProtocol? {
|
||||
// Try fetching the room from the cold cache (if available) first
|
||||
var (slidingSyncRoom, room) = await Task.dispatch(on: clientQueue) {
|
||||
var (roomListItem, room) = await Task.dispatch(on: clientQueue) {
|
||||
self.roomTupleForIdentifier(identifier)
|
||||
}
|
||||
|
||||
if let slidingSyncRoom, let room {
|
||||
return await RoomProxy(slidingSyncRoom: slidingSyncRoom,
|
||||
if let roomListItem, let room {
|
||||
return await RoomProxy(roomListItem: roomListItem,
|
||||
room: room,
|
||||
backgroundTaskService: backgroundTaskService)
|
||||
}
|
||||
|
||||
// Else wait for the visible rooms list to go into fully loaded
|
||||
|
||||
guard let visibleRoomsSummaryProvider else {
|
||||
MXLog.error("Visible rooms summary provider not setup yet")
|
||||
guard let roomSummaryProvider else {
|
||||
MXLog.error("Rooms summary provider not setup yet")
|
||||
return nil
|
||||
}
|
||||
|
||||
if visibleRoomsSummaryProvider.statePublisher.value != .fullyLoaded {
|
||||
_ = await visibleRoomsSummaryProvider.statePublisher.values.first(where: { $0 == .fullyLoaded })
|
||||
if roomSummaryProvider.statePublisher.value != .fullyLoaded {
|
||||
_ = await roomSummaryProvider.statePublisher.values.first(where: { $0 == .fullyLoaded })
|
||||
}
|
||||
|
||||
(slidingSyncRoom, room) = await Task.dispatch(on: clientQueue) {
|
||||
(roomListItem, room) = await Task.dispatch(on: clientQueue) {
|
||||
self.roomTupleForIdentifier(identifier)
|
||||
}
|
||||
|
||||
guard let slidingSyncRoom else {
|
||||
MXLog.error("Invalid slidingSyncRoom for identifier \(identifier)")
|
||||
|
||||
guard let roomListItem else {
|
||||
MXLog.error("Invalid roomListItem for identifier \(identifier)")
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let room else {
|
||||
MXLog.error("Invalid slidingSyncRoom fullRoom for identifier \(identifier)")
|
||||
MXLog.error("Invalid roomListItem fullRoom for identifier \(identifier)")
|
||||
return nil
|
||||
}
|
||||
|
||||
return await RoomProxy(slidingSyncRoom: slidingSyncRoom,
|
||||
|
||||
return await RoomProxy(roomListItem: roomListItem,
|
||||
room: room,
|
||||
backgroundTaskService: backgroundTaskService)
|
||||
}
|
||||
@@ -378,6 +356,11 @@ class ClientProxy: ClientProxyProtocol {
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func restartSync() {
|
||||
stopSync()
|
||||
startSync()
|
||||
}
|
||||
|
||||
private func loadUserAvatarURLFromCache() {
|
||||
loadCachedAvatarURLTask = Task {
|
||||
@@ -394,210 +377,59 @@ class ClientProxy: ClientProxyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
private func configureSlidingSync() {
|
||||
guard slidingSync == nil else {
|
||||
private func configureRoomListService() async {
|
||||
guard roomListService == nil else {
|
||||
fatalError("This shouldn't be called more than once")
|
||||
}
|
||||
|
||||
do {
|
||||
let slidingSyncBuilder = try client.slidingSync(id: "ElementX")
|
||||
let roomListService = try client.roomList()
|
||||
roomListStateUpdateTaskHandle = roomListService.state(listener: RoomListStateListenerProxy { [weak self] state in
|
||||
guard let self else { return }
|
||||
MXLog.info("Received room list update: \(state)")
|
||||
|
||||
if state == .allRooms || state == .carryOn {
|
||||
self.callbacks.send(.receivedSyncUpdate)
|
||||
}
|
||||
|
||||
#warning("Not great, not terrible (ツ)")
|
||||
if state == .terminated {
|
||||
self.restartSync()
|
||||
}
|
||||
|
||||
if state == .allRooms {
|
||||
Task {
|
||||
// Subscribe to invites later as the underlying SlidingSync list is only added when entering AllRooms
|
||||
await self.inviteSummaryProvider?.subscribeIfNecessary(entriesFunction: roomListService.invites(listener:),
|
||||
// This state function is wrong but it's not used
|
||||
entriesLoadingStateFunction: roomListService.entriesLoadingState(listener:))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// List observers need to be setup before calling build() on the SlidingSyncBuilder otherwise
|
||||
// cold cache state and count updates will be lost
|
||||
buildAndConfigureVisibleRoomsSlidingSyncList()
|
||||
buildAndConfigureAllRoomsSlidingSyncList()
|
||||
buildAndConfigureInvitesSlidingSyncList()
|
||||
|
||||
guard let visibleRoomsListBuilder else {
|
||||
MXLog.error("Visible rooms sliding sync view unavailable")
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
var slidingSyncBuilderWithExtension = slidingSyncBuilder
|
||||
.addList(listBuilder: visibleRoomsListBuilder)
|
||||
.withCommonExtensions()
|
||||
|
||||
if !ServiceLocator.shared.settings.readReceiptsEnabled {
|
||||
slidingSyncBuilderWithExtension = slidingSyncBuilderWithExtension.withoutReceiptExtension()
|
||||
}
|
||||
|
||||
let slidingSync = try slidingSyncBuilderWithExtension.build()
|
||||
|
||||
// Don't forget to update the view proxies after building the 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 populated with the data from the cold cache
|
||||
buildRoomSummaryProviders()
|
||||
|
||||
slidingSync.setObserver(observer: WeakClientProxyWrapper(clientProxy: self))
|
||||
|
||||
self.slidingSync = slidingSync
|
||||
} catch {
|
||||
MXLog.error("Failed building sliding sync with error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func buildAndConfigureVisibleRoomsSlidingSyncList() {
|
||||
guard visibleRoomsListBuilder == nil else {
|
||||
fatalError("This shouldn't be called more than once")
|
||||
}
|
||||
|
||||
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)
|
||||
.syncModeSelective(selectiveModeBuilder: .init().addRange(start: 0, endInclusive: 20))
|
||||
.bumpEventTypes(bumpEventTypes: roomListRecencyOrderingAllowedEventTypes)
|
||||
.onceBuilt(callback: visibleRoomsListProxy)
|
||||
|
||||
self.visibleRoomsListBuilder = visibleRoomsListBuilder
|
||||
self.visibleRoomsListProxy = visibleRoomsListProxy
|
||||
|
||||
// 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?.visibleRoomsListProxyStateObservationToken = nil
|
||||
}
|
||||
}
|
||||
|
||||
private func buildAndConfigureAllRoomsSlidingSyncList() {
|
||||
guard allRoomsListBuilder == nil else {
|
||||
fatalError("This shouldn't be called more than once")
|
||||
}
|
||||
|
||||
let listName = "AllRooms"
|
||||
|
||||
let allRoomsListProxy = SlidingSyncListProxy(name: listName)
|
||||
|
||||
let allRoomsListBuilder = SlidingSyncListBuilder(name: listName)
|
||||
.noTimelineLimit()
|
||||
.requiredState(requiredState: slidingSyncRequiredState)
|
||||
.filters(filters: slidingSyncFilters)
|
||||
.syncModeGrowing(batchSize: 100, maximumNumberOfRoomsToFetch: nil)
|
||||
.bumpEventTypes(bumpEventTypes: roomListRecencyOrderingAllowedEventTypes)
|
||||
.onceBuilt(callback: allRoomsListProxy)
|
||||
|
||||
self.allRoomsListBuilder = allRoomsListBuilder
|
||||
self.allRoomsListProxy = allRoomsListProxy
|
||||
}
|
||||
|
||||
private func buildAndConfigureInvitesSlidingSyncList() {
|
||||
guard invitesListBuilder == nil else {
|
||||
fatalError("This shouldn't be called more than once")
|
||||
}
|
||||
|
||||
let listName = "Invites"
|
||||
|
||||
let invitesListProxy = SlidingSyncListProxy(name: "Invites")
|
||||
|
||||
let invitesListBuilder = SlidingSyncListBuilder(name: listName)
|
||||
.noTimelineLimit()
|
||||
.requiredState(requiredState: slidingSyncInvitesRequiredState)
|
||||
.filters(filters: slidingSyncInviteFilters)
|
||||
.syncModeGrowing(batchSize: 100, maximumNumberOfRoomsToFetch: nil)
|
||||
.onceBuilt(callback: invitesListProxy)
|
||||
|
||||
self.invitesListBuilder = invitesListBuilder
|
||||
self.invitesListProxy = invitesListProxy
|
||||
}
|
||||
|
||||
private func buildRoomSummaryProviders() {
|
||||
guard visibleRoomsSummaryProvider == nil, allRoomsSummaryProvider == nil, invitesSummaryProvider == nil else {
|
||||
fatalError("This shouldn't be called more than once")
|
||||
}
|
||||
|
||||
guard let visibleRoomsListProxy, let allRoomsListProxy, let invitesListProxy else {
|
||||
MXLog.error("Sliding sync view proxies unavailable")
|
||||
return
|
||||
}
|
||||
|
||||
visibleRoomsSummaryProvider = RoomSummaryProvider(slidingSyncListProxy: visibleRoomsListProxy,
|
||||
eventStringBuilder: RoomEventStringBuilder(stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)),
|
||||
name: visibleRoomsListProxy.name)
|
||||
|
||||
allRoomsSummaryProvider = RoomSummaryProvider(slidingSyncListProxy: allRoomsListProxy,
|
||||
roomSummaryProvider = RoomSummaryProvider(roomListService: roomListService,
|
||||
eventStringBuilder: RoomEventStringBuilder(stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)),
|
||||
name: allRoomsListProxy.name)
|
||||
|
||||
invitesSummaryProvider = RoomSummaryProvider(slidingSyncListProxy: invitesListProxy,
|
||||
eventStringBuilder: RoomEventStringBuilder(stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)),
|
||||
name: invitesListProxy.name)
|
||||
}
|
||||
|
||||
private lazy var slidingSyncRequiredState = [RequiredState(key: "m.room.avatar", value: ""),
|
||||
RequiredState(key: "m.room.encryption", value: ""),
|
||||
RequiredState(key: "m.room.power_levels", value: "")]
|
||||
|
||||
private lazy var slidingSyncInvitesRequiredState = [RequiredState(key: "m.room.avatar", value: ""),
|
||||
RequiredState(key: "m.room.encryption", value: ""),
|
||||
RequiredState(key: "m.room.member", value: "$ME"),
|
||||
RequiredState(key: "m.room.canonical_alias", value: "")]
|
||||
|
||||
private lazy var slidingSyncFilters = SlidingSyncRequestListFilters(isDm: nil,
|
||||
spaces: [],
|
||||
isEncrypted: nil,
|
||||
isInvite: false,
|
||||
isTombstoned: false,
|
||||
roomTypes: [],
|
||||
notRoomTypes: ["m.space"],
|
||||
roomNameLike: nil,
|
||||
tags: [],
|
||||
notTags: [])
|
||||
|
||||
private lazy var slidingSyncInviteFilters = SlidingSyncRequestListFilters(isDm: nil,
|
||||
spaces: [],
|
||||
isEncrypted: nil,
|
||||
isInvite: true,
|
||||
isTombstoned: false,
|
||||
roomTypes: [],
|
||||
notRoomTypes: ["m.space"],
|
||||
roomNameLike: nil,
|
||||
tags: [],
|
||||
notTags: [])
|
||||
|
||||
private func configureViewsPostInitialSync() {
|
||||
if let visibleRoomsListProxy {
|
||||
MXLog.info("Setting visible rooms view timeline limit to \(SlidingSyncConstants.lastMessageTimelineLimit)")
|
||||
visibleRoomsListProxy.updateVisibleRange(nil, timelineLimit: SlidingSyncConstants.lastMessageTimelineLimit)
|
||||
} else {
|
||||
MXLog.error("Visible rooms sliding sync view unavailable")
|
||||
}
|
||||
|
||||
if let allRoomsListBuilder {
|
||||
MXLog.info("Registering all rooms view")
|
||||
slidingSyncTasks.append(slidingSync?.addList(listBuilder: allRoomsListBuilder))
|
||||
} else {
|
||||
MXLog.error("All rooms sliding sync view unavailable")
|
||||
}
|
||||
|
||||
if let invitesListBuilder {
|
||||
MXLog.info("Registering invites view")
|
||||
slidingSyncTasks.append(slidingSync?.addList(listBuilder: invitesListBuilder))
|
||||
} else {
|
||||
MXLog.error("Invites sliding sync view unavailable")
|
||||
name: "AllRooms")
|
||||
|
||||
await roomSummaryProvider?.subscribeIfNecessary(entriesFunction: roomListService.entries(listener:),
|
||||
entriesLoadingStateFunction: roomListService.entriesLoadingState(listener:))
|
||||
|
||||
inviteSummaryProvider = RoomSummaryProvider(roomListService: roomListService,
|
||||
eventStringBuilder: RoomEventStringBuilder(stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)),
|
||||
name: "Invites")
|
||||
|
||||
self.roomListService = roomListService
|
||||
} catch {
|
||||
MXLog.error("Failed building room list service with error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func roomTupleForIdentifier(_ identifier: String) -> (SlidingSyncRoom?, Room?) {
|
||||
private func roomTupleForIdentifier(_ identifier: String) -> (RoomListItem?, Room?) {
|
||||
do {
|
||||
let slidingSyncRoom = try slidingSync?.getRoom(roomId: identifier)
|
||||
let fullRoom = slidingSyncRoom?.fullRoom()
|
||||
|
||||
return (slidingSyncRoom, fullRoom)
|
||||
let roomListItem = try roomListService?.room(roomId: identifier)
|
||||
let fullRoom = roomListItem?.fullRoom()
|
||||
|
||||
return (roomListItem, fullRoom)
|
||||
} catch {
|
||||
MXLog.error("Failed retrieving room with identifier: \(identifier)")
|
||||
return (nil, nil)
|
||||
@@ -611,10 +443,6 @@ class ClientProxy: ClientProxyProtocol {
|
||||
fileprivate func didReceiveAuthError(isSoftLogout: Bool) {
|
||||
callbacks.send(.receivedAuthError(isSoftLogout: isSoftLogout))
|
||||
}
|
||||
|
||||
fileprivate func didReceiveSlidingSyncUpdate(summary: UpdateSummary) {
|
||||
callbacks.send(.receivedSyncUpdate)
|
||||
}
|
||||
}
|
||||
|
||||
extension ClientProxy: MediaLoaderProtocol {
|
||||
@@ -630,3 +458,15 @@ extension ClientProxy: MediaLoaderProtocol {
|
||||
try await mediaLoader.loadMediaFileForSource(source, body: body)
|
||||
}
|
||||
}
|
||||
|
||||
private class RoomListStateListenerProxy: RoomListStateListener {
|
||||
private let onUpdateClosure: (RoomListState) -> Void
|
||||
|
||||
init(_ onUpdateClosure: @escaping (RoomListState) -> Void) {
|
||||
self.onUpdateClosure = onUpdateClosure
|
||||
}
|
||||
|
||||
func onUpdate(state: RoomListState) {
|
||||
onUpdateClosure(state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,9 +47,7 @@ enum ClientProxyError: Error {
|
||||
}
|
||||
|
||||
enum SlidingSyncConstants {
|
||||
static let initialTimelineLimit: UInt = 0
|
||||
static let lastMessageTimelineLimit: UInt = 1
|
||||
static let timelinePrecachingTimelineLimit: UInt = 20
|
||||
static let defaultTimelineLimit: UInt = 20
|
||||
}
|
||||
|
||||
/// This struct represents the configuration that we are using to register the application through Pusher to Sygnal
|
||||
@@ -77,11 +75,9 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
|
||||
|
||||
var restorationToken: RestorationToken? { get }
|
||||
|
||||
var visibleRoomsSummaryProvider: RoomSummaryProviderProtocol? { get }
|
||||
var roomSummaryProvider: RoomSummaryProviderProtocol? { get }
|
||||
|
||||
var allRoomsSummaryProvider: RoomSummaryProviderProtocol? { get }
|
||||
|
||||
var invitesSummaryProvider: RoomSummaryProviderProtocol? { get }
|
||||
var inviteSummaryProvider: RoomSummaryProviderProtocol? { get }
|
||||
|
||||
var isSyncing: Bool { get }
|
||||
|
||||
|
||||
@@ -26,11 +26,9 @@ class MockClientProxy: ClientProxyProtocol {
|
||||
let homeserver = ""
|
||||
let restorationToken: RestorationToken? = nil
|
||||
|
||||
var visibleRoomsSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()
|
||||
var roomSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()
|
||||
|
||||
var allRoomsSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()
|
||||
|
||||
var invitesSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()
|
||||
var inviteSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()
|
||||
|
||||
var avatarURLPublisher: AnyPublisher<URL?, Never> { Empty().eraseToAnyPublisher() }
|
||||
|
||||
@@ -38,7 +36,7 @@ class MockClientProxy: ClientProxyProtocol {
|
||||
|
||||
internal init(userID: String, roomSummaryProvider: RoomSummaryProviderProtocol? = MockRoomSummaryProvider()) {
|
||||
self.userID = userID
|
||||
visibleRoomsSummaryProvider = roomSummaryProvider
|
||||
self.roomSummaryProvider = roomSummaryProvider
|
||||
}
|
||||
|
||||
func loadUserAvatarURL() async { }
|
||||
@@ -72,14 +70,14 @@ class MockClientProxy: ClientProxyProtocol {
|
||||
return roomForIdentifierMocks[identifier]
|
||||
}
|
||||
|
||||
guard let room = visibleRoomsSummaryProvider?.roomListPublisher.value.first(where: { $0.id == identifier }) else {
|
||||
guard let room = roomSummaryProvider?.roomListPublisher.value.first(where: { $0.id == identifier }) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch room {
|
||||
case .empty:
|
||||
return RoomProxyMock(with: .init(displayName: "Empty room"))
|
||||
case .filled(let details), .invalidated(let details):
|
||||
case .filled(let details):
|
||||
return RoomProxyMock(with: .init(displayName: details.name))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
private class SlidingSyncListObserver: SlidingSyncListRoomListObserver, SlidingSyncListStateObserver, SlidingSyncListRoomsCountObserver {
|
||||
/// Publishes room list diffs as they come in through sliding sync
|
||||
let roomListDiffPublisher = PassthroughSubject<SlidingSyncListRoomsListDiff, Never>()
|
||||
|
||||
/// Publishes the current state of sliding sync, such as whether its catching up or live.
|
||||
let stateUpdatePublisher = CurrentValueSubject<SlidingSyncState, Never>(.notLoaded)
|
||||
|
||||
/// Publishes the number of available rooms
|
||||
let countUpdatePublisher = CurrentValueSubject<UInt, Never>(0)
|
||||
|
||||
private let name: String
|
||||
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
|
||||
// MARK: - SlidingSyncListRoomListObserver
|
||||
|
||||
func didReceiveUpdate(diff: SlidingSyncListRoomsListDiff) {
|
||||
MXLog.verbose("\(name): Received room diff")
|
||||
roomListDiffPublisher.send(diff)
|
||||
}
|
||||
|
||||
// MARK: - SlidingSyncListStateObserver
|
||||
|
||||
func didReceiveUpdate(newState: SlidingSyncState) {
|
||||
MXLog.info("\(name): Updated state: \(newState)")
|
||||
stateUpdatePublisher.send(newState)
|
||||
}
|
||||
|
||||
// MARK: - SlidingSyncListRoomsCountObserver
|
||||
|
||||
func didReceiveUpdate(count: UInt32) {
|
||||
MXLog.info("\(name): Updated room count: \(count)")
|
||||
countUpdatePublisher.send(UInt(count))
|
||||
}
|
||||
}
|
||||
|
||||
class SlidingSyncListProxy: SlidingSyncListOnceBuilt {
|
||||
let name: String
|
||||
private var slidingSync: SlidingSyncProtocol?
|
||||
private var slidingSyncList: SlidingSyncListProtocol?
|
||||
|
||||
private var listUpdateObserverToken: TaskHandle?
|
||||
private var stateUpdateObserverToken: TaskHandle?
|
||||
private var countUpdateObserverToken: TaskHandle?
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
let diffPublisher = PassthroughSubject<SlidingSyncListRoomsListDiff, Never>()
|
||||
let statePublisher = PassthroughSubject<SlidingSyncState, Never>()
|
||||
let countPublisher = PassthroughSubject<UInt, Never>()
|
||||
|
||||
deinit {
|
||||
listUpdateObserverToken?.cancel()
|
||||
stateUpdateObserverToken?.cancel()
|
||||
countUpdateObserverToken?.cancel()
|
||||
}
|
||||
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
|
||||
func setSlidingSync(slidingSync: SlidingSyncProtocol) {
|
||||
self.slidingSync = slidingSync
|
||||
}
|
||||
|
||||
func currentRoomsList() -> [RoomListEntry] {
|
||||
guard let slidingSyncList else {
|
||||
return []
|
||||
}
|
||||
|
||||
return slidingSyncList.currentRoomList()
|
||||
}
|
||||
|
||||
func roomForIdentifier(_ identifier: String) throws -> SlidingSyncRoomProtocol? {
|
||||
guard let slidingSync else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return try slidingSync.getRoom(roomId: identifier)
|
||||
}
|
||||
|
||||
func updateVisibleRange(_ range: Range<Int>?, timelineLimit: UInt?) {
|
||||
if let range {
|
||||
MXLog.info("Setting '\(name)' list range to \(range)")
|
||||
slidingSyncList?.setSyncMode(builder: .init().addRange(start: UInt32(range.lowerBound), endInclusive: UInt32(range.upperBound)))
|
||||
}
|
||||
|
||||
if let timelineLimit {
|
||||
MXLog.info("Setting '\(name)' list timeline limit to \(timelineLimit)")
|
||||
slidingSyncList?.setTimelineLimit(value: UInt32(timelineLimit))
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user