vojo/src/app/hooks/usePendingCallActionConsumer.ts

68 lines
2.9 KiB
TypeScript

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]);
};