import React, { ReactNode, useCallback, useEffect, useRef } from 'react'; import { useAtomValue, useSetAtom, useStore } from 'jotai'; import { CallEmbedContextProvider, CallEmbedRefContextProvider, useCallCloseEvent, useCallHangupEvent, useCallJoined, useCallThemeSync, useCallMemberSoundSync, } from '../hooks/useCallEmbed'; import { callChatAtom, callEmbedAtom } from '../state/callEmbed'; import { CallEmbed } from '../plugins/call'; import { useSelectedRoom } from '../hooks/router/useSelectedRoom'; import { ScreenSize, useScreenSizeContext } from '../hooks/useScreenSize'; import { useAndroidCallForegroundSync } from '../hooks/useAndroidCallForegroundSync'; function CallUtils({ embed }: { embed: CallEmbed }) { const setCallEmbed = useSetAtom(callEmbedAtom); const store = useStore(); useCallMemberSoundSync(embed); useCallThemeSync(embed); const clearIfCurrent = useCallback(() => { if (store.get(callEmbedAtom) !== embed) return; setCallEmbed(undefined); }, [store, embed, setCallEmbed]); useCallHangupEvent(embed, clearIfCurrent); // EC emits Close (not Hangup) when LiveKit gives up without explicit user // action — e.g. reconnect fail after a network blip. Without this listener // the embed lingers as a zombie and the Android FGS stays up. useCallCloseEvent(embed, clearIfCurrent); // Widget failed to prepare (bad network, iframe load error). Clear the atom // so callEmbedAtom's setter can dispose the embed and the Android FGS // (keyed on the atom) stops. useEffect(() => { const unsubscribe = embed.onPreparingError(clearIfCurrent); return unsubscribe; }, [embed, clearIfCurrent]); return null; } type CallEmbedProviderProps = { children?: ReactNode; }; export function CallEmbedProvider({ children }: CallEmbedProviderProps) { const callEmbed = useAtomValue(callEmbedAtom); const callEmbedRef = useRef(null); const joined = useCallJoined(callEmbed); useAndroidCallForegroundSync(); const selectedRoom = useSelectedRoom(); const chat = useAtomValue(callChatAtom); const screenSize = useScreenSizeContext(); const chatOnlyView = chat && screenSize !== ScreenSize.Desktop; const callVisible = callEmbed && !callEmbed.voiceOnly && selectedRoom === callEmbed.roomId && joined && !chatOnlyView; return ( {callEmbed && } {children}
); }