fix: batch participants + isLiveKitConnected on disconnect to prevent double sound

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.
This commit is contained in:
Jannis Braun
2026-04-08 14:59:08 +02:00
parent 5b1c847e57
commit 22aa1e3f66
+5 -4
View File
@@ -561,9 +561,11 @@ export function useLiveKit() {
setConnectionState(ConnectionState.Disconnected); setConnectionState(ConnectionState.Disconnected);
setConnectedChannelId(null); setConnectedChannelId(null);
roomRef.current = null; _activeRoom = null; setIsConnected(false); setRoom(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().setSpeakingParticipants(new Set());
useVoiceStore.getState().setIsLiveKitConnected(false); useVoiceStore.setState({ participants: [], isLiveKitConnected: false });
// Non-client disconnect (identity collision, server shutdown, kicked, etc.) // Non-client disconnect (identity collision, server shutdown, kicked, etc.)
// → clear voice intent so AppLayout doesn't auto-retry into an infinite loop. // → clear voice intent so AppLayout doesn't auto-retry into an infinite loop.
@@ -626,9 +628,8 @@ export function useLiveKit() {
setIsConnected(false); setIsConnected(false);
setIsConnecting(false); setIsConnecting(false);
setConnectionState(ConnectionState.Disconnected); setConnectionState(ConnectionState.Disconnected);
useVoiceStore.getState().setParticipants([]);
useVoiceStore.getState().setSpeakingParticipants(new Set()); useVoiceStore.getState().setSpeakingParticipants(new Set());
useVoiceStore.getState().setIsLiveKitConnected(false); useVoiceStore.setState({ participants: [], isLiveKitConnected: false });
} }
}, []); }, []);