diff --git a/.changes/room-moved b/.changes/room-moved new file mode 100644 index 000000000..36a73c204 --- /dev/null +++ b/.changes/room-moved @@ -0,0 +1 @@ +patch type="added" "Handle Room moved event" \ No newline at end of file diff --git a/Sources/LiveKit/Core/Room+SignalClientDelegate.swift b/Sources/LiveKit/Core/Room+SignalClientDelegate.swift index 3298e72c4..da32dfda8 100644 --- a/Sources/LiveKit/Core/Room+SignalClientDelegate.swift +++ b/Sources/LiveKit/Core/Room+SignalClientDelegate.swift @@ -135,6 +135,46 @@ extension Room: SignalClientDelegate { } } + func signalClient(_: SignalClient, didReceiveRoomMoved response: Livekit_RoomMovedResponse) async { + log("didReceiveRoomMoved to room: \(response.hasRoom ? response.room.name : "unknown")") + + // Update room info if available + if response.hasRoom { + _state.mutate { + $0.metadata = response.room.metadata + $0.isRecording = response.room.activeRecording + $0.numParticipants = Int(response.room.numParticipants) + $0.numPublishers = Int(response.room.numPublishers) + } + } + + // Disconnect all remote participants + let participantsToDisconnect = Array(_state.remoteParticipants.values) + for participant in participantsToDisconnect { + guard let identity = participant.identity else { continue } + await participant.unpublishAll(notify: false) + _state.mutate { $0.remoteParticipants.removeValue(forKey: identity) } + } + + // Emit room moved event with new room name + if response.hasRoom { + delegates.notify(label: { "room.didMoveToRoomNamed \(response.room.name)" }) { + $0.room?(self, didMoveToRoomNamed: response.room.name) + } + } + + // Re-add participants + var participantsToAdd: [Livekit_ParticipantInfo] = [] + if response.hasParticipant { + participantsToAdd.append(response.participant) + } + participantsToAdd.append(contentsOf: response.otherParticipants) + + for info in participantsToAdd { + _state.mutate { $0.updateRemoteParticipant(info: info, room: self) } + } + } + func signalClient(_: SignalClient, didUpdateSpeakers speakers: [Livekit_SpeakerInfo]) async { let activeSpeakers = _state.mutate { state -> [Participant] in var lastSpeakers = state.activeSpeakers.reduce(into: [Sid: Participant]()) { $0[$1.sid] = $1 } diff --git a/Sources/LiveKit/Core/SignalClient.swift b/Sources/LiveKit/Core/SignalClient.swift index 5df326a08..9befbb389 100644 --- a/Sources/LiveKit/Core/SignalClient.swift +++ b/Sources/LiveKit/Core/SignalClient.swift @@ -297,6 +297,9 @@ private extension SignalClient { case let .roomUpdate(update): _delegate.notifyDetached { await $0.signalClient(self, didUpdateRoom: update.room) } + case let .roomMoved(response): + _delegate.notifyDetached { await $0.signalClient(self, didReceiveRoomMoved: response) } + case let .trackPublished(trackPublished): log("[publish] resolving completer for cid: \(trackPublished.cid)") // Complete diff --git a/Sources/LiveKit/Protocols/RoomDelegate.swift b/Sources/LiveKit/Protocols/RoomDelegate.swift index c276f746d..d0400dfd6 100644 --- a/Sources/LiveKit/Protocols/RoomDelegate.swift +++ b/Sources/LiveKit/Protocols/RoomDelegate.swift @@ -87,6 +87,10 @@ public protocol RoomDelegate: AnyObject, Sendable { @objc optional func room(_ room: Room, didUpdateIsRecording isRecording: Bool) + /// Room was moved to a different server. + @objc optional + func room(_ room: Room, didMoveToRoomNamed roomName: String) + // MARK: - Participant Management /// A ``RemoteParticipant`` joined the room. diff --git a/Sources/LiveKit/Protocols/SignalClientDelegate.swift b/Sources/LiveKit/Protocols/SignalClientDelegate.swift index bff50161b..93b22214d 100644 --- a/Sources/LiveKit/Protocols/SignalClientDelegate.swift +++ b/Sources/LiveKit/Protocols/SignalClientDelegate.swift @@ -32,6 +32,7 @@ protocol SignalClientDelegate: AnyObject, Sendable { func signalClient(_ signalClient: SignalClient, didUpdateRemoteMute trackSid: Track.Sid, muted: Bool) async func signalClient(_ signalClient: SignalClient, didUpdateTrackStreamStates streamStates: [Livekit_StreamStateInfo]) async func signalClient(_ signalClient: SignalClient, didUpdateSubscribedCodecs codecs: [Livekit_SubscribedCodec], qualities: [Livekit_SubscribedQuality], forTrackSid sid: String) async + func signalClient(_ signalClient: SignalClient, didReceiveRoomMoved response: Livekit_RoomMovedResponse) async func signalClient(_ signalClient: SignalClient, didUpdateSubscriptionPermission permission: Livekit_SubscriptionPermissionUpdate) async func signalClient(_ signalClient: SignalClient, didUpdateToken token: String) async func signalClient(_ signalClient: SignalClient, didReceiveLeave canReconnect: Bool, reason: Livekit_DisconnectReason) async