fix: prevent ghost users in voice sidebar after deploy
Send voice_join to WS server only after LiveKit successfully connects, not immediately on joinVoiceChannel() or WS reconnect. Background tabs can reconnect WebSocket but not WebRTC, causing phantom voice users.
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
LocalTrackPublication,
|
||||
} from 'livekit-client';
|
||||
import { getApiForOrigin, getChannelOrigin, useServerStore } from '../stores/serverStore';
|
||||
import { wsSend } from './useWebSocket';
|
||||
import { useVoiceStore } from '../stores/voiceStore';
|
||||
import { AudioManager } from '../audio/AudioManager';
|
||||
import { SpeakingDetector } from '../audio/SpeakingDetector';
|
||||
@@ -308,6 +309,15 @@ export function useLiveKit() {
|
||||
const connect = useCallback(async (channelId: string, isDm?: boolean) => {
|
||||
const storedId = isDm ? `dm-${channelId}` : channelId;
|
||||
if (connectedChannelRef.current === storedId && roomRef.current?.state === ConnectionState.Connected) return;
|
||||
|
||||
// Register voice state with the WS server after LiveKit connects (not for DM calls)
|
||||
const registerWithServer = () => {
|
||||
if (isDm) return;
|
||||
const origin = getChannelOrigin(channelId);
|
||||
wsSend({ type: 'voice_join', channelId }, origin);
|
||||
const { isMuted: m, isDeafened: d, isCameraOn: c, isScreenSharing: s } = useVoiceStore.getState();
|
||||
wsSend({ type: 'voice_status', isMuted: m, isDeafened: d, isCameraOn: c, isScreenSharing: s }, origin);
|
||||
};
|
||||
const gen = ++_connectGeneration;
|
||||
|
||||
// Ensure AudioContext is created and resumed before tracks arrive
|
||||
@@ -433,6 +443,10 @@ export function useLiveKit() {
|
||||
useVoiceStore.getState().setIsLiveKitConnected(connected);
|
||||
|
||||
if (connected) {
|
||||
// On LiveKit reconnect, re-register with WS server (server may have restarted)
|
||||
if (connectedChannelRef.current) {
|
||||
registerWithServer();
|
||||
}
|
||||
updateParticipants();
|
||||
}
|
||||
}
|
||||
@@ -457,6 +471,9 @@ export function useLiveKit() {
|
||||
setIsConnected(true);
|
||||
useVoiceStore.getState().setIsLiveKitConnected(true);
|
||||
|
||||
// Tell WS server we're in the voice channel now that LiveKit is connected
|
||||
registerWithServer();
|
||||
|
||||
updateParticipants();
|
||||
|
||||
// Subscribe to non-screen-share tracks from existing participants (safety net)
|
||||
|
||||
@@ -162,17 +162,15 @@ function handleEvent(origin: string, event: ServerEvent): void {
|
||||
}
|
||||
}
|
||||
|
||||
// Re-register in voice channel if we're still connected to LiveKit
|
||||
// Origin-aware: re-sends voice_join only if the voice channel belongs to this origin
|
||||
// Re-register in voice channel — DEFERRED to useLiveKit after LiveKit connects.
|
||||
// Just keep currentVoiceChannelId set so AppLayout triggers LiveKit connection.
|
||||
// The voice_join WS message will be sent by useLiveKit on connect/reconnect.
|
||||
{
|
||||
const { currentVoiceChannelId, isMuted: curMuted, isDeafened: curDeafened, isCameraOn: curCamera, isScreenSharing: curScreen } = useVoiceStore.getState();
|
||||
const { currentVoiceChannelId } = useVoiceStore.getState();
|
||||
if (currentVoiceChannelId) {
|
||||
const voiceOrigin = getChannelOrigin(currentVoiceChannelId);
|
||||
if (voiceOrigin === origin) {
|
||||
console.log('[WebSocket] Re-syncing voice status on reconnect:', { currentVoiceChannelId, origin, curMuted, curDeafened, curCamera, curScreen });
|
||||
wsSend({ type: 'voice_join', channelId: currentVoiceChannelId }, origin);
|
||||
wsSend({ type: 'voice_status', isMuted: curMuted, isDeafened: curDeafened, isCameraOn: curCamera, isScreenSharing: curScreen }, origin);
|
||||
// Optimistic: immediately show self in voice channel sidebar
|
||||
// Optimistic: show self in sidebar immediately (local only)
|
||||
const myId = isHome ? event.user.id : useAuthStore.getState().user?.id;
|
||||
if (myId) addVoiceUser(currentVoiceChannelId, myId);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export function joinVoiceChannel(channelId: string): void {
|
||||
}
|
||||
|
||||
setCurrentVoiceChannel(channelId);
|
||||
wsSend({ type: 'voice_join', channelId }, getChannelOrigin(channelId));
|
||||
// voice_join is now sent by useLiveKit after successful LiveKit connection
|
||||
// Optimistic: immediately show self in new channel
|
||||
if (myId) addVoiceUser(channelId, myId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user