import { useEffect } from 'react'; import { useAtomValue, useSetAtom } from 'jotai'; import { App } from '@capacitor/app'; import { pendingCallActionAtom } from '../state/pendingCallAction'; import { incomingCallsAtom } from '../state/incomingCalls'; import { useMatrixClient } from './useMatrixClient'; import { isNativePlatform } from '../utils/capacitor'; import { useSwitchOrStartDmCall } from './useSwitchOrStartDmCall'; // Consumes pending call actions emitted by the native Android push-action // listener (see usePushNotifications.ts). Must be mounted inside CallEmbedProvider // so useSwitchOrStartDmCall can reach the embed atom. export const usePendingCallActionConsumer = (): void => { const pending = useAtomValue(pendingCallActionAtom); const setPending = useSetAtom(pendingCallActionAtom); const setIncoming = useSetAtom(incomingCallsAtom); const switchOrStartDmCall = useSwitchOrStartDmCall(); const mx = useMatrixClient(); useEffect(() => { if (!pending) return; if (pending.kind === 'answer') { const { roomId, notifEventId } = pending; setPending(undefined); switchOrStartDmCall(roomId) .then(() => { if (notifEventId) { setIncoming({ type: 'REMOVE_BY_NOTIF_ID', notifEventId }); return; } setIncoming({ type: 'REMOVE_BY_ROOM', roomId }); }) .catch((err: unknown) => { // eslint-disable-next-line no-console console.warn('[call] native answer switch/start failed', err); }); return; } const { roomId, notifEventId } = pending; setPending(undefined); // Unreachable in practice: every Decline button press fires // CallDeclineReceiver via PendingIntent.getBroadcast, // so MainActivity never boots and `pushNotificationActionPerformed` never // fires with call_action='decline'. Nothing else queues a decline onto // pendingCallActionAtom. Kept as a safety-net in case a future JS-path // (in-app banner decline, retry flow, etc.) starts emitting here. setIncoming({ type: 'REMOVE_BY_NOTIF_ID', notifEventId }); // Fire-and-minimize: dispatch the decline then minimize the app once the // request settles (success OR failure). Minimizing before sendRtcDecline // resolves risks the WebView getting paused mid-request on slower devices; // waiting for settlement gives the network call the tick it needs. const minimize = () => { if (!isNativePlatform()) return; App.minimizeApp().catch(() => { /* minimize not supported / already in background */ }); }; mx.sendRtcDecline(roomId, notifEventId).then( () => minimize(), (err: unknown) => { // eslint-disable-next-line no-console console.warn('[call] sendRtcDecline (from push action) failed', err); minimize(); } ); }, [pending, setPending, setIncoming, switchOrStartDmCall, mx]); };