@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.
Files changed (73) hide show
  1. package/TelnyxVoiceCommons.podspec +32 -0
  2. package/ios/CallKitBridge.m +44 -0
  3. package/ios/CallKitBridge.swift +879 -0
  4. package/ios/README.md +211 -0
  5. package/ios/VoicePnBridge.m +31 -0
  6. package/ios/VoicePnBridge.swift +87 -0
  7. package/lib/callkit/callkit-coordinator.d.ts +126 -0
  8. package/lib/callkit/callkit-coordinator.js +728 -0
  9. package/lib/callkit/callkit.d.ts +49 -0
  10. package/lib/callkit/callkit.js +262 -0
  11. package/lib/callkit/index.d.ts +4 -0
  12. package/lib/callkit/index.js +15 -0
  13. package/lib/callkit/use-callkit-coordinator.d.ts +21 -0
  14. package/lib/callkit/use-callkit-coordinator.js +53 -0
  15. package/lib/callkit/use-callkit.d.ts +28 -0
  16. package/lib/callkit/use-callkit.js +279 -0
  17. package/lib/context/TelnyxVoiceContext.d.ts +18 -0
  18. package/lib/context/TelnyxVoiceContext.js +18 -0
  19. package/lib/hooks/use-callkit-coordinator.d.ts +13 -0
  20. package/lib/hooks/use-callkit-coordinator.js +48 -0
  21. package/lib/hooks/useAppReadyNotifier.d.ts +9 -0
  22. package/lib/hooks/useAppReadyNotifier.js +25 -0
  23. package/lib/hooks/useAppStateHandler.d.ts +16 -0
  24. package/lib/hooks/useAppStateHandler.js +105 -0
  25. package/lib/index.d.ts +24 -0
  26. package/lib/index.js +66 -0
  27. package/lib/internal/CallKitHandler.d.ts +17 -0
  28. package/lib/internal/CallKitHandler.js +110 -0
  29. package/lib/internal/callkit-manager.d.ts +69 -0
  30. package/lib/internal/callkit-manager.js +326 -0
  31. package/lib/internal/calls/call-state-controller.d.ts +92 -0
  32. package/lib/internal/calls/call-state-controller.js +294 -0
  33. package/lib/internal/session/session-manager.d.ts +87 -0
  34. package/lib/internal/session/session-manager.js +385 -0
  35. package/lib/internal/user-defaults-helpers.d.ts +10 -0
  36. package/lib/internal/user-defaults-helpers.js +69 -0
  37. package/lib/internal/voice-pn-bridge.d.ts +14 -0
  38. package/lib/internal/voice-pn-bridge.js +5 -0
  39. package/lib/models/call-state.d.ts +61 -0
  40. package/lib/models/call-state.js +87 -0
  41. package/lib/models/call.d.ts +145 -0
  42. package/lib/models/call.js +372 -0
  43. package/lib/models/config.d.ts +64 -0
  44. package/lib/models/config.js +92 -0
  45. package/lib/models/connection-state.d.ts +34 -0
  46. package/lib/models/connection-state.js +50 -0
  47. package/lib/telnyx-voice-app.d.ts +48 -0
  48. package/lib/telnyx-voice-app.js +486 -0
  49. package/lib/telnyx-voip-client.d.ts +184 -0
  50. package/lib/telnyx-voip-client.js +386 -0
  51. package/package.json +104 -0
  52. package/src/callkit/callkit-coordinator.ts +846 -0
  53. package/src/callkit/callkit.ts +322 -0
  54. package/src/callkit/index.ts +4 -0
  55. package/src/callkit/use-callkit.ts +345 -0
  56. package/src/context/TelnyxVoiceContext.tsx +33 -0
  57. package/src/hooks/use-callkit-coordinator.ts +60 -0
  58. package/src/hooks/useAppReadyNotifier.ts +25 -0
  59. package/src/hooks/useAppStateHandler.ts +134 -0
  60. package/src/index.ts +56 -0
  61. package/src/internal/CallKitHandler.tsx +149 -0
  62. package/src/internal/callkit-manager.ts +335 -0
  63. package/src/internal/calls/call-state-controller.ts +384 -0
  64. package/src/internal/session/session-manager.ts +467 -0
  65. package/src/internal/user-defaults-helpers.ts +58 -0
  66. package/src/internal/voice-pn-bridge.ts +18 -0
  67. package/src/models/call-state.ts +98 -0
  68. package/src/models/call.ts +388 -0
  69. package/src/models/config.ts +125 -0
  70. package/src/models/connection-state.ts +50 -0
  71. package/src/telnyx-voice-app.tsx +690 -0
  72. package/src/telnyx-voip-client.ts +475 -0
  73. package/src/types/telnyx-sdk.d.ts +79 -0
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.useCallKit = useCallKit;
37
+ const react_1 = require("react");
38
+ const callkit_1 = __importStar(require("./callkit"));
39
+ function useCallKit(options = {}) {
40
+ const { onAnswerCall, onEndCall, onStartCall } = options;
41
+ const [activeCalls, setActiveCalls] = (0, react_1.useState)([]);
42
+ const [isAvailable, setIsAvailable] = (0, react_1.useState)(false);
43
+ // Use refs to store stable callback references and current state
44
+ const onAnswerCallRef = (0, react_1.useRef)(onAnswerCall);
45
+ const onEndCallRef = (0, react_1.useRef)(onEndCall);
46
+ const onStartCallRef = (0, react_1.useRef)(onStartCall);
47
+ const activeCallsRef = (0, react_1.useRef)(activeCalls);
48
+ // Update refs when callbacks change
49
+ (0, react_1.useEffect)(() => {
50
+ onAnswerCallRef.current = onAnswerCall;
51
+ }, [onAnswerCall]);
52
+ (0, react_1.useEffect)(() => {
53
+ onEndCallRef.current = onEndCall;
54
+ }, [onEndCall]);
55
+ (0, react_1.useEffect)(() => {
56
+ onStartCallRef.current = onStartCall;
57
+ }, [onStartCall]);
58
+ // Update active calls ref
59
+ (0, react_1.useEffect)(() => {
60
+ activeCallsRef.current = activeCalls;
61
+ }, [activeCalls]);
62
+ (0, react_1.useEffect)(() => {
63
+ setIsAvailable(callkit_1.default.isAvailable());
64
+ if (!callkit_1.default.isAvailable()) {
65
+ console.log('CallKit: Not available on this platform');
66
+ return;
67
+ }
68
+ // Load existing active calls only once
69
+ const loadActiveCalls = async () => {
70
+ const calls = await callkit_1.default.getActiveCalls();
71
+ setActiveCalls(calls.map((call) => ({
72
+ uuid: call.uuid,
73
+ handle: call.handle || call.caller || 'Unknown',
74
+ displayName: call.caller || call.displayName || 'Unknown Caller',
75
+ isActive: true,
76
+ direction: call.direction || 'incoming',
77
+ })));
78
+ };
79
+ loadActiveCalls();
80
+ // Set up event listeners using stable refs
81
+ const unsubscribeAnswer = callkit_1.default.onAnswerCall((event) => {
82
+ console.log('useCallKit: Call answered via CallKit', event);
83
+ onAnswerCallRef.current?.(event.callUUID);
84
+ });
85
+ const unsubscribeEnd = callkit_1.default.onEndCall((event) => {
86
+ console.log('useCallKit: Call ended via CallKit', event);
87
+ setActiveCalls((prev) => prev.filter((call) => call.uuid !== event.callUUID));
88
+ onEndCallRef.current?.(event.callUUID);
89
+ });
90
+ const unsubscribeStart = callkit_1.default.onStartCall((event) => {
91
+ console.log('useCallKit: Call started via CallKit', event);
92
+ onStartCallRef.current?.(event.callUUID);
93
+ });
94
+ return () => {
95
+ unsubscribeAnswer();
96
+ unsubscribeEnd();
97
+ unsubscribeStart();
98
+ };
99
+ }, []); // Empty dependency array - only run once
100
+ // Start an outgoing call
101
+ const startOutgoingCall = (0, react_1.useCallback)(async (call, handle, displayName) => {
102
+ if (!callkit_1.default.isAvailable()) {
103
+ console.warn('CallKit: Not available, cannot start outgoing call');
104
+ return null;
105
+ }
106
+ const callUUID = callkit_1.default.generateCallUUID();
107
+ const callHandle = handle || call.destinationNumber || 'Unknown';
108
+ const callDisplayName = displayName || callHandle;
109
+ console.log('useCallKit: Starting outgoing call', {
110
+ callUUID,
111
+ callHandle,
112
+ callDisplayName,
113
+ telnyxCallId: call.callId,
114
+ });
115
+ const success = await callkit_1.default.startOutgoingCall(callUUID, callHandle, callDisplayName);
116
+ if (success) {
117
+ const newCall = {
118
+ uuid: callUUID,
119
+ handle: callHandle,
120
+ displayName: callDisplayName,
121
+ isActive: false,
122
+ direction: 'outgoing',
123
+ };
124
+ setActiveCalls((prev) => [...prev, newCall]);
125
+ // Store mapping between CallKit UUID and Telnyx call for later reference
126
+ call._callKitUUID = callUUID;
127
+ return callUUID;
128
+ }
129
+ return null;
130
+ }, []);
131
+ // Report an incoming call
132
+ const reportIncomingCall = (0, react_1.useCallback)(async (call, handle, displayName) => {
133
+ if (!callkit_1.default.isAvailable()) {
134
+ console.warn('CallKit: Not available, cannot report incoming call');
135
+ return null;
136
+ }
137
+ const callUUID = callkit_1.default.generateCallUUID();
138
+ const callHandle = handle || call.destinationNumber || call.callId || 'Unknown';
139
+ const callDisplayName = displayName || callHandle;
140
+ console.log('useCallKit: Reporting incoming call', {
141
+ callUUID,
142
+ callHandle,
143
+ callDisplayName,
144
+ telnyxCallId: call.callId,
145
+ });
146
+ const success = await callkit_1.default.reportIncomingCall(callUUID, callHandle, callDisplayName);
147
+ if (success) {
148
+ const newCall = {
149
+ uuid: callUUID,
150
+ handle: callHandle,
151
+ displayName: callDisplayName,
152
+ isActive: false,
153
+ direction: 'incoming',
154
+ };
155
+ setActiveCalls((prev) => [...prev, newCall]);
156
+ // Store mapping between CallKit UUID and Telnyx call for later reference
157
+ call._callKitUUID = callUUID;
158
+ return callUUID;
159
+ }
160
+ return null;
161
+ }, []);
162
+ // End a call
163
+ const endCall = (0, react_1.useCallback)(async (callUUID, reason = callkit_1.CallEndReason.RemoteEnded) => {
164
+ if (!callkit_1.default.isAvailable()) {
165
+ return false;
166
+ }
167
+ console.log('useCallKit: Ending call', { callUUID, reason });
168
+ try {
169
+ // For incoming calls, we should use reportCallEnded (which dismisses the UI)
170
+ // For outgoing calls, we should use endCall (which sends the end request)
171
+ // Check if this is likely an incoming call by looking at our active calls
172
+ const activeCall = activeCallsRef.current.find((call) => call.uuid === callUUID);
173
+ const isIncomingCall = activeCall?.direction === 'incoming';
174
+ if (isIncomingCall) {
175
+ await callkit_1.default.reportCallEnded(callUUID, reason);
176
+ }
177
+ else {
178
+ await callkit_1.default.endCall(callUUID);
179
+ }
180
+ // Update our local state
181
+ setActiveCalls((prev) => prev.filter((call) => call.uuid !== callUUID));
182
+ return true;
183
+ }
184
+ catch (error) {
185
+ console.log('useCallKit: Error ending call (may already be ended):', error);
186
+ // Still remove from our local state even if CallKit operation failed
187
+ // This ensures our UI stays in sync
188
+ setActiveCalls((prev) => prev.filter((call) => call.uuid !== callUUID));
189
+ // Return true since the call is effectively ended from our perspective
190
+ return true;
191
+ }
192
+ }, []);
193
+ // Report call connected
194
+ const reportCallConnected = (0, react_1.useCallback)(async (callUUID) => {
195
+ if (!callkit_1.default.isAvailable()) {
196
+ return false;
197
+ }
198
+ console.log('useCallKit: Reporting call connected', { callUUID });
199
+ const success = await callkit_1.default.reportCallConnected(callUUID);
200
+ if (success) {
201
+ // Update our local state to mark the call as active
202
+ setActiveCalls((prev) => prev.map((call) => (call.uuid === callUUID ? { ...call, isActive: true } : call)));
203
+ }
204
+ return success;
205
+ }, []);
206
+ // Update call information
207
+ const updateCall = (0, react_1.useCallback)(async (callUUID, displayName, handle) => {
208
+ if (!callkit_1.default.isAvailable()) {
209
+ return false;
210
+ }
211
+ console.log('useCallKit: Updating call', { callUUID, displayName, handle });
212
+ const success = await callkit_1.default.updateCall(callUUID, displayName, handle);
213
+ if (success) {
214
+ // Update our local state
215
+ setActiveCalls((prev) => prev.map((call) => (call.uuid === callUUID ? { ...call, displayName, handle } : call)));
216
+ }
217
+ return success;
218
+ }, []);
219
+ // Answer a call
220
+ const answerCall = (0, react_1.useCallback)(async (callUUID) => {
221
+ if (!callkit_1.default.isAvailable()) {
222
+ return false;
223
+ }
224
+ console.log('useCallKit: Answering call', { callUUID });
225
+ const success = await callkit_1.default.answerCall(callUUID);
226
+ if (success) {
227
+ // Update our local state to mark call as active
228
+ setActiveCalls((prev) => prev.map((call) => (call.uuid === callUUID ? { ...call, isActive: true } : call)));
229
+ }
230
+ return success;
231
+ }, []);
232
+ // Get CallKit UUID for a Telnyx call
233
+ const getCallKitUUID = (0, react_1.useCallback)((call) => {
234
+ return call._callKitUUID || null;
235
+ }, []);
236
+ // Set up automatic CallKit integration for a Telnyx call
237
+ const integrateCall = (0, react_1.useCallback)(async (call, direction) => {
238
+ if (!callkit_1.default.isAvailable()) {
239
+ return null;
240
+ }
241
+ let callUUID = null;
242
+ if (direction === 'incoming') {
243
+ callUUID = await reportIncomingCall(call);
244
+ }
245
+ else {
246
+ callUUID = await startOutgoingCall(call);
247
+ }
248
+ if (callUUID) {
249
+ // Set up automatic state reporting
250
+ const handleStateChange = async (call, state) => {
251
+ if (state === 'active') {
252
+ await reportCallConnected(callUUID);
253
+ }
254
+ else if (state === 'ended' || state === 'failed') {
255
+ const reason = state === 'failed' ? callkit_1.CallEndReason.Failed : callkit_1.CallEndReason.RemoteEnded;
256
+ await endCall(callUUID, reason);
257
+ }
258
+ };
259
+ call.on('telnyx.call.state', handleStateChange);
260
+ }
261
+ return callUUID;
262
+ }, [reportIncomingCall, startOutgoingCall, reportCallConnected, endCall]);
263
+ return {
264
+ // State
265
+ isAvailable,
266
+ activeCalls,
267
+ // Methods
268
+ startOutgoingCall,
269
+ reportIncomingCall,
270
+ answerCall,
271
+ endCall,
272
+ reportCallConnected,
273
+ updateCall,
274
+ getCallKitUUID,
275
+ integrateCall,
276
+ // Utility
277
+ generateCallUUID: callkit_1.default.generateCallUUID,
278
+ };
279
+ }
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import { TelnyxVoipClient } from '../telnyx-voip-client';
3
+ import { TelnyxConnectionState } from '../models/connection-state';
4
+ interface TelnyxVoiceContextValue {
5
+ voipClient: TelnyxVoipClient;
6
+ connectionState?: TelnyxConnectionState;
7
+ setConnectionState?: (state: TelnyxConnectionState) => void;
8
+ connect?: (payload: any) => Promise<any>;
9
+ client?: any;
10
+ setClient?: (client: any) => void;
11
+ enableAutoReconnect?: (enabled: boolean) => void;
12
+ }
13
+ export declare const TelnyxVoiceProvider: React.FC<{
14
+ voipClient: TelnyxVoipClient;
15
+ children: React.ReactNode;
16
+ }>;
17
+ export declare const useTelnyxVoice: () => TelnyxVoiceContextValue;
18
+ export {};
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useTelnyxVoice = exports.TelnyxVoiceProvider = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const TelnyxVoiceContext = (0, react_1.createContext)(null);
7
+ const TelnyxVoiceProvider = ({ voipClient, children }) => {
8
+ return ((0, jsx_runtime_1.jsx)(TelnyxVoiceContext.Provider, { value: { voipClient }, children: children }));
9
+ };
10
+ exports.TelnyxVoiceProvider = TelnyxVoiceProvider;
11
+ const useTelnyxVoice = () => {
12
+ const context = (0, react_1.useContext)(TelnyxVoiceContext);
13
+ if (!context) {
14
+ throw new Error('useTelnyxVoice must be used within a TelnyxVoiceProvider');
15
+ }
16
+ return context;
17
+ };
18
+ exports.useTelnyxVoice = useTelnyxVoice;
@@ -0,0 +1,13 @@
1
+ import { Call } from '@telnyx/react-native-voice-sdk';
2
+ import { TelnyxVoipClient } from '../telnyx-voip-client';
3
+ export declare function useCallKitCoordinator(): {
4
+ reportIncomingCall: (call: Call, callerName: string, callerNumber: string) => Promise<string | null>;
5
+ startOutgoingCall: (call: Call, destinationNumber: string, displayName?: string) => Promise<string | null>;
6
+ answerCallFromUI: (call: Call) => Promise<boolean>;
7
+ endCallFromUI: (call: Call) => Promise<boolean>;
8
+ getCallKitUUID: (call: Call) => string | null;
9
+ getWebRTCCall: (callKitUUID: string) => Call | null;
10
+ linkExistingCallKitCall: (call: Call, callKitUUID: string) => void;
11
+ isAvailable: () => boolean;
12
+ setVoipClient: (voipClient: TelnyxVoipClient) => void;
13
+ };
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.useCallKitCoordinator = useCallKitCoordinator;
7
+ const react_1 = require("react");
8
+ const callkit_coordinator_1 = __importDefault(require("../callkit/callkit-coordinator"));
9
+ function useCallKitCoordinator() {
10
+ const reportIncomingCall = (0, react_1.useCallback)(async (call, callerName, callerNumber) => {
11
+ return callkit_coordinator_1.default.reportIncomingCall(call, callerName, callerNumber);
12
+ }, []);
13
+ const startOutgoingCall = (0, react_1.useCallback)(async (call, destinationNumber, displayName) => {
14
+ return callkit_coordinator_1.default.startOutgoingCall(call, destinationNumber, displayName);
15
+ }, []);
16
+ const answerCallFromUI = (0, react_1.useCallback)(async (call) => {
17
+ return callkit_coordinator_1.default.answerCallFromUI(call);
18
+ }, []);
19
+ const endCallFromUI = (0, react_1.useCallback)(async (call) => {
20
+ return callkit_coordinator_1.default.endCallFromUI(call);
21
+ }, []);
22
+ const getCallKitUUID = (0, react_1.useCallback)((call) => {
23
+ return callkit_coordinator_1.default.getCallKitUUID(call);
24
+ }, []);
25
+ const getWebRTCCall = (0, react_1.useCallback)((callKitUUID) => {
26
+ return callkit_coordinator_1.default.getWebRTCCall(callKitUUID);
27
+ }, []);
28
+ const linkExistingCallKitCall = (0, react_1.useCallback)((call, callKitUUID) => {
29
+ callkit_coordinator_1.default.linkExistingCallKitCall(call, callKitUUID);
30
+ }, []);
31
+ const isAvailable = (0, react_1.useCallback)(() => {
32
+ return callkit_coordinator_1.default.isAvailable();
33
+ }, []);
34
+ const setVoipClient = (0, react_1.useCallback)((voipClient) => {
35
+ callkit_coordinator_1.default.setVoipClient(voipClient);
36
+ }, []);
37
+ return {
38
+ reportIncomingCall,
39
+ startOutgoingCall,
40
+ answerCallFromUI,
41
+ endCallFromUI,
42
+ getCallKitUUID,
43
+ getWebRTCCall,
44
+ linkExistingCallKitCall,
45
+ isAvailable,
46
+ setVoipClient,
47
+ };
48
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Hook to notify the native side when React Native is ready
3
+ * Call this hook when your main screen/login screen is visible and ready
4
+ * This is automatically called by TelnyxVoiceApp, but can be used manually if needed
5
+ *
6
+ * Note: With the native VoicePnManager integration, this notification is now
7
+ * handled automatically by the native Android services, so this hook is simplified.
8
+ */
9
+ export declare const useAppReadyNotifier: () => void;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useAppReadyNotifier = void 0;
4
+ const react_1 = require("react");
5
+ const react_native_1 = require("react-native");
6
+ /**
7
+ * Hook to notify the native side when React Native is ready
8
+ * Call this hook when your main screen/login screen is visible and ready
9
+ * This is automatically called by TelnyxVoiceApp, but can be used manually if needed
10
+ *
11
+ * Note: With the native VoicePnManager integration, this notification is now
12
+ * handled automatically by the native Android services, so this hook is simplified.
13
+ */
14
+ const useAppReadyNotifier = () => {
15
+ (0, react_1.useEffect)(() => {
16
+ // Only notify on Android since this is Android-specific functionality
17
+ if (react_native_1.Platform.OS === 'android') {
18
+ console.log('useAppReadyNotifier: React Native is ready (native services handle FCM automatically)');
19
+ // Note: The VoicePnManager in the native Android code automatically handles
20
+ // push notification state when the Firebase messaging service receives notifications.
21
+ // No JavaScript module communication is needed anymore.
22
+ }
23
+ }, []);
24
+ };
25
+ exports.useAppReadyNotifier = useAppReadyNotifier;
@@ -0,0 +1,16 @@
1
+ import { AppStateStatus } from 'react-native';
2
+ import { TelnyxVoipClient } from '../telnyx-voip-client';
3
+ interface UseAppStateHandlerOptions {
4
+ voipClient: TelnyxVoipClient;
5
+ disconnectOnBackground?: boolean;
6
+ navigateToLoginOnDisconnect?: boolean;
7
+ debug?: boolean;
8
+ }
9
+ /**
10
+ * Hook to handle app state changes for VoIP behavior
11
+ * When app goes to background without an active call, disconnect socket and redirect to login
12
+ */
13
+ export declare const useAppStateHandler: ({ voipClient, disconnectOnBackground, navigateToLoginOnDisconnect, debug, }: UseAppStateHandlerOptions) => {
14
+ currentAppState: AppStateStatus;
15
+ };
16
+ export {};
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.useAppStateHandler = void 0;
7
+ const react_1 = require("react");
8
+ const react_native_1 = require("react-native");
9
+ const expo_router_1 = require("expo-router");
10
+ const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
11
+ const connection_state_1 = require("../models/connection-state");
12
+ const call_state_1 = require("../models/call-state");
13
+ /**
14
+ * Hook to handle app state changes for VoIP behavior
15
+ * When app goes to background without an active call, disconnect socket and redirect to login
16
+ */
17
+ const useAppStateHandler = ({ voipClient, disconnectOnBackground = true, navigateToLoginOnDisconnect = true, debug = false, }) => {
18
+ const appState = (0, react_1.useRef)(react_native_1.AppState.currentState);
19
+ const log = debug ? console.log : () => { };
20
+ (0, react_1.useEffect)(() => {
21
+ const handleAppStateChange = async (nextAppState) => {
22
+ log('AppStateHandler: App state changed from', appState.current, 'to', nextAppState);
23
+ // When app goes to background
24
+ if (appState.current.match(/active/) && nextAppState === 'background') {
25
+ log('AppStateHandler: App went to background, checking for active calls...');
26
+ const connectionState = voipClient.currentConnectionState;
27
+ if (connectionState !== connection_state_1.TelnyxConnectionState.CONNECTED) {
28
+ log('AppStateHandler: Not connected, skipping background handling');
29
+ appState.current = nextAppState;
30
+ return;
31
+ }
32
+ // Check if there's an active call (including ringing calls)
33
+ const activeCalls = voipClient.currentCalls;
34
+ const hasActiveCall = activeCalls.length > 0 &&
35
+ activeCalls.some((call) => call_state_1.CallStateHelpers.isActive(call.currentState) ||
36
+ call.currentState === call_state_1.TelnyxCallState.RINGING);
37
+ log('AppStateHandler: Active call check:', {
38
+ callCount: activeCalls.length,
39
+ hasActiveCall,
40
+ callStates: activeCalls.map((call) => ({
41
+ callId: call.callId,
42
+ currentState: call.currentState,
43
+ destination: call.destination,
44
+ isIncoming: call.isIncoming,
45
+ })),
46
+ });
47
+ if (!hasActiveCall && disconnectOnBackground) {
48
+ // Check if there's a push notification call in progress
49
+ const isPushNotificationInProgress = await async_storage_1.default.getItem('@push_notification_payload');
50
+ if (isPushNotificationInProgress) {
51
+ log('AppStateHandler: Push notification call in progress, keeping socket connected');
52
+ // Wait a bit longer before allowing disconnection to give time for WebRTC call to establish
53
+ setTimeout(async () => {
54
+ const stillInProgress = await async_storage_1.default.getItem('@push_notification_payload');
55
+ if (!stillInProgress) {
56
+ log('AppStateHandler: Push notification call completed, now disconnecting socket');
57
+ await voipClient.logout();
58
+ if (navigateToLoginOnDisconnect) {
59
+ expo_router_1.router.replace('/');
60
+ }
61
+ }
62
+ }, 5000); // Wait 5 seconds
63
+ appState.current = nextAppState;
64
+ return;
65
+ }
66
+ log('AppStateHandler: No active call detected, disconnecting socket...');
67
+ try {
68
+ // Disconnect the socket with background reason
69
+ await voipClient.logout();
70
+ log('AppStateHandler: Socket disconnected successfully');
71
+ // Navigate to login screen
72
+ if (navigateToLoginOnDisconnect) {
73
+ // Use a small delay to ensure the disconnect completes
74
+ setTimeout(() => {
75
+ log('AppStateHandler: Navigating to login screen');
76
+ expo_router_1.router.replace('/');
77
+ }, 100);
78
+ }
79
+ }
80
+ catch (error) {
81
+ console.error('AppStateHandler: Error during background disconnect:', error);
82
+ }
83
+ }
84
+ else {
85
+ log('AppStateHandler: Active call detected or disconnect disabled, keeping socket connected');
86
+ }
87
+ }
88
+ // When app comes to foreground
89
+ if (appState.current.match(/background/) && nextAppState === 'active') {
90
+ log('AppStateHandler: App came to foreground');
91
+ // User will need to manually login again when returning from background
92
+ // Auto-login only happens for push notification calls
93
+ }
94
+ appState.current = nextAppState;
95
+ };
96
+ const subscription = react_native_1.AppState.addEventListener('change', handleAppStateChange);
97
+ return () => {
98
+ subscription?.remove();
99
+ };
100
+ }, [voipClient, disconnectOnBackground, navigateToLoginOnDisconnect, log]);
101
+ return {
102
+ currentAppState: appState.current,
103
+ };
104
+ };
105
+ exports.useAppStateHandler = useAppStateHandler;
package/lib/index.d.ts ADDED
@@ -0,0 +1,24 @@
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
+ export { TelnyxVoipClient, createTelnyxVoipClient, createBackgroundTelnyxVoipClient, } from './telnyx-voip-client';
12
+ export type { TelnyxVoipClientOptions } from './telnyx-voip-client';
13
+ export { TelnyxVoiceApp } from './telnyx-voice-app';
14
+ export type { TelnyxVoiceAppOptions, TelnyxVoiceAppProps } from './telnyx-voice-app';
15
+ export { useAppStateHandler } from './hooks/useAppStateHandler';
16
+ export { useTelnyxVoice } from './context/TelnyxVoiceContext';
17
+ export { Call } from './models/call';
18
+ export { TelnyxConnectionState, isTelnyxConnectionState, canMakeCalls, isConnected, isTransitioning, } from './models/connection-state';
19
+ export { TelnyxCallState, isTelnyxCallState, CallStateHelpers } from './models/call-state';
20
+ export { isCredentialConfig, isTokenConfig, validateConfig, validateCredentialConfig, validateTokenConfig, createCredentialConfig, createTokenConfig, } from './models/config';
21
+ export type { Config, CredentialConfig, TokenConfig } from './models/config';
22
+ export type { Call as TelnyxCall } from '@telnyx/react-native-voice-sdk';
23
+ export * from './callkit';
24
+ export { useAppReadyNotifier } from './hooks/useAppReadyNotifier';
package/lib/index.js ADDED
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ /**
3
+ * @telnyx/react-voice-commons-sdk
4
+ *
5
+ * A high-level, state-agnostic, drop-in module for the Telnyx React Native SDK.
6
+ *
7
+ * This library provides a simplified interface for integrating Telnyx WebRTC
8
+ * capabilities into React Native applications. It handles session management,
9
+ * call state transitions, push notification processing, and native call UI
10
+ * integration.
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
24
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
25
+ };
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.useAppReadyNotifier = exports.createTokenConfig = exports.createCredentialConfig = exports.validateTokenConfig = exports.validateCredentialConfig = exports.validateConfig = exports.isTokenConfig = exports.isCredentialConfig = exports.CallStateHelpers = exports.isTelnyxCallState = exports.TelnyxCallState = exports.isTransitioning = exports.isConnected = exports.canMakeCalls = exports.isTelnyxConnectionState = exports.TelnyxConnectionState = exports.Call = exports.useTelnyxVoice = exports.useAppStateHandler = exports.TelnyxVoiceApp = exports.createBackgroundTelnyxVoipClient = exports.createTelnyxVoipClient = exports.TelnyxVoipClient = void 0;
28
+ // Main client
29
+ var telnyx_voip_client_1 = require("./telnyx-voip-client");
30
+ Object.defineProperty(exports, "TelnyxVoipClient", { enumerable: true, get: function () { return telnyx_voip_client_1.TelnyxVoipClient; } });
31
+ Object.defineProperty(exports, "createTelnyxVoipClient", { enumerable: true, get: function () { return telnyx_voip_client_1.createTelnyxVoipClient; } });
32
+ Object.defineProperty(exports, "createBackgroundTelnyxVoipClient", { enumerable: true, get: function () { return telnyx_voip_client_1.createBackgroundTelnyxVoipClient; } });
33
+ // TelnyxVoiceApp component
34
+ var telnyx_voice_app_1 = require("./telnyx-voice-app");
35
+ Object.defineProperty(exports, "TelnyxVoiceApp", { enumerable: true, get: function () { return telnyx_voice_app_1.TelnyxVoiceApp; } });
36
+ // Hooks
37
+ var useAppStateHandler_1 = require("./hooks/useAppStateHandler");
38
+ Object.defineProperty(exports, "useAppStateHandler", { enumerable: true, get: function () { return useAppStateHandler_1.useAppStateHandler; } });
39
+ var TelnyxVoiceContext_1 = require("./context/TelnyxVoiceContext");
40
+ Object.defineProperty(exports, "useTelnyxVoice", { enumerable: true, get: function () { return TelnyxVoiceContext_1.useTelnyxVoice; } });
41
+ // Models
42
+ var call_1 = require("./models/call");
43
+ Object.defineProperty(exports, "Call", { enumerable: true, get: function () { return call_1.Call; } });
44
+ var connection_state_1 = require("./models/connection-state");
45
+ Object.defineProperty(exports, "TelnyxConnectionState", { enumerable: true, get: function () { return connection_state_1.TelnyxConnectionState; } });
46
+ Object.defineProperty(exports, "isTelnyxConnectionState", { enumerable: true, get: function () { return connection_state_1.isTelnyxConnectionState; } });
47
+ Object.defineProperty(exports, "canMakeCalls", { enumerable: true, get: function () { return connection_state_1.canMakeCalls; } });
48
+ Object.defineProperty(exports, "isConnected", { enumerable: true, get: function () { return connection_state_1.isConnected; } });
49
+ Object.defineProperty(exports, "isTransitioning", { enumerable: true, get: function () { return connection_state_1.isTransitioning; } });
50
+ var call_state_1 = require("./models/call-state");
51
+ Object.defineProperty(exports, "TelnyxCallState", { enumerable: true, get: function () { return call_state_1.TelnyxCallState; } });
52
+ Object.defineProperty(exports, "isTelnyxCallState", { enumerable: true, get: function () { return call_state_1.isTelnyxCallState; } });
53
+ Object.defineProperty(exports, "CallStateHelpers", { enumerable: true, get: function () { return call_state_1.CallStateHelpers; } });
54
+ var config_1 = require("./models/config");
55
+ Object.defineProperty(exports, "isCredentialConfig", { enumerable: true, get: function () { return config_1.isCredentialConfig; } });
56
+ Object.defineProperty(exports, "isTokenConfig", { enumerable: true, get: function () { return config_1.isTokenConfig; } });
57
+ Object.defineProperty(exports, "validateConfig", { enumerable: true, get: function () { return config_1.validateConfig; } });
58
+ Object.defineProperty(exports, "validateCredentialConfig", { enumerable: true, get: function () { return config_1.validateCredentialConfig; } });
59
+ Object.defineProperty(exports, "validateTokenConfig", { enumerable: true, get: function () { return config_1.validateTokenConfig; } });
60
+ Object.defineProperty(exports, "createCredentialConfig", { enumerable: true, get: function () { return config_1.createCredentialConfig; } });
61
+ Object.defineProperty(exports, "createTokenConfig", { enumerable: true, get: function () { return config_1.createTokenConfig; } });
62
+ // Export CallKit functionality
63
+ __exportStar(require("./callkit"), exports);
64
+ // Export hooks
65
+ var useAppReadyNotifier_1 = require("./hooks/useAppReadyNotifier");
66
+ Object.defineProperty(exports, "useAppReadyNotifier", { enumerable: true, get: function () { return useAppReadyNotifier_1.useAppReadyNotifier; } });
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ interface CallKitHandlerProps {
3
+ /** Callback when user needs to login from push notification */
4
+ onLoginRequired?: (pushPayload: any) => void;
5
+ /** Callback when call is answered and user should navigate to dialer */
6
+ onNavigateToDialer?: () => void;
7
+ /** Callback when call ends and user should navigate back */
8
+ onNavigateBack?: () => void;
9
+ }
10
+ /**
11
+ * Internal CallKit handler for iOS push notifications
12
+ * This component is automatically included in TelnyxVoiceApp
13
+ *
14
+ * @internal - Users should not use this component directly
15
+ */
16
+ export declare const CallKitHandler: React.FC<CallKitHandlerProps>;
17
+ export {};