diff --git a/packages/web/src/components/layout/AppLayout.tsx b/packages/web/src/components/layout/AppLayout.tsx index ff4f1998..f148582a 100644 --- a/packages/web/src/components/layout/AppLayout.tsx +++ b/packages/web/src/components/layout/AppLayout.tsx @@ -108,18 +108,24 @@ export function AppLayout() { const channels = useSpaceStore((s) => s.channels); - const currentVoiceChannelId = useVoiceStore((s) => s.currentVoiceChannelId); - const activeDmCall = useVoiceStore((s) => s.activeDmCall); const { connect: connectVoice, disconnect: disconnectVoice, - isConnected: isVoiceConnected, - isConnecting: isVoiceConnecting, - connectedChannelId, } = useLiveKit(); + // Register connect/disconnect refs in voiceStore so click handlers can access them + // without calling useLiveKit() (which would create duplicate Room instances). + useEffect(() => { + useVoiceStore.getState().setConnectFn(connectVoice); + useVoiceStore.getState().setDisconnectFn(disconnectVoice); + return () => { + useVoiceStore.getState().setConnectFn(null); + useVoiceStore.getState().setDisconnectFn(null); + }; + }, [connectVoice, disconnectVoice]); + // Initialize WebSocket - const { isConnected: isWsConnected } = useWebSocket(); + useWebSocket(); // Federation toast notifications for remote instance connection state changes useFederationToasts(); @@ -136,67 +142,6 @@ export function AppLayout() { return () => teardownActivityBridge(); }, []); - // Track the last channel we attempted to connect to, to prevent effect loops - const lastAttemptedRef = React.useRef(null); - - // Manage voice connection - useEffect(() => { - if (isLoading || !user || !isWsConnected) return; - - const manageConnection = async () => { - // Determine what we SHOULD be connected to - const targetChannelId = activeDmCall - ? `dm-${activeDmCall.dmChannelId}` - : currentVoiceChannelId; - - // 1. If we have a target - if (targetChannelId) { - // If we're not connected to the RIGHT place, trigger connect. - // We IGNORE isVoiceConnecting here to allow "interrupting" a connection - // or switching rooms immediately. - if (connectedChannelId !== targetChannelId) { - // Prevent spamming the same connection attempt if React re-renders - if (lastAttemptedRef.current === targetChannelId && isVoiceConnecting) { - return; - } - - console.log(`[AppLayout] Switching/Connecting to: ${targetChannelId}`); - lastAttemptedRef.current = targetChannelId; - - if (activeDmCall) { - await connectVoice(activeDmCall.dmChannelId, true); - } else { - await connectVoice(targetChannelId); - } - } else { - // We are connected to the right place. Reset ref. - lastAttemptedRef.current = null; - } - return; - } - - // 2. No target — ensure disconnected - if (connectedChannelId !== null || isVoiceConnected || isVoiceConnecting) { - console.log('[AppLayout] Leaving voice (no target)'); - lastAttemptedRef.current = null; - await disconnectVoice(); - } - }; - - manageConnection(); - }, [ - currentVoiceChannelId, - activeDmCall, - connectedChannelId, - isVoiceConnected, - isVoiceConnecting, - isWsConnected, - isLoading, - user, - connectVoice, - disconnectVoice - ]); - // Responsive detection useEffect(() => { const checkMobile = () => setIsMobile(window.innerWidth < 768); diff --git a/packages/web/src/components/layout/ChannelSidebar.tsx b/packages/web/src/components/layout/ChannelSidebar.tsx index caba7df2..d6b5f0f1 100644 --- a/packages/web/src/components/layout/ChannelSidebar.tsx +++ b/packages/web/src/components/layout/ChannelSidebar.tsx @@ -403,7 +403,8 @@ export function ChannelSidebar() { navigate(`/channels/${currentSpaceId}/${channelId}`); return; } - joinVoiceChannel(channelId); + const connectFn = useVoiceStore.getState().connectFn; + joinVoiceChannel(channelId, connectFn ?? undefined); navigate(`/channels/${currentSpaceId}/${channelId}`); }; diff --git a/packages/web/src/components/layout/MainContent.tsx b/packages/web/src/components/layout/MainContent.tsx index ce988998..fa42568c 100644 --- a/packages/web/src/components/layout/MainContent.tsx +++ b/packages/web/src/components/layout/MainContent.tsx @@ -274,7 +274,7 @@ export function MainContent() {

No one is currently in this voice channel.