68 lines
2.9 KiB
TypeScript
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]);
|
|
};
|