/** * Bridge zwischen CallKit/ConnectionService-Events und unserer Call-Store. * * Wenn der User in der nativen Call-UI Accept/Reject/Hangup tippt, kommt das * NICHT über unser React-UI rein — sondern via RNCallKeep-Events. Wir * übersetzen die in store-Actions. * * Wird einmal app-weit im _layout.tsx aufgerufen. */ import { useEffect } from 'react'; import { useRouter } from 'expo-router'; import RNCallKeep from 'react-native-callkeep'; import { useCallStore } from '../stores/call'; import { setupCallKeep } from '../lib/callkit'; export function useCallKeepEvents() { const router = useRouter(); useEffect(() => { void setupCallKeep(); // User tippt "Annehmen" in der CallKit-/ConnectionService-UI const onAnswer = ({ callUUID }: { callUUID: string }) => { console.log('[callkeep] answer', callUUID); const st = useCallStore.getState(); if (st.status !== 'incoming') return; // Call-Screen öffnen und Accept-Flow triggern. router.push('/call'); void st.acceptCall(); }; // User tippt "Ablehnen" oder "Auflegen" in der nativen UI const onEnd = ({ callUUID }: { callUUID: string }) => { console.log('[callkeep] end', callUUID); const st = useCallStore.getState(); if (st.status === 'idle' || st.status === 'ended') return; if (st.status === 'incoming') { st.declineCall(); } else { st.hangup('ended'); } }; // User mutet/unmutet über die native UI const onMuted = ({ muted }: { muted: boolean; callUUID: string }) => { const st = useCallStore.getState(); if (st.muted !== muted) st.toggleMute(); }; RNCallKeep.addEventListener('answerCall', onAnswer); RNCallKeep.addEventListener('endCall', onEnd); RNCallKeep.addEventListener('didPerformSetMutedCallAction', onMuted); // didActivateAudioSession kommt nach CallKit-Audio-Activation — wir nutzen // das (noch) nicht aktiv, weil WebRTC + InCallManager das selber regeln. return () => { RNCallKeep.removeEventListener('answerCall'); RNCallKeep.removeEventListener('endCall'); RNCallKeep.removeEventListener('didPerformSetMutedCallAction'); }; }, [router]); }