From 1468b68199b34bf81555c4101548c270ba521caa Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:05:49 +0200 Subject: [PATCH] fix: skip participant leave sounds during self-disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added justDisconnected guard to the participant sound loop. When the user hangs up, isLiveKitConnected transitions to false — but in a separate or same subscription tick, the participants list also empties. Without the guard, SoundController plays user_leave for every departed participant AND the disconnect sound simultaneously. Now: if justDisconnected is true, the entire participant loop is skipped. Only the disconnect sound plays. --- .../src/components/voice/SoundController.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/web/src/components/voice/SoundController.tsx b/packages/web/src/components/voice/SoundController.tsx index b94fef5a..5e0f8689 100644 --- a/packages/web/src/components/voice/SoundController.tsx +++ b/packages/web/src/components/voice/SoundController.tsx @@ -64,21 +64,26 @@ export function SoundController() { prevIsScreenSharing.current = state.isScreenSharing; } - // Disconnect (Self) - if (prevIsConnected.current && !state.isLiveKitConnected) { + // Self-disconnect detection — check BEFORE participant sounds + const justDisconnected = prevIsConnected.current && !state.isLiveKitConnected; + const justConnected = !prevIsConnected.current && state.isLiveKitConnected; + + if (justDisconnected) { audioManager.playSound('disconnect', sfxOpts); } - // Connect (Self) - if (!prevIsConnected.current && state.isLiveKitConnected) { + if (justConnected) { audioManager.playSound('user_join', sfxOpts); } prevIsConnected.current = state.isLiveKitConnected; // Participant Joins/Leaves & Screen Sharing + // CRITICAL: Skip entirely if we just disconnected or are not connected. + // Without this guard, hanging up plays "user_leave" for every participant + // (they "left" from our perspective) simultaneously with the disconnect sound. const currentParticipantIds = new Set(state.participants.map(p => p.userId)); const currentScreenShareUserIds = new Set(state.participants.filter(p => p.isScreenSharing).map(p => p.userId)); - - if (state.isLiveKitConnected) { + + if (state.isLiveKitConnected && !justDisconnected) { // Someone joined voice (Others only) state.participants.forEach(p => { if (!prevParticipantIds.current.has(p.userId) && p.userId !== currentUser?.id) { @@ -107,7 +112,7 @@ export function SoundController() { } }); } - + prevParticipantIds.current = currentParticipantIds; prevScreenShareUserIds.current = currentScreenShareUserIds;