From 22aa1e3f667d0db5f90264c21ff7ce67950cb0e1 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Wed, 8 Apr 2026 14:59:08 +0200 Subject: [PATCH] fix: batch participants + isLiveKitConnected on disconnect to prevent double sound MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RoomEvent.Disconnected handler set participants=[] and isLiveKitConnected=false in separate setState calls. SoundController subscription fired between them — saw empty participants while still "connected" → played user_leave, then saw disconnected → played disconnect. Both sounds played simultaneously. Batching into one setState ensures SoundController sees the final state atomically: participants gone AND disconnected in one update. --- packages/web/src/hooks/useLiveKit.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/web/src/hooks/useLiveKit.ts b/packages/web/src/hooks/useLiveKit.ts index 1dedede4..38ec5d7e 100644 --- a/packages/web/src/hooks/useLiveKit.ts +++ b/packages/web/src/hooks/useLiveKit.ts @@ -561,9 +561,11 @@ export function useLiveKit() { setConnectionState(ConnectionState.Disconnected); setConnectedChannelId(null); roomRef.current = null; _activeRoom = null; setIsConnected(false); setRoom(null); - useVoiceStore.getState().setParticipants([]); + // Batch participants + connected into one setState to prevent SoundController + // from seeing intermediate states (e.g., participants empty but still "connected" + // → triggers user_leave sound before the disconnect sound). useVoiceStore.getState().setSpeakingParticipants(new Set()); - useVoiceStore.getState().setIsLiveKitConnected(false); + useVoiceStore.setState({ participants: [], isLiveKitConnected: false }); // Non-client disconnect (identity collision, server shutdown, kicked, etc.) // → clear voice intent so AppLayout doesn't auto-retry into an infinite loop. @@ -626,9 +628,8 @@ export function useLiveKit() { setIsConnected(false); setIsConnecting(false); setConnectionState(ConnectionState.Disconnected); - useVoiceStore.getState().setParticipants([]); useVoiceStore.getState().setSpeakingParticipants(new Set()); - useVoiceStore.getState().setIsLiveKitConnected(false); + useVoiceStore.setState({ participants: [], isLiveKitConnected: false }); } }, []);