diff --git a/Mactrix/Models/LiveRoom.swift b/Mactrix/Models/LiveRoom.swift index f28ca66..4ddceb7 100644 --- a/Mactrix/Models/LiveRoom.swift +++ b/Mactrix/Models/LiveRoom.swift @@ -12,9 +12,7 @@ public final class LiveRoom: Identifiable { @ObservationIgnored private var typingHandle: TaskHandle? - public nonisolated var room: MatrixRustSDK.Room { - sidebarRoom.room - } + public let room: MatrixRustSDK.Room public var roomInfo: MatrixRustSDK.RoomInfo? { sidebarRoom.roomInfo @@ -26,6 +24,7 @@ public final class LiveRoom: Identifiable { public init(sidebarRoom: SidebarRoom) { self.sidebarRoom = sidebarRoom + self.room = sidebarRoom.room startListening() diff --git a/Mactrix/Models/MatrixClient+Listeners.swift b/Mactrix/Models/MatrixClient+Listeners.swift index 53bc71d..204ddb9 100644 --- a/Mactrix/Models/MatrixClient+Listeners.swift +++ b/Mactrix/Models/MatrixClient+Listeners.swift @@ -21,13 +21,25 @@ extension MatrixClient { case let .insert(index, room): self.rooms.insert(SidebarRoom(room: room), at: Int(index)) case let .set(index, room): - self.rooms[Int(index)] = SidebarRoom(room: room) + let existing = self.rooms[Int(index)] + if existing.id == room.id() { + existing.updateRoom(room) + } else { + self.rooms[Int(index)] = SidebarRoom(room: room) + } case let .remove(index): self.rooms.remove(at: Int(index)) case let .truncate(length): self.rooms.removeSubrange(Int(length) ..< self.rooms.count) case let .reset(values: values): - self.rooms = values.map(SidebarRoom.init(room:)) + let existingById = Dictionary(self.rooms.map { ($0.id, $0) }, uniquingKeysWith: { first, _ in first }) + self.rooms = values.map { room in + if let existing = existingById[room.id()] { + existing.updateRoom(room) + return existing + } + return SidebarRoom(room: room) + } } } } diff --git a/Mactrix/Models/SidebarRoom.swift b/Mactrix/Models/SidebarRoom.swift index 2456f31..c3b4b49 100644 --- a/Mactrix/Models/SidebarRoom.swift +++ b/Mactrix/Models/SidebarRoom.swift @@ -5,16 +5,15 @@ import OSLog @MainActor @Observable public final class SidebarRoom: Identifiable { - public let room: MatrixRustSDK.Room + public let id: String + public private(set) var room: MatrixRustSDK.Room public var roomInfo: RoomInfo? @ObservationIgnored private var roomInfoHandle: TaskHandle? - - public nonisolated var id: String { - room.id() - } + @ObservationIgnored private var listenerTask: Task? public init(room: MatrixRustSDK.Room) { + self.id = room.id() self.room = room Task { @@ -28,13 +27,24 @@ public final class SidebarRoom: Identifiable { } } + /// Updates the underlying room reference without replacing this instance. + /// Preserves object identity and loaded roomInfo while ensuring the room + /// object stays current. Re-subscribes to room info updates on the new reference. + public func updateRoom(_ newRoom: MatrixRustSDK.Room) { + assert(id == newRoom.id()) + room = newRoom + listenerTask?.cancel() + roomInfoHandle = nil + listenToRoomInfo() + } + private func listenToRoomInfo() { let listener = AsyncSDKListener() roomInfoHandle = room.subscribeToRoomInfoUpdates(listener: listener) - Task { [weak self] in + listenerTask = Task { [weak self] in for await roomInfo in listener._throttle(for: .milliseconds(500)) { - guard let self else { break } + guard let self, !Task.isCancelled else { break } self.roomInfo = roomInfo } }