diff --git a/packages/web/src/hooks/useLiveKit.ts b/packages/web/src/hooks/useLiveKit.ts index 75725d57..9be26d4a 100644 --- a/packages/web/src/hooks/useLiveKit.ts +++ b/packages/web/src/hooks/useLiveKit.ts @@ -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(); } } @@ -453,10 +467,13 @@ export function useLiveKit() { _activeRoom = newRoom; connectedChannelRef.current = storedId; setConnectedChannelId(storedId); - setRoom(newRoom); + setRoom(newRoom); 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) diff --git a/packages/web/src/hooks/useWebSocket.ts b/packages/web/src/hooks/useWebSocket.ts index 1887f73f..37b7f504 100644 --- a/packages/web/src/hooks/useWebSocket.ts +++ b/packages/web/src/hooks/useWebSocket.ts @@ -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); } diff --git a/packages/web/src/utils/voice.ts b/packages/web/src/utils/voice.ts index 5fbd8e2d..51c1b6b9 100644 --- a/packages/web/src/utils/voice.ts +++ b/packages/web/src/utils/voice.ts @@ -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); }