shellphone.app/app/features/phone-calls/hooks/use-call.ts

65 lines
1.8 KiB
TypeScript

import { useCallback, useEffect } from "react";
import type { Call } from "@twilio/voice-sdk";
import { atom, useAtom } from "jotai";
import { notificationDataAtom } from "~/features/core/hooks/use-notifications";
export default function useCall() {
const [, setNotificationData] = useAtom(notificationDataAtom);
const [call, setCall] = useAtom(callAtom);
const endCall = useCallback(
function endCallFn() {
call?.removeListener("cancel", endCall);
call?.removeListener("disconnect", endCall);
call?.disconnect();
setCall(null);
setNotificationData(null);
},
[call, setCall, setNotificationData],
);
const onError = useCallback(
function onErrorFn(error: any) {
call?.removeListener("cancel", endCall);
call?.removeListener("disconnect", endCall);
call?.disconnect();
setCall(null);
setNotificationData(null);
throw error; // TODO: might not get caught by error boundary
},
[call, setCall, endCall, setNotificationData],
);
const eventHandlers = [
["error", onError],
["cancel", endCall],
["disconnect", endCall],
] as const;
for (const [eventName, handler] of eventHandlers) {
// register call event handlers
// one event at a time to only update the handlers that changed
// without resetting the other handlers
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
if (!call) {
return;
}
// if we already have this event handler registered, no need to re-register it
const listeners = call.listeners(eventName);
if (listeners.length > 0 && listeners.every((fn) => fn.toString() === handler.toString())) {
return;
}
call.on(eventName, handler);
return () => {
call.removeListener(eventName, handler);
};
}, [call, setCall, eventName, handler]);
}
return [call, setCall] as const;
}
const callAtom = atom<Call | null>(null);