fix: resync voice state after LiveKit reconnect

This commit is contained in:
2026-06-01 11:30:30 +05:30
parent 1dd0270f4f
commit 1f5a219734
2 changed files with 40 additions and 1 deletions
+27
View File
@@ -92,6 +92,27 @@ const syncParticipantMedia = (participant: Participant) => {
})
}
// LiveKit does not emit ParticipantConnected/Disconnected during reconnect.
const resyncAfterReconnect = (room: LiveKitRoom) => {
const next = new Map<string, {muted: boolean; cameraOn: boolean}>()
for (const p of [room.localParticipant, ...room.remoteParticipants.values()]) {
next.set(p.identity, {muted: !p.isMicrophoneEnabled, cameraOn: p.isCameraEnabled})
}
participantMediaState.set(next)
const session = get(currentVoiceSession)
if (session?.room !== room) return
const {localParticipant} = room
voiceMicMuted.set(!localParticipant.isMicrophoneEnabled)
currentVoiceSession.set({
...session,
cameraOn: localParticipant.isCameraEnabled,
screenShareOn: localParticipant.isScreenShareEnabled,
})
triggerVideoFeedCount()
}
const onParticipantMediaChanged = (_publication: TrackPublication, participant: Participant) => {
syncParticipantMedia(participant)
}
@@ -191,6 +212,11 @@ const setUpMicrophone = async (
// (after switching calls or an engine reconnect give-up) must not clobber it.
let activeRoom: LiveKitRoom | undefined
const makeOnRoomReconnected = (room: LiveKitRoom) => () => {
if (room !== activeRoom) return
resyncAfterReconnect(room)
}
const makeOnRoomDisconnected = (room: LiveKitRoom) => (reason?: DisconnectReason) => {
// Ignore disconnects from rooms that are no longer the active session.
if (room !== activeRoom) return
@@ -320,6 +346,7 @@ export const joinVoiceRoom = async (
activeRoom = liveKitRoom
liveKitRoom.on(RoomEvent.Disconnected, makeOnRoomDisconnected(liveKitRoom))
liveKitRoom.on(RoomEvent.Reconnected, makeOnRoomReconnected(liveKitRoom))
liveKitRoom.on(RoomEvent.ParticipantConnected, onParticipantConnected)
liveKitRoom.on(RoomEvent.ParticipantDisconnected, onParticipantDisconnected)
liveKitRoom.on(RoomEvent.TrackSubscribed, onTrackSubscribed)
+13 -1
View File
@@ -44,7 +44,7 @@
voiceMicMuted,
voiceState,
} from "@app/call/stores"
import {cancelJoinVoiceRoom, leaveVoiceRoom, toggleMute} from "@app/call/voice"
import {cancelJoinVoiceRoom, joinVoiceRoom, leaveVoiceRoom, toggleMute} from "@app/call/voice"
const {relay, h} = $derived($page.params)
const url = $derived(relay ? decodeRelay(relay) : undefined)
@@ -86,6 +86,11 @@
pushModal(VoiceRoomJoinDialog, {url: targetRoom.url, h: targetRoom.h})
}
const onReconnect = () => {
if (!targetRoom) return
void joinVoiceRoom(targetRoom.url, targetRoom.h)
}
const goToRoom = () => {
if (!targetRoom) return
const path = makeRoomPath(targetRoom.url, targetRoom.h)
@@ -237,6 +242,13 @@
onclick={leaveVoiceRoom}>
<Icon icon={PhoneRounded} size={4} />
</Button>
{:else if $currentVoiceRoom}
<Button
data-tip="Reconnect"
class="center tooltip tooltip-top btn btn-sm btn-square btn-success"
onclick={onReconnect}>
<Icon icon={PhoneCallingRounded} size={4} />
</Button>
{:else}
<Button
data-tip="Join Voice"