87 lines
2.9 KiB
TypeScript
87 lines
2.9 KiB
TypeScript
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<HTMLDivElement>(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 (
|
|
<CallEmbedContextProvider value={callEmbed}>
|
|
{callEmbed && <CallUtils embed={callEmbed} />}
|
|
<CallEmbedRefContextProvider value={callEmbedRef}>{children}</CallEmbedRefContextProvider>
|
|
<div
|
|
data-call-embed-container
|
|
style={{
|
|
visibility: callVisible ? undefined : 'hidden',
|
|
pointerEvents: callVisible ? undefined : 'none',
|
|
position: 'fixed',
|
|
top: 0,
|
|
left: 0,
|
|
width: '100%',
|
|
height: '50%',
|
|
}}
|
|
ref={callEmbedRef}
|
|
/>
|
|
</CallEmbedContextProvider>
|
|
);
|
|
}
|