@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,294 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CallStateController = void 0;
4
+ const rxjs_1 = require("rxjs");
5
+ const operators_1 = require("rxjs/operators");
6
+ const call_1 = require("../../models/call");
7
+ const call_state_1 = require("../../models/call-state");
8
+ const callkit_coordinator_1 = require("../../callkit/callkit-coordinator");
9
+ /**
10
+ * Central state machine for call management.
11
+ *
12
+ * This class manages all active calls, handles call state transitions,
13
+ * and provides reactive streams for call-related state changes.
14
+ */
15
+ class CallStateController {
16
+ constructor(_sessionManager) {
17
+ this._sessionManager = _sessionManager;
18
+ this._calls = new rxjs_1.BehaviorSubject([]);
19
+ this._callMap = new Map();
20
+ this._disposed = false;
21
+ // Don't set up client listeners here - client doesn't exist yet
22
+ // Will be called when client is available
23
+ }
24
+ /**
25
+ * Observable stream of all current calls
26
+ */
27
+ get calls$() {
28
+ return this._calls.asObservable().pipe((0, operators_1.distinctUntilChanged)());
29
+ }
30
+ /**
31
+ * Observable stream of the currently active call
32
+ */
33
+ get activeCall$() {
34
+ return this.calls$.pipe((0, operators_1.map)((calls) => {
35
+ // Find the first call that is not terminated (includes RINGING, CONNECTING, ACTIVE, HELD)
36
+ return (calls.find((call) => call.currentState === call_state_1.TelnyxCallState.RINGING ||
37
+ call.currentState === call_state_1.TelnyxCallState.CONNECTING ||
38
+ call.currentState === call_state_1.TelnyxCallState.ACTIVE ||
39
+ call.currentState === call_state_1.TelnyxCallState.HELD) || null);
40
+ }), (0, operators_1.distinctUntilChanged)());
41
+ }
42
+ /**
43
+ * Current list of calls (synchronous access)
44
+ */
45
+ get currentCalls() {
46
+ return this._calls.value;
47
+ }
48
+ /**
49
+ * Current active call (synchronous access)
50
+ */
51
+ get currentActiveCall() {
52
+ const calls = this.currentCalls;
53
+ return (calls.find((call) => call.currentState === call_state_1.TelnyxCallState.RINGING ||
54
+ call.currentState === call_state_1.TelnyxCallState.CONNECTING ||
55
+ call.currentState === call_state_1.TelnyxCallState.ACTIVE ||
56
+ call.currentState === call_state_1.TelnyxCallState.HELD) || null);
57
+ }
58
+ /**
59
+ * Set a call to connecting state (used for push notification calls when answered via CallKit)
60
+ * @param callId The ID of the call to set to connecting state
61
+ */
62
+ setCallConnecting(callId) {
63
+ const call = this._callMap.get(callId);
64
+ if (call) {
65
+ console.log('CallStateController: Setting call to connecting state:', callId);
66
+ call.setConnecting();
67
+ }
68
+ else {
69
+ console.warn('CallStateController: Could not find call to set connecting:', callId);
70
+ }
71
+ }
72
+ /**
73
+ * Find a call by its underlying Telnyx call ID
74
+ * @param telnyxCall The Telnyx call object to find
75
+ */
76
+ findCallByTelnyxCall(telnyxCall) {
77
+ for (const call of this._callMap.values()) {
78
+ if (call.telnyxCall === telnyxCall || call.telnyxCall.callId === telnyxCall.callId) {
79
+ return call;
80
+ }
81
+ }
82
+ return null;
83
+ }
84
+ /**
85
+ * Initialize client listeners when the Telnyx client becomes available
86
+ * This should be called by the session manager after client creation
87
+ */
88
+ initializeClientListeners() {
89
+ console.log('🔧 CallStateController: initializeClientListeners called');
90
+ this._setupClientListeners();
91
+ // CallKit integration now handled by CallKitCoordinator
92
+ console.log('🔧 CallStateController: Using CallKitCoordinator for CallKit integration');
93
+ }
94
+ /**
95
+ * Initiate a new outgoing call
96
+ */
97
+ async newCall(destination, callerName, callerNumber, debug = false) {
98
+ if (this._disposed) {
99
+ throw new Error('CallStateController has been disposed');
100
+ }
101
+ if (!this._sessionManager.telnyxClient) {
102
+ throw new Error('Telnyx client not available');
103
+ }
104
+ try {
105
+ // Create the call using the Telnyx SDK
106
+ const callOptions = {
107
+ destinationNumber: destination,
108
+ callerIdName: callerName,
109
+ callerIdNumber: callerNumber,
110
+ };
111
+ const telnyxCall = await this._sessionManager.telnyxClient.newCall(callOptions);
112
+ // Create our wrapper Call object
113
+ const call = new call_1.Call(telnyxCall, telnyxCall.callId || this._generateCallId(), destination, false // outgoing call
114
+ );
115
+ // Add to our call tracking
116
+ this._addCall(call);
117
+ return call;
118
+ }
119
+ catch (error) {
120
+ console.error('Failed to create new call:', error);
121
+ throw error;
122
+ }
123
+ }
124
+ /**
125
+ * Set callbacks for waiting for invite logic (used for push notifications)
126
+ */
127
+ setWaitingForInviteCallbacks(callbacks) {
128
+ this._isWaitingForInvite = callbacks.isWaitingForInvite;
129
+ this._onInviteAutoAccepted = callbacks.onInviteAutoAccepted;
130
+ }
131
+ /**
132
+ * Dispose of the controller and clean up resources
133
+ */
134
+ dispose() {
135
+ if (this._disposed) {
136
+ return;
137
+ }
138
+ this._disposed = true;
139
+ // Dispose of all calls
140
+ this.currentCalls.forEach((call) => call.dispose());
141
+ this._callMap.clear();
142
+ // CallKit cleanup is now handled by CallKitCoordinator automatically
143
+ this._calls.complete();
144
+ }
145
+ /**
146
+ * Set up event listeners for the Telnyx client
147
+ */
148
+ _setupClientListeners() {
149
+ console.log('🔧 CallStateController: Setting up client listeners...');
150
+ if (!this._sessionManager.telnyxClient) {
151
+ console.log('🔧 CallStateController: No telnyxClient available yet, skipping listener setup');
152
+ return;
153
+ }
154
+ console.log('🔧 CallStateController: TelnyxClient found, setting up incoming call listener');
155
+ // Listen for incoming calls
156
+ this._sessionManager.telnyxClient.on('telnyx.call.incoming', (telnyxCall, msg) => {
157
+ console.log('📞 CallStateController: Incoming call received:', telnyxCall.callId);
158
+ this._handleIncomingCall(telnyxCall, msg);
159
+ });
160
+ // Listen for other call events if needed
161
+ // this._sessionManager.telnyxClient.on('telnyx.call.stateChange', this._handleCallStateChange.bind(this));
162
+ console.log('🔧 CallStateController: Client listeners set up successfully');
163
+ }
164
+ /**
165
+ * Handle incoming call
166
+ */
167
+ _handleIncomingCall(telnyxCall, inviteMsg) {
168
+ const callId = telnyxCall.callId || this._generateCallId();
169
+ console.log('📞 CallStateController: Handling incoming call:', callId);
170
+ console.log('📞 CallStateController: TelnyxCall object:', telnyxCall);
171
+ console.log('📞 CallStateController: Invite message:', inviteMsg);
172
+ // Check if we already have this call
173
+ if (this._callMap.has(callId)) {
174
+ console.log('Call already exists:', callId);
175
+ return;
176
+ }
177
+ // Get caller information from the invite message (preferred) or fallback to TelnyxCall
178
+ let callerNumber = 'Unknown';
179
+ let callerName = 'Unknown';
180
+ if (inviteMsg && inviteMsg.params) {
181
+ callerNumber = inviteMsg.params.caller_id_number || 'Unknown';
182
+ callerName = inviteMsg.params.caller_id_name || callerNumber;
183
+ console.log('📞 CallStateController: Extracted caller info from invite - Number:', callerNumber, 'Name:', callerName);
184
+ }
185
+ else {
186
+ // Fallback to TelnyxCall properties
187
+ callerNumber = telnyxCall.remoteCallerIdNumber || 'Unknown';
188
+ callerName = telnyxCall.remoteCallerIdName || callerNumber;
189
+ console.log('📞 CallStateController: Extracted caller info from TelnyxCall - Number:', callerNumber, 'Name:', callerName);
190
+ }
191
+ // Create our wrapper Call object
192
+ const call = new call_1.Call(telnyxCall, callId, callerNumber, // Use caller number as destination for incoming calls
193
+ true // incoming call
194
+ );
195
+ // Check if we're waiting for an invite (push notification scenario)
196
+ if (this._isWaitingForInvite && this._isWaitingForInvite()) {
197
+ console.log('Auto-accepting call from push notification');
198
+ call.answer().catch((error) => {
199
+ console.error('Failed to auto-accept call:', error);
200
+ });
201
+ if (this._onInviteAutoAccepted) {
202
+ this._onInviteAutoAccepted();
203
+ }
204
+ }
205
+ // Add to our call tracking - CallKit integration happens in _addCall
206
+ this._addCall(call);
207
+ }
208
+ /**
209
+ * Handle call state changes from the Telnyx client
210
+ */
211
+ _handleCallStateChange(event) {
212
+ const callId = event.callId || event.id;
213
+ const call = this._callMap.get(callId);
214
+ if (call) {
215
+ // The Call object will handle its own state updates through its listeners
216
+ console.log(`Call ${callId} state changed to ${event.state}`);
217
+ }
218
+ else {
219
+ console.warn(`Received state change for unknown call: ${callId}`);
220
+ }
221
+ }
222
+ /**
223
+ * Handle call updates from notifications
224
+ */
225
+ _handleCallUpdate(callData) {
226
+ const callId = callData.id;
227
+ const call = this._callMap.get(callId);
228
+ if (call) {
229
+ // Update call state based on the notification
230
+ console.log(`Call ${callId} updated:`, callData);
231
+ }
232
+ else {
233
+ console.warn(`Received update for unknown call: ${callId}`);
234
+ }
235
+ }
236
+ /**
237
+ * Add a call to our tracking
238
+ */
239
+ _addCall(call) {
240
+ this._callMap.set(call.callId, call);
241
+ const currentCalls = this.currentCalls;
242
+ currentCalls.push(call);
243
+ this._calls.next([...currentCalls]);
244
+ // Integrate with CallKit using CallKitCoordinator
245
+ if (callkit_coordinator_1.callKitCoordinator.isAvailable()) {
246
+ // Get the underlying TelnyxCall for CallKitCoordinator
247
+ const telnyxCall = call.telnyxCall;
248
+ // Check if this call already has CallKit integration (e.g., from push notification)
249
+ const existingCallKitUUID = callkit_coordinator_1.callKitCoordinator.getCallKitUUID(telnyxCall);
250
+ if (existingCallKitUUID) {
251
+ console.log('CallStateController: Call already has CallKit integration, skipping duplicate report:', existingCallKitUUID);
252
+ }
253
+ else if (call.isIncoming) {
254
+ // Handle incoming call with CallKit (only if not already integrated)
255
+ console.log('CallStateController: Reporting incoming call to CallKitCoordinator');
256
+ callkit_coordinator_1.callKitCoordinator.reportIncomingCall(telnyxCall, call.destination, call.destination);
257
+ }
258
+ else {
259
+ // Handle outgoing call with CallKit
260
+ console.log('CallStateController: Starting outgoing call with CallKitCoordinator');
261
+ callkit_coordinator_1.callKitCoordinator.startOutgoingCall(telnyxCall, call.destination, call.destination);
262
+ }
263
+ }
264
+ // Listen for call state changes - CallKitCoordinator handles this automatically
265
+ call.callState$.subscribe((state) => {
266
+ // CallKitCoordinator automatically updates CallKit via setupWebRTCCallListeners
267
+ console.log('CallStateController: Call state changed to:', state);
268
+ // Clean up when call ends
269
+ if (state === call_state_1.TelnyxCallState.ENDED || state === call_state_1.TelnyxCallState.FAILED) {
270
+ this._removeCall(call.callId);
271
+ }
272
+ });
273
+ }
274
+ /**
275
+ * Remove a call from our tracking
276
+ */
277
+ _removeCall(callId) {
278
+ const call = this._callMap.get(callId);
279
+ if (call) {
280
+ // CallKit cleanup is handled automatically by CallKitCoordinator
281
+ call.dispose();
282
+ this._callMap.delete(callId);
283
+ const currentCalls = this.currentCalls.filter((c) => c.callId !== callId);
284
+ this._calls.next(currentCalls);
285
+ }
286
+ }
287
+ /**
288
+ * Generate a unique call ID
289
+ */
290
+ _generateCallId() {
291
+ return `call_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
292
+ }
293
+ }
294
+ exports.CallStateController = CallStateController;
@@ -0,0 +1,87 @@
1
+ import { Observable } from 'rxjs';
2
+ import * as TelnyxSDK from '@telnyx/react-native-voice-sdk';
3
+ import { TelnyxConnectionState } from '../../models/connection-state';
4
+ import { Config, CredentialConfig, TokenConfig } from '../../models/config';
5
+ /**
6
+ * Manages the connection lifecycle to the Telnyx platform.
7
+ *
8
+ * This class handles authentication, connection state management,
9
+ * and automatic reconnection logic.
10
+ */
11
+ export declare class SessionManager {
12
+ private readonly _connectionState;
13
+ private _telnyxClient?;
14
+ private _currentConfig?;
15
+ private _sessionId;
16
+ private _disposed;
17
+ private _onClientReady?;
18
+ constructor();
19
+ /**
20
+ * Observable stream of connection state changes
21
+ */
22
+ get connectionState$(): Observable<TelnyxConnectionState>;
23
+ /**
24
+ * Set callback to be called when the Telnyx client is ready
25
+ */
26
+ setOnClientReady(callback: () => void): void;
27
+ /**
28
+ * Current connection state (synchronous access)
29
+ */
30
+ get currentState(): TelnyxConnectionState;
31
+ /**
32
+ * Current session ID
33
+ */
34
+ get sessionId(): string;
35
+ /**
36
+ * Get the underlying Telnyx client instance
37
+ */
38
+ get telnyxClient(): TelnyxSDK.TelnyxRTC | undefined;
39
+ /**
40
+ * Connect using credential authentication
41
+ */
42
+ connectWithCredential(config: CredentialConfig): Promise<void>;
43
+ /**
44
+ * Connect using token authentication
45
+ */
46
+ connectWithToken(config: TokenConfig): Promise<void>;
47
+ /**
48
+ * Disconnect from the Telnyx platform
49
+ */
50
+ disconnect(): Promise<void>;
51
+ /**
52
+ * Disable push notifications for the current session
53
+ */
54
+ disablePushNotifications(): void;
55
+ /**
56
+ * Handle push notification with stored config
57
+ */
58
+ handlePushNotificationWithConfig(pushMetaData: any, config: Config): void;
59
+ /**
60
+ * Handle push notification (async version)
61
+ */
62
+ handlePushNotification(payload: Record<string, any>): Promise<void>;
63
+ /**
64
+ * Dispose of the session manager and clean up resources
65
+ */
66
+ dispose(): void;
67
+ /**
68
+ * Internal method to establish connection
69
+ */
70
+ private _connect;
71
+ /**
72
+ * Set up event listeners for the Telnyx client
73
+ */
74
+ private _setupClientListeners;
75
+ /**
76
+ * Attempt to reconnect after connection loss
77
+ */
78
+ private _attemptReconnection;
79
+ /**
80
+ * Extract the actual payload metadata from wrapped push notification payload
81
+ */
82
+ private _extractPushPayload;
83
+ /**
84
+ * Generate a unique session ID
85
+ */
86
+ private _generateSessionId;
87
+ }