@telnyx/react-voice-commons-sdk 0.1.1 → 0.1.2
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/README.md +483 -0
- package/ios/README.md +211 -211
- package/lib/callkit/callkit-coordinator.d.ts +117 -113
- package/lib/callkit/callkit-coordinator.js +727 -681
- package/lib/callkit/callkit.d.ts +41 -41
- package/lib/callkit/callkit.js +242 -252
- package/lib/callkit/index.js +47 -15
- package/lib/callkit/use-callkit-coordinator.d.ts +21 -21
- package/lib/callkit/use-callkit-coordinator.js +53 -53
- package/lib/callkit/use-callkit.d.ts +19 -19
- package/lib/callkit/use-callkit.js +310 -270
- package/lib/context/TelnyxVoiceContext.d.ts +9 -9
- package/lib/context/TelnyxVoiceContext.js +13 -10
- package/lib/hooks/use-callkit-coordinator.d.ts +17 -9
- package/lib/hooks/use-callkit-coordinator.js +50 -45
- package/lib/hooks/useAppReadyNotifier.js +15 -13
- package/lib/hooks/useAppStateHandler.d.ts +11 -6
- package/lib/hooks/useAppStateHandler.js +110 -95
- package/lib/index.d.ts +21 -3
- package/lib/index.js +201 -50
- package/lib/internal/CallKitHandler.d.ts +6 -6
- package/lib/internal/CallKitHandler.js +104 -96
- package/lib/internal/callkit-manager.d.ts +57 -57
- package/lib/internal/callkit-manager.js +316 -299
- package/lib/internal/calls/call-state-controller.d.ts +86 -81
- package/lib/internal/calls/call-state-controller.js +307 -269
- package/lib/internal/session/session-manager.d.ts +75 -75
- package/lib/internal/session/session-manager.js +424 -350
- package/lib/internal/user-defaults-helpers.js +39 -49
- package/lib/internal/voice-pn-bridge.d.ts +11 -11
- package/lib/internal/voice-pn-bridge.js +3 -3
- package/lib/models/call-state.d.ts +44 -44
- package/lib/models/call-state.js +68 -66
- package/lib/models/call.d.ts +133 -133
- package/lib/models/call.js +382 -354
- package/lib/models/config.d.ts +18 -11
- package/lib/models/config.js +35 -37
- package/lib/models/connection-state.d.ts +10 -10
- package/lib/models/connection-state.js +16 -16
- package/lib/telnyx-voice-app.d.ts +28 -28
- package/lib/telnyx-voice-app.js +482 -424
- package/lib/telnyx-voip-client.d.ts +167 -165
- package/lib/telnyx-voip-client.js +390 -383
- package/package.json +104 -104
- package/src/callkit/callkit-coordinator.ts +846 -846
- package/src/callkit/callkit.ts +322 -322
- package/src/callkit/index.ts +4 -4
- package/src/callkit/use-callkit.ts +345 -345
- package/src/context/TelnyxVoiceContext.tsx +33 -33
- package/src/hooks/use-callkit-coordinator.ts +60 -60
- package/src/hooks/useAppReadyNotifier.ts +25 -25
- package/src/hooks/useAppStateHandler.ts +134 -134
- package/src/index.ts +56 -56
- package/src/internal/CallKitHandler.tsx +149 -149
- package/src/internal/callkit-manager.ts +335 -335
- package/src/internal/calls/call-state-controller.ts +384 -384
- package/src/internal/session/session-manager.ts +467 -467
- package/src/internal/user-defaults-helpers.ts +58 -58
- package/src/internal/voice-pn-bridge.ts +18 -18
- package/src/models/call-state.ts +98 -98
- package/src/models/call.ts +388 -388
- package/src/models/config.ts +125 -125
- package/src/models/connection-state.ts +50 -50
- package/src/telnyx-voice-app.tsx +690 -690
- package/src/telnyx-voip-client.ts +539 -539
- package/src/types/telnyx-sdk.d.ts +79 -79
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import React, { createContext, useContext } from 'react';
|
|
2
|
-
import { TelnyxVoipClient } from '../telnyx-voip-client';
|
|
3
|
-
import { TelnyxConnectionState } from '../models/connection-state';
|
|
4
|
-
|
|
5
|
-
interface TelnyxVoiceContextValue {
|
|
6
|
-
voipClient: TelnyxVoipClient;
|
|
7
|
-
// Add methods that CallKitHandler expects (these can be no-ops for now)
|
|
8
|
-
connectionState?: TelnyxConnectionState;
|
|
9
|
-
setConnectionState?: (state: TelnyxConnectionState) => void;
|
|
10
|
-
connect?: (payload: any) => Promise<any>;
|
|
11
|
-
client?: any;
|
|
12
|
-
setClient?: (client: any) => void;
|
|
13
|
-
enableAutoReconnect?: (enabled: boolean) => void;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const TelnyxVoiceContext = createContext<TelnyxVoiceContextValue | null>(null);
|
|
17
|
-
|
|
18
|
-
export const TelnyxVoiceProvider: React.FC<{
|
|
19
|
-
voipClient: TelnyxVoipClient;
|
|
20
|
-
children: React.ReactNode;
|
|
21
|
-
}> = ({ voipClient, children }) => {
|
|
22
|
-
return (
|
|
23
|
-
<TelnyxVoiceContext.Provider value={{ voipClient }}>{children}</TelnyxVoiceContext.Provider>
|
|
24
|
-
);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export const useTelnyxVoice = (): TelnyxVoiceContextValue => {
|
|
28
|
-
const context = useContext(TelnyxVoiceContext);
|
|
29
|
-
if (!context) {
|
|
30
|
-
throw new Error('useTelnyxVoice must be used within a TelnyxVoiceProvider');
|
|
31
|
-
}
|
|
32
|
-
return context;
|
|
33
|
-
};
|
|
1
|
+
import React, { createContext, useContext } from 'react';
|
|
2
|
+
import { TelnyxVoipClient } from '../telnyx-voip-client';
|
|
3
|
+
import { TelnyxConnectionState } from '../models/connection-state';
|
|
4
|
+
|
|
5
|
+
interface TelnyxVoiceContextValue {
|
|
6
|
+
voipClient: TelnyxVoipClient;
|
|
7
|
+
// Add methods that CallKitHandler expects (these can be no-ops for now)
|
|
8
|
+
connectionState?: TelnyxConnectionState;
|
|
9
|
+
setConnectionState?: (state: TelnyxConnectionState) => void;
|
|
10
|
+
connect?: (payload: any) => Promise<any>;
|
|
11
|
+
client?: any;
|
|
12
|
+
setClient?: (client: any) => void;
|
|
13
|
+
enableAutoReconnect?: (enabled: boolean) => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const TelnyxVoiceContext = createContext<TelnyxVoiceContextValue | null>(null);
|
|
17
|
+
|
|
18
|
+
export const TelnyxVoiceProvider: React.FC<{
|
|
19
|
+
voipClient: TelnyxVoipClient;
|
|
20
|
+
children: React.ReactNode;
|
|
21
|
+
}> = ({ voipClient, children }) => {
|
|
22
|
+
return (
|
|
23
|
+
<TelnyxVoiceContext.Provider value={{ voipClient }}>{children}</TelnyxVoiceContext.Provider>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const useTelnyxVoice = (): TelnyxVoiceContextValue => {
|
|
28
|
+
const context = useContext(TelnyxVoiceContext);
|
|
29
|
+
if (!context) {
|
|
30
|
+
throw new Error('useTelnyxVoice must be used within a TelnyxVoiceProvider');
|
|
31
|
+
}
|
|
32
|
+
return context;
|
|
33
|
+
};
|
|
@@ -1,60 +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
|
-
}
|
|
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
|
+
}
|
|
@@ -1,25 +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
|
-
};
|
|
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
|
+
};
|
|
@@ -1,134 +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
|
-
};
|
|
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
CHANGED
|
@@ -1,56 +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';
|
|
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';
|