@telnyx/react-voice-commons-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/TelnyxVoiceCommons.podspec +32 -0
- package/ios/CallKitBridge.m +44 -0
- package/ios/CallKitBridge.swift +879 -0
- package/ios/README.md +211 -0
- package/ios/VoicePnBridge.m +31 -0
- package/ios/VoicePnBridge.swift +87 -0
- package/lib/callkit/callkit-coordinator.d.ts +126 -0
- package/lib/callkit/callkit-coordinator.js +728 -0
- package/lib/callkit/callkit.d.ts +49 -0
- package/lib/callkit/callkit.js +262 -0
- package/lib/callkit/index.d.ts +4 -0
- package/lib/callkit/index.js +15 -0
- package/lib/callkit/use-callkit-coordinator.d.ts +21 -0
- package/lib/callkit/use-callkit-coordinator.js +53 -0
- package/lib/callkit/use-callkit.d.ts +28 -0
- package/lib/callkit/use-callkit.js +279 -0
- package/lib/context/TelnyxVoiceContext.d.ts +18 -0
- package/lib/context/TelnyxVoiceContext.js +18 -0
- package/lib/hooks/use-callkit-coordinator.d.ts +13 -0
- package/lib/hooks/use-callkit-coordinator.js +48 -0
- package/lib/hooks/useAppReadyNotifier.d.ts +9 -0
- package/lib/hooks/useAppReadyNotifier.js +25 -0
- package/lib/hooks/useAppStateHandler.d.ts +16 -0
- package/lib/hooks/useAppStateHandler.js +105 -0
- package/lib/index.d.ts +24 -0
- package/lib/index.js +66 -0
- package/lib/internal/CallKitHandler.d.ts +17 -0
- package/lib/internal/CallKitHandler.js +110 -0
- package/lib/internal/callkit-manager.d.ts +69 -0
- package/lib/internal/callkit-manager.js +326 -0
- package/lib/internal/calls/call-state-controller.d.ts +92 -0
- package/lib/internal/calls/call-state-controller.js +294 -0
- package/lib/internal/session/session-manager.d.ts +87 -0
- package/lib/internal/session/session-manager.js +385 -0
- package/lib/internal/user-defaults-helpers.d.ts +10 -0
- package/lib/internal/user-defaults-helpers.js +69 -0
- package/lib/internal/voice-pn-bridge.d.ts +14 -0
- package/lib/internal/voice-pn-bridge.js +5 -0
- package/lib/models/call-state.d.ts +61 -0
- package/lib/models/call-state.js +87 -0
- package/lib/models/call.d.ts +145 -0
- package/lib/models/call.js +372 -0
- package/lib/models/config.d.ts +64 -0
- package/lib/models/config.js +92 -0
- package/lib/models/connection-state.d.ts +34 -0
- package/lib/models/connection-state.js +50 -0
- package/lib/telnyx-voice-app.d.ts +48 -0
- package/lib/telnyx-voice-app.js +486 -0
- package/lib/telnyx-voip-client.d.ts +184 -0
- package/lib/telnyx-voip-client.js +386 -0
- package/package.json +104 -0
- package/src/callkit/callkit-coordinator.ts +846 -0
- package/src/callkit/callkit.ts +322 -0
- package/src/callkit/index.ts +4 -0
- package/src/callkit/use-callkit.ts +345 -0
- package/src/context/TelnyxVoiceContext.tsx +33 -0
- package/src/hooks/use-callkit-coordinator.ts +60 -0
- package/src/hooks/useAppReadyNotifier.ts +25 -0
- package/src/hooks/useAppStateHandler.ts +134 -0
- package/src/index.ts +56 -0
- package/src/internal/CallKitHandler.tsx +149 -0
- package/src/internal/callkit-manager.ts +335 -0
- package/src/internal/calls/call-state-controller.ts +384 -0
- package/src/internal/session/session-manager.ts +467 -0
- package/src/internal/user-defaults-helpers.ts +58 -0
- package/src/internal/voice-pn-bridge.ts +18 -0
- package/src/models/call-state.ts +98 -0
- package/src/models/call.ts +388 -0
- package/src/models/config.ts +125 -0
- package/src/models/connection-state.ts +50 -0
- package/src/telnyx-voice-app.tsx +690 -0
- package/src/telnyx-voip-client.ts +475 -0
- package/src/types/telnyx-sdk.d.ts +79 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { Call } from '@telnyx/react-native-voice-sdk';
|
|
3
|
+
import callKitCoordinator from '../callkit/callkit-coordinator';
|
|
4
|
+
import { TelnyxVoipClient } from '../telnyx-voip-client';
|
|
5
|
+
|
|
6
|
+
export function useCallKitCoordinator() {
|
|
7
|
+
const reportIncomingCall = useCallback(
|
|
8
|
+
async (call: Call, callerName: string, callerNumber: string): Promise<string | null> => {
|
|
9
|
+
return callKitCoordinator.reportIncomingCall(call, callerName, callerNumber);
|
|
10
|
+
},
|
|
11
|
+
[]
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const startOutgoingCall = useCallback(
|
|
15
|
+
async (call: Call, destinationNumber: string, displayName?: string): Promise<string | null> => {
|
|
16
|
+
return callKitCoordinator.startOutgoingCall(call, destinationNumber, displayName);
|
|
17
|
+
},
|
|
18
|
+
[]
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const answerCallFromUI = useCallback(async (call: Call): Promise<boolean> => {
|
|
22
|
+
return callKitCoordinator.answerCallFromUI(call);
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
const endCallFromUI = useCallback(async (call: Call): Promise<boolean> => {
|
|
26
|
+
return callKitCoordinator.endCallFromUI(call);
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
const getCallKitUUID = useCallback((call: Call): string | null => {
|
|
30
|
+
return callKitCoordinator.getCallKitUUID(call);
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
const getWebRTCCall = useCallback((callKitUUID: string): Call | null => {
|
|
34
|
+
return callKitCoordinator.getWebRTCCall(callKitUUID);
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
const linkExistingCallKitCall = useCallback((call: Call, callKitUUID: string): void => {
|
|
38
|
+
callKitCoordinator.linkExistingCallKitCall(call, callKitUUID);
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
const isAvailable = useCallback((): boolean => {
|
|
42
|
+
return callKitCoordinator.isAvailable();
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
const setVoipClient = useCallback((voipClient: TelnyxVoipClient): void => {
|
|
46
|
+
callKitCoordinator.setVoipClient(voipClient);
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
reportIncomingCall,
|
|
51
|
+
startOutgoingCall,
|
|
52
|
+
answerCallFromUI,
|
|
53
|
+
endCallFromUI,
|
|
54
|
+
getCallKitUUID,
|
|
55
|
+
getWebRTCCall,
|
|
56
|
+
linkExistingCallKitCall,
|
|
57
|
+
isAvailable,
|
|
58
|
+
setVoipClient,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hook to notify the native side when React Native is ready
|
|
6
|
+
* Call this hook when your main screen/login screen is visible and ready
|
|
7
|
+
* This is automatically called by TelnyxVoiceApp, but can be used manually if needed
|
|
8
|
+
*
|
|
9
|
+
* Note: With the native VoicePnManager integration, this notification is now
|
|
10
|
+
* handled automatically by the native Android services, so this hook is simplified.
|
|
11
|
+
*/
|
|
12
|
+
export const useAppReadyNotifier = () => {
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// Only notify on Android since this is Android-specific functionality
|
|
15
|
+
if (Platform.OS === 'android') {
|
|
16
|
+
console.log(
|
|
17
|
+
'useAppReadyNotifier: React Native is ready (native services handle FCM automatically)'
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// Note: The VoicePnManager in the native Android code automatically handles
|
|
21
|
+
// push notification state when the Firebase messaging service receives notifications.
|
|
22
|
+
// No JavaScript module communication is needed anymore.
|
|
23
|
+
}
|
|
24
|
+
}, []);
|
|
25
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { AppState, AppStateStatus } from 'react-native';
|
|
3
|
+
import { router } from 'expo-router';
|
|
4
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
5
|
+
import { TelnyxVoipClient } from '../telnyx-voip-client';
|
|
6
|
+
import { TelnyxConnectionState } from '../models/connection-state';
|
|
7
|
+
import { TelnyxCallState, CallStateHelpers } from '../models/call-state';
|
|
8
|
+
|
|
9
|
+
interface UseAppStateHandlerOptions {
|
|
10
|
+
voipClient: TelnyxVoipClient;
|
|
11
|
+
disconnectOnBackground?: boolean;
|
|
12
|
+
navigateToLoginOnDisconnect?: boolean;
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Hook to handle app state changes for VoIP behavior
|
|
18
|
+
* When app goes to background without an active call, disconnect socket and redirect to login
|
|
19
|
+
*/
|
|
20
|
+
export const useAppStateHandler = ({
|
|
21
|
+
voipClient,
|
|
22
|
+
disconnectOnBackground = true,
|
|
23
|
+
navigateToLoginOnDisconnect = true,
|
|
24
|
+
debug = false,
|
|
25
|
+
}: UseAppStateHandlerOptions) => {
|
|
26
|
+
const appState = useRef(AppState.currentState);
|
|
27
|
+
const log = debug ? console.log : () => {};
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const handleAppStateChange = async (nextAppState: AppStateStatus) => {
|
|
31
|
+
log('AppStateHandler: App state changed from', appState.current, 'to', nextAppState);
|
|
32
|
+
|
|
33
|
+
// When app goes to background
|
|
34
|
+
if (appState.current.match(/active/) && nextAppState === 'background') {
|
|
35
|
+
log('AppStateHandler: App went to background, checking for active calls...');
|
|
36
|
+
|
|
37
|
+
const connectionState = voipClient.currentConnectionState;
|
|
38
|
+
if (connectionState !== TelnyxConnectionState.CONNECTED) {
|
|
39
|
+
log('AppStateHandler: Not connected, skipping background handling');
|
|
40
|
+
appState.current = nextAppState;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check if there's an active call (including ringing calls)
|
|
45
|
+
const activeCalls = voipClient.currentCalls;
|
|
46
|
+
const hasActiveCall =
|
|
47
|
+
activeCalls.length > 0 &&
|
|
48
|
+
activeCalls.some(
|
|
49
|
+
(call) =>
|
|
50
|
+
CallStateHelpers.isActive(call.currentState) ||
|
|
51
|
+
call.currentState === TelnyxCallState.RINGING
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
log('AppStateHandler: Active call check:', {
|
|
55
|
+
callCount: activeCalls.length,
|
|
56
|
+
hasActiveCall,
|
|
57
|
+
callStates: activeCalls.map((call) => ({
|
|
58
|
+
callId: call.callId,
|
|
59
|
+
currentState: call.currentState,
|
|
60
|
+
destination: call.destination,
|
|
61
|
+
isIncoming: call.isIncoming,
|
|
62
|
+
})),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (!hasActiveCall && disconnectOnBackground) {
|
|
66
|
+
// Check if there's a push notification call in progress
|
|
67
|
+
const isPushNotificationInProgress = await AsyncStorage.getItem(
|
|
68
|
+
'@push_notification_payload'
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (isPushNotificationInProgress) {
|
|
72
|
+
log('AppStateHandler: Push notification call in progress, keeping socket connected');
|
|
73
|
+
// Wait a bit longer before allowing disconnection to give time for WebRTC call to establish
|
|
74
|
+
setTimeout(async () => {
|
|
75
|
+
const stillInProgress = await AsyncStorage.getItem('@push_notification_payload');
|
|
76
|
+
if (!stillInProgress) {
|
|
77
|
+
log('AppStateHandler: Push notification call completed, now disconnecting socket');
|
|
78
|
+
await voipClient.logout();
|
|
79
|
+
if (navigateToLoginOnDisconnect) {
|
|
80
|
+
router.replace('/');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}, 5000); // Wait 5 seconds
|
|
84
|
+
appState.current = nextAppState;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
log('AppStateHandler: No active call detected, disconnecting socket...');
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
// Disconnect the socket with background reason
|
|
92
|
+
await voipClient.logout();
|
|
93
|
+
|
|
94
|
+
log('AppStateHandler: Socket disconnected successfully');
|
|
95
|
+
|
|
96
|
+
// Navigate to login screen
|
|
97
|
+
if (navigateToLoginOnDisconnect) {
|
|
98
|
+
// Use a small delay to ensure the disconnect completes
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
log('AppStateHandler: Navigating to login screen');
|
|
101
|
+
router.replace('/');
|
|
102
|
+
}, 100);
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error('AppStateHandler: Error during background disconnect:', error);
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
log(
|
|
109
|
+
'AppStateHandler: Active call detected or disconnect disabled, keeping socket connected'
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// When app comes to foreground
|
|
115
|
+
if (appState.current.match(/background/) && nextAppState === 'active') {
|
|
116
|
+
log('AppStateHandler: App came to foreground');
|
|
117
|
+
// User will need to manually login again when returning from background
|
|
118
|
+
// Auto-login only happens for push notification calls
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
appState.current = nextAppState;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const subscription = AppState.addEventListener('change', handleAppStateChange);
|
|
125
|
+
|
|
126
|
+
return () => {
|
|
127
|
+
subscription?.remove();
|
|
128
|
+
};
|
|
129
|
+
}, [voipClient, disconnectOnBackground, navigateToLoginOnDisconnect, log]);
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
currentAppState: appState.current,
|
|
133
|
+
};
|
|
134
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @telnyx/react-voice-commons-sdk
|
|
3
|
+
*
|
|
4
|
+
* A high-level, state-agnostic, drop-in module for the Telnyx React Native SDK.
|
|
5
|
+
*
|
|
6
|
+
* This library provides a simplified interface for integrating Telnyx WebRTC
|
|
7
|
+
* capabilities into React Native applications. It handles session management,
|
|
8
|
+
* call state transitions, push notification processing, and native call UI
|
|
9
|
+
* integration.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Main client
|
|
13
|
+
export {
|
|
14
|
+
TelnyxVoipClient,
|
|
15
|
+
createTelnyxVoipClient,
|
|
16
|
+
createBackgroundTelnyxVoipClient,
|
|
17
|
+
} from './telnyx-voip-client';
|
|
18
|
+
export type { TelnyxVoipClientOptions } from './telnyx-voip-client';
|
|
19
|
+
|
|
20
|
+
// TelnyxVoiceApp component
|
|
21
|
+
export { TelnyxVoiceApp } from './telnyx-voice-app';
|
|
22
|
+
export type { TelnyxVoiceAppOptions, TelnyxVoiceAppProps } from './telnyx-voice-app';
|
|
23
|
+
|
|
24
|
+
// Hooks
|
|
25
|
+
export { useAppStateHandler } from './hooks/useAppStateHandler';
|
|
26
|
+
export { useTelnyxVoice } from './context/TelnyxVoiceContext';
|
|
27
|
+
|
|
28
|
+
// Models
|
|
29
|
+
export { Call } from './models/call';
|
|
30
|
+
export {
|
|
31
|
+
TelnyxConnectionState,
|
|
32
|
+
isTelnyxConnectionState,
|
|
33
|
+
canMakeCalls,
|
|
34
|
+
isConnected,
|
|
35
|
+
isTransitioning,
|
|
36
|
+
} from './models/connection-state';
|
|
37
|
+
export { TelnyxCallState, isTelnyxCallState, CallStateHelpers } from './models/call-state';
|
|
38
|
+
export {
|
|
39
|
+
isCredentialConfig,
|
|
40
|
+
isTokenConfig,
|
|
41
|
+
validateConfig,
|
|
42
|
+
validateCredentialConfig,
|
|
43
|
+
validateTokenConfig,
|
|
44
|
+
createCredentialConfig,
|
|
45
|
+
createTokenConfig,
|
|
46
|
+
} from './models/config';
|
|
47
|
+
export type { Config, CredentialConfig, TokenConfig } from './models/config';
|
|
48
|
+
|
|
49
|
+
// Re-export useful types from the underlying SDK
|
|
50
|
+
export type { Call as TelnyxCall } from '@telnyx/react-native-voice-sdk';
|
|
51
|
+
|
|
52
|
+
// Export CallKit functionality
|
|
53
|
+
export * from './callkit';
|
|
54
|
+
|
|
55
|
+
// Export hooks
|
|
56
|
+
export { useAppReadyNotifier } from './hooks/useAppReadyNotifier';
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
|
+
import { Platform, DeviceEventEmitter } from 'react-native';
|
|
3
|
+
import { useRouter } from 'expo-router';
|
|
4
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
5
|
+
import { useTelnyxVoice } from '../context/TelnyxVoiceContext';
|
|
6
|
+
import { callKitCoordinator } from '../callkit';
|
|
7
|
+
|
|
8
|
+
// Global flag to ensure only one CallKitHandler is active
|
|
9
|
+
let isCallKitHandlerActive = false;
|
|
10
|
+
|
|
11
|
+
interface CallData {
|
|
12
|
+
type: string;
|
|
13
|
+
callUUID: string;
|
|
14
|
+
payload?: any;
|
|
15
|
+
callData?: any;
|
|
16
|
+
action: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface CallKitHandlerProps {
|
|
20
|
+
/** Callback when user needs to login from push notification */
|
|
21
|
+
onLoginRequired?: (pushPayload: any) => void;
|
|
22
|
+
/** Callback when call is answered and user should navigate to dialer */
|
|
23
|
+
onNavigateToDialer?: () => void;
|
|
24
|
+
/** Callback when call ends and user should navigate back */
|
|
25
|
+
onNavigateBack?: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Internal CallKit handler for iOS push notifications
|
|
30
|
+
* This component is automatically included in TelnyxVoiceApp
|
|
31
|
+
*
|
|
32
|
+
* @internal - Users should not use this component directly
|
|
33
|
+
*/
|
|
34
|
+
export const CallKitHandler: React.FC<CallKitHandlerProps> = ({
|
|
35
|
+
onLoginRequired,
|
|
36
|
+
onNavigateToDialer,
|
|
37
|
+
onNavigateBack,
|
|
38
|
+
}) => {
|
|
39
|
+
const router = useRouter();
|
|
40
|
+
const { voipClient } = useTelnyxVoice();
|
|
41
|
+
|
|
42
|
+
// Store active calls by CallKit UUID for coordination
|
|
43
|
+
const activeCallsRef = useRef<Map<string, any>>(new Map());
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (Platform.OS !== 'ios') return;
|
|
47
|
+
|
|
48
|
+
if (isCallKitHandlerActive) {
|
|
49
|
+
console.log('CallKitHandler: Another instance is already active, skipping setup');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
isCallKitHandlerActive = true;
|
|
54
|
+
console.log('CallKitHandler: Setting up DeviceEventEmitter listeners (singleton instance)...');
|
|
55
|
+
|
|
56
|
+
DeviceEventEmitter.removeAllListeners('incomingVoIPCall');
|
|
57
|
+
DeviceEventEmitter.removeAllListeners('callKitAction');
|
|
58
|
+
|
|
59
|
+
const incomingCallListener = DeviceEventEmitter.addListener(
|
|
60
|
+
'incomingVoIPCall',
|
|
61
|
+
async (eventData: CallData) => {
|
|
62
|
+
console.log('🔥 CallKitHandler: Incoming VoIP call received:', eventData);
|
|
63
|
+
if (eventData.action === 'connect_webrtc') {
|
|
64
|
+
await handleIncomingCall(eventData);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const callKitActionListener = DeviceEventEmitter.addListener(
|
|
70
|
+
'callKitAction',
|
|
71
|
+
async (eventData: CallData) => {
|
|
72
|
+
console.log('🔥 CallKitHandler: CallKit action received:', eventData);
|
|
73
|
+
switch (eventData.action) {
|
|
74
|
+
case 'answer':
|
|
75
|
+
await handleAnswerCall(eventData);
|
|
76
|
+
break;
|
|
77
|
+
case 'end':
|
|
78
|
+
await handleEndCall(eventData);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
console.log('CallKitHandler: DeviceEventEmitter listeners set up successfully');
|
|
85
|
+
|
|
86
|
+
return () => {
|
|
87
|
+
console.log('CallKitHandler: Cleaning up DeviceEventEmitter listeners');
|
|
88
|
+
incomingCallListener.remove();
|
|
89
|
+
callKitActionListener.remove();
|
|
90
|
+
isCallKitHandlerActive = false;
|
|
91
|
+
};
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
const handleIncomingCall = async (eventData: CallData) => {
|
|
95
|
+
console.log('CallKitHandler: Handling incoming call', {
|
|
96
|
+
callUUID: eventData.callUUID,
|
|
97
|
+
hasClient: !!voipClient,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Store the push notification payload
|
|
101
|
+
await AsyncStorage.setItem('@push_notification_payload', JSON.stringify(eventData.payload));
|
|
102
|
+
|
|
103
|
+
// Mark this call as being processed
|
|
104
|
+
activeCallsRef.current.set(eventData.callUUID, {
|
|
105
|
+
processing: true,
|
|
106
|
+
timestamp: Date.now(),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Trigger login required callback if provided
|
|
110
|
+
if (onLoginRequired) {
|
|
111
|
+
onLoginRequired(eventData.payload);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const handleAnswerCall = async (eventData: CallData) => {
|
|
116
|
+
console.log('CallKitHandler: User answered call via CallKit', {
|
|
117
|
+
callUUID: eventData.callUUID,
|
|
118
|
+
isTrackedCall: activeCallsRef.current.has(eventData.callUUID),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Navigate to dialer after answering
|
|
122
|
+
if (onNavigateToDialer) {
|
|
123
|
+
onNavigateToDialer();
|
|
124
|
+
} else {
|
|
125
|
+
router.replace('/dialer');
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const handleEndCall = async (eventData: CallData) => {
|
|
130
|
+
console.log('CallKitHandler: User ended call via CallKit', {
|
|
131
|
+
callUUID: eventData.callUUID,
|
|
132
|
+
isTrackedCall: activeCallsRef.current.has(eventData.callUUID),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Clean up our local tracking info
|
|
136
|
+
activeCallsRef.current.delete(eventData.callUUID);
|
|
137
|
+
await AsyncStorage.removeItem('@push_notification_payload');
|
|
138
|
+
|
|
139
|
+
// Navigate back after call ends
|
|
140
|
+
if (onNavigateBack) {
|
|
141
|
+
onNavigateBack();
|
|
142
|
+
} else {
|
|
143
|
+
router.replace('/dialer');
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// This component doesn't render anything, it just handles events
|
|
148
|
+
return null;
|
|
149
|
+
};
|