From a1a7c2ff18413e83318403b07056eb3d314da74d Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:39:21 +0200 Subject: [PATCH] fix: null roomRef before destroyRoom to suppress teardown sounds During room.disconnect(), LiveKit fires ParticipantDisconnected for each remote participant BEFORE the final Disconnected event. Because roomRef was still set, guardedUpdate() called updateParticipants(), which updated the voiceStore while isLiveKitConnected was still true. SoundController played user_leave for each departing participant alongside the disconnect sound. Fix: set roomRef.current = null before calling destroyRoom(). This causes guardedUpdate() to return early for all teardown events. The disconnect function handles cleanup after destroyRoom resolves. --- packages/web/src/hooks/useLiveKit.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/web/src/hooks/useLiveKit.ts b/packages/web/src/hooks/useLiveKit.ts index 38ec5d7e..91fed92d 100644 --- a/packages/web/src/hooks/useLiveKit.ts +++ b/packages/web/src/hooks/useLiveKit.ts @@ -621,9 +621,15 @@ export function useLiveKit() { connectedChannelRef.current = null; setConnectedChannelId(null); if (roomRef.current) { - await destroyRoom(roomRef.current); + // Null out roomRef BEFORE destroying so that guardedUpdate() skips + // during teardown. Without this, ParticipantDisconnected events fire + // before RoomEvent.Disconnected, calling updateParticipants while + // isLiveKitConnected is still true — SoundController plays user_leave + // for departing participants alongside the disconnect sound. + const roomToDestroy = roomRef.current; roomRef.current = null; _activeRoom = null; + await destroyRoom(roomToDestroy); setRoom(null); setIsConnected(false); setIsConnecting(false);