@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,467 @@
1
+ import { BehaviorSubject, Observable } from 'rxjs';
2
+ import { distinctUntilChanged } from 'rxjs/operators';
3
+ import * as TelnyxSDK from '@telnyx/react-native-voice-sdk';
4
+ import { TelnyxConnectionState } from '../../models/connection-state';
5
+ import {
6
+ Config,
7
+ CredentialConfig,
8
+ TokenConfig,
9
+ isCredentialConfig,
10
+ isTokenConfig,
11
+ } from '../../models/config';
12
+
13
+ /**
14
+ * Manages the connection lifecycle to the Telnyx platform.
15
+ *
16
+ * This class handles authentication, connection state management,
17
+ * and automatic reconnection logic.
18
+ */
19
+ export class SessionManager {
20
+ private readonly _connectionState = new BehaviorSubject<TelnyxConnectionState>(
21
+ TelnyxConnectionState.DISCONNECTED
22
+ );
23
+ private _telnyxClient?: TelnyxSDK.TelnyxRTC;
24
+ private _currentConfig?: Config;
25
+ private _sessionId: string;
26
+ private _disposed = false;
27
+ private _onClientReady?: () => void;
28
+
29
+ constructor() {
30
+ this._sessionId = this._generateSessionId();
31
+ }
32
+
33
+ /**
34
+ * Observable stream of connection state changes
35
+ */
36
+ get connectionState$(): Observable<TelnyxConnectionState> {
37
+ return this._connectionState.asObservable().pipe(distinctUntilChanged());
38
+ }
39
+
40
+ /**
41
+ * Set callback to be called when the Telnyx client is ready
42
+ */
43
+ setOnClientReady(callback: () => void): void {
44
+ this._onClientReady = callback;
45
+ }
46
+
47
+ /**
48
+ * Current connection state (synchronous access)
49
+ */
50
+ get currentState(): TelnyxConnectionState {
51
+ return this._connectionState.value;
52
+ }
53
+
54
+ /**
55
+ * Current session ID
56
+ */
57
+ get sessionId(): string {
58
+ return this._sessionId;
59
+ }
60
+
61
+ /**
62
+ * Get the underlying Telnyx client instance
63
+ */
64
+ get telnyxClient(): TelnyxSDK.TelnyxRTC | undefined {
65
+ return this._telnyxClient;
66
+ }
67
+
68
+ /**
69
+ * Connect using credential authentication
70
+ */
71
+ async connectWithCredential(config: CredentialConfig): Promise<void> {
72
+ if (this._disposed) {
73
+ throw new Error('SessionManager has been disposed');
74
+ }
75
+
76
+ this._currentConfig = config;
77
+ await this._connect();
78
+ }
79
+
80
+ /**
81
+ * Connect using token authentication
82
+ */
83
+ async connectWithToken(config: TokenConfig): Promise<void> {
84
+ if (this._disposed) {
85
+ throw new Error('SessionManager has been disposed');
86
+ }
87
+
88
+ this._currentConfig = config;
89
+ await this._connect();
90
+ }
91
+
92
+ /**
93
+ * Disconnect from the Telnyx platform
94
+ */
95
+ async disconnect(): Promise<void> {
96
+ if (this._disposed) {
97
+ return;
98
+ }
99
+
100
+ this._currentConfig = undefined;
101
+
102
+ if (this._telnyxClient) {
103
+ try {
104
+ await this._telnyxClient.disconnect();
105
+ } catch (error) {
106
+ console.error('Error during disconnect:', error);
107
+ }
108
+ }
109
+
110
+ this._connectionState.next(TelnyxConnectionState.DISCONNECTED);
111
+ }
112
+
113
+ /**
114
+ * Disable push notifications for the current session
115
+ */
116
+ disablePushNotifications(): void {
117
+ if (this._telnyxClient && this.currentState === TelnyxConnectionState.CONNECTED) {
118
+ // Implementation depends on the actual Telnyx SDK API
119
+ // This is a placeholder for the actual implementation
120
+ console.log('Disabling push notifications for session:', this._sessionId);
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Handle push notification with stored config
126
+ */
127
+ handlePushNotificationWithConfig(pushMetaData: any, config: Config): void {
128
+ if (this._disposed) {
129
+ return;
130
+ }
131
+
132
+ this._currentConfig = config;
133
+ // Implementation for handling push notifications
134
+ // This would integrate with the actual Telnyx SDK push handling
135
+ console.log('Handling push notification with config:', { pushMetaData, config: config.type });
136
+ }
137
+
138
+ /**
139
+ * Handle push notification (async version)
140
+ */
141
+ async handlePushNotification(payload: Record<string, any>): Promise<void> {
142
+ if (this._disposed) {
143
+ return;
144
+ }
145
+
146
+ console.log(
147
+ 'SessionManager: RELEASE DEBUG - Processing push notification, payload:',
148
+ JSON.stringify(payload)
149
+ );
150
+
151
+ // Store the push notification payload for when the client is created
152
+ (this as any)._pendingPushPayload = payload;
153
+
154
+ // If we don't have a config yet but we're processing a push notification,
155
+ // attempt to load stored config first (for terminated app startup)
156
+ if (!this._currentConfig && !this._telnyxClient) {
157
+ console.log(
158
+ 'SessionManager: RELEASE DEBUG - No config available, attempting to load from stored config for push notification'
159
+ );
160
+
161
+ try {
162
+ // Try to retrieve stored credentials and token from AsyncStorage
163
+ const AsyncStorage = require('@react-native-async-storage/async-storage').default;
164
+
165
+ const storedUsername = await AsyncStorage.getItem('@telnyx_username');
166
+ const storedPassword = await AsyncStorage.getItem('@telnyx_password');
167
+ const storedCredentialToken = await AsyncStorage.getItem('@credential_token');
168
+ const storedPushToken = await AsyncStorage.getItem('@push_token');
169
+
170
+ // Check if we have credential-based authentication data
171
+ if (storedUsername && storedPassword) {
172
+ console.log('SessionManager: RELEASE DEBUG - Found stored credentials, creating config');
173
+ const { createCredentialConfig } = require('../../models/config');
174
+ this._currentConfig = createCredentialConfig(storedUsername, storedPassword, {
175
+ pushNotificationDeviceToken: storedPushToken,
176
+ });
177
+ }
178
+ // Check if we have token-based authentication data
179
+ else if (storedCredentialToken) {
180
+ console.log('SessionManager: RELEASE DEBUG - Found stored token, creating config');
181
+ const { createTokenConfig } = require('../../models/config');
182
+ this._currentConfig = createTokenConfig(storedCredentialToken, {
183
+ pushNotificationDeviceToken: storedPushToken,
184
+ });
185
+ }
186
+
187
+ if (this._currentConfig) {
188
+ console.log(
189
+ 'SessionManager: RELEASE DEBUG - Successfully loaded stored config for push notification'
190
+ );
191
+ } else {
192
+ console.log('SessionManager: RELEASE DEBUG - No stored authentication data found');
193
+ }
194
+ } catch (error) {
195
+ console.warn('SessionManager: Failed to load stored config for push notification:', error);
196
+ }
197
+ }
198
+
199
+ // If we already have a client, process the push notification immediately
200
+ if (this._telnyxClient) {
201
+ console.log(
202
+ 'SessionManager: RELEASE DEBUG - Client available, processing push notification immediately'
203
+ );
204
+
205
+ // Use type assertion to access the processVoIPNotification method
206
+ // This method sets the isCallFromPush flag which is needed for proper push handling
207
+ if (typeof (this._telnyxClient as any).processVoIPNotification === 'function') {
208
+ console.log(
209
+ 'SessionManager: RELEASE DEBUG - Calling processVoIPNotification with payload:',
210
+ JSON.stringify(payload)
211
+ );
212
+
213
+ // Extract the actual push notification metadata that the client expects
214
+ const actualPayload = this._extractPushPayload(payload);
215
+
216
+ (this._telnyxClient as any).processVoIPNotification(actualPayload);
217
+ console.log('SessionManager: RELEASE DEBUG - Called processVoIPNotification successfully');
218
+ } else {
219
+ console.warn(
220
+ 'SessionManager: processVoIPNotification method not available on TelnyxRTC client'
221
+ );
222
+ }
223
+
224
+ // Clear the pending payload since it was processed
225
+ (this as any)._pendingPushPayload = null;
226
+ } else {
227
+ console.log(
228
+ 'SessionManager: RELEASE DEBUG - No client available, checking if we can trigger immediate connection'
229
+ );
230
+
231
+ // If we have config (either existing or newly loaded from storage) and are disconnected, trigger immediate connection
232
+ // The _connect() method will process the pending push payload BEFORE calling connect()
233
+ if (this._currentConfig && this.currentState === TelnyxConnectionState.DISCONNECTED) {
234
+ console.log(
235
+ 'SessionManager: RELEASE DEBUG - Triggering immediate connection for push notification with config type:',
236
+ (this._currentConfig as any).type || 'credential'
237
+ );
238
+ try {
239
+ await this._connect();
240
+ console.log(
241
+ 'SessionManager: RELEASE DEBUG - Successfully connected after push notification trigger'
242
+ );
243
+ } catch (error) {
244
+ console.error(
245
+ 'SessionManager: Failed to connect after push notification trigger:',
246
+ error
247
+ );
248
+ }
249
+ } else {
250
+ console.log(
251
+ 'SessionManager: RELEASE DEBUG - Cannot trigger connection, config available:',
252
+ !!this._currentConfig,
253
+ 'current state:',
254
+ this.currentState
255
+ );
256
+ console.log(
257
+ 'SessionManager: RELEASE DEBUG - Push payload stored for later processing when client becomes available'
258
+ );
259
+ }
260
+ }
261
+
262
+ console.log('SessionManager: RELEASE DEBUG - Push notification handling complete');
263
+ }
264
+
265
+ /**
266
+ * Dispose of the session manager and clean up resources
267
+ */
268
+ dispose(): void {
269
+ if (this._disposed) {
270
+ return;
271
+ }
272
+
273
+ this._disposed = true;
274
+ this.disconnect();
275
+ this._connectionState.complete();
276
+ }
277
+
278
+ /**
279
+ * Internal method to establish connection
280
+ */
281
+ private async _connect(): Promise<void> {
282
+ if (!this._currentConfig) {
283
+ throw new Error('No configuration provided');
284
+ }
285
+
286
+ this._connectionState.next(TelnyxConnectionState.CONNECTING);
287
+
288
+ try {
289
+ // Clean up existing client
290
+ if (this._telnyxClient) {
291
+ await this._telnyxClient.disconnect();
292
+ }
293
+
294
+ // Create new client instance with authentication options
295
+ let clientOptions: TelnyxSDK.ClientOptions;
296
+
297
+ if (isCredentialConfig(this._currentConfig)) {
298
+ clientOptions = {
299
+ login: this._currentConfig.sipUser,
300
+ password: this._currentConfig.sipPassword,
301
+ logLevel: this._currentConfig.debug ? 'debug' : 'warn',
302
+ pushNotificationDeviceToken: this._currentConfig.pushNotificationDeviceToken,
303
+ };
304
+ console.log(
305
+ '🔧 SessionManager: Creating TelnyxRTC with credential config, logLevel:',
306
+ clientOptions.logLevel,
307
+ 'pushToken:',
308
+ !!this._currentConfig.pushNotificationDeviceToken
309
+ );
310
+ } else if (isTokenConfig(this._currentConfig)) {
311
+ clientOptions = {
312
+ login_token: this._currentConfig.token,
313
+ logLevel: this._currentConfig.debug ? 'debug' : 'warn',
314
+ pushNotificationDeviceToken: this._currentConfig.pushNotificationDeviceToken,
315
+ };
316
+ console.log(
317
+ '🔧 SessionManager: Creating TelnyxRTC with token config, logLevel:',
318
+ clientOptions.logLevel,
319
+ 'pushToken:',
320
+ !!this._currentConfig.pushNotificationDeviceToken
321
+ );
322
+ } else {
323
+ throw new Error('Invalid configuration type');
324
+ }
325
+
326
+ this._telnyxClient = new TelnyxSDK.TelnyxRTC(clientOptions);
327
+
328
+ // CRITICAL: Process any pending push notification payload BEFORE connecting
329
+ // This ensures voice_sdk_id and other payload variables are set before connect() is called
330
+ const pendingPushPayload = (this as any)._pendingPushPayload;
331
+ if (pendingPushPayload) {
332
+ console.log(
333
+ 'SessionManager: RELEASE DEBUG - Processing pending push notification BEFORE connect:',
334
+ JSON.stringify(pendingPushPayload)
335
+ );
336
+
337
+ if (typeof (this._telnyxClient as any).processVoIPNotification === 'function') {
338
+ console.log(
339
+ 'SessionManager: RELEASE DEBUG - Calling processVoIPNotification BEFORE connect to set voice_sdk_id'
340
+ );
341
+
342
+ // Extract the actual push notification metadata that the client expects
343
+ const actualPayload = this._extractPushPayload(pendingPushPayload);
344
+
345
+ (this._telnyxClient as any).processVoIPNotification(actualPayload);
346
+ console.log(
347
+ 'SessionManager: RELEASE DEBUG - Successfully processed pending push notification before connect'
348
+ );
349
+ } else {
350
+ console.warn(
351
+ 'SessionManager: processVoIPNotification method not available on new client'
352
+ );
353
+ }
354
+
355
+ // Clear the pending payload
356
+ (this as any)._pendingPushPayload = null;
357
+ }
358
+
359
+ this._setupClientListeners();
360
+
361
+ // Set up CallStateController listeners immediately after client creation
362
+ // This ensures they're ready before any incoming call events are emitted
363
+ console.log(
364
+ '🔧 SessionManager: Setting up CallStateController listeners before connection...'
365
+ );
366
+ if (this._onClientReady) {
367
+ this._onClientReady();
368
+ }
369
+
370
+ // Connect to the platform AFTER processing push notification
371
+ console.log(
372
+ 'SessionManager: RELEASE DEBUG - About to call connect() after processing push notification'
373
+ );
374
+ await this._telnyxClient.connect();
375
+
376
+ // Notify that client is ready for event listeners
377
+ console.log('🔧 SessionManager: Client connected successfully');
378
+ } catch (error) {
379
+ console.error('Connection failed:', error);
380
+ this._connectionState.next(TelnyxConnectionState.ERROR);
381
+ throw error;
382
+ }
383
+ }
384
+
385
+ /**
386
+ * Set up event listeners for the Telnyx client
387
+ */
388
+ private _setupClientListeners(): void {
389
+ if (!this._telnyxClient) {
390
+ return;
391
+ }
392
+
393
+ this._telnyxClient.on('telnyx.client.ready', () => {
394
+ console.log('Telnyx client ready');
395
+ this._connectionState.next(TelnyxConnectionState.CONNECTED);
396
+ });
397
+
398
+ this._telnyxClient.on('telnyx.client.error', (error: Error) => {
399
+ console.error('Telnyx client error:', error);
400
+ this._connectionState.next(TelnyxConnectionState.ERROR);
401
+ });
402
+
403
+ // Note: Socket-level events are not exposed in the current SDK
404
+ // We'll rely on the client-level events for now
405
+ }
406
+
407
+ /**
408
+ * Attempt to reconnect after connection loss
409
+ */
410
+ private async _attemptReconnection(): Promise<void> {
411
+ if (this._disposed || !this._currentConfig) {
412
+ return;
413
+ }
414
+
415
+ // Simple reconnection logic - in production, this should include
416
+ // exponential backoff and maximum retry attempts
417
+ setTimeout(async () => {
418
+ if (this.currentState === TelnyxConnectionState.RECONNECTING) {
419
+ try {
420
+ await this._connect();
421
+ } catch (error) {
422
+ console.error('Reconnection failed:', error);
423
+ // Could implement retry logic here
424
+ }
425
+ }
426
+ }, 3000);
427
+ }
428
+
429
+ /**
430
+ * Extract the actual payload metadata from wrapped push notification payload
431
+ */
432
+ private _extractPushPayload(payload: Record<string, any>): any {
433
+ // The payload might be wrapped, so we need to extract the core metadata
434
+ let actualPayload = payload;
435
+
436
+ if (payload.metadata && typeof payload.metadata === 'object') {
437
+ // If there's a metadata wrapper, use that
438
+ actualPayload = payload.metadata;
439
+ console.log(
440
+ 'SessionManager: RELEASE DEBUG - Using metadata portion of payload:',
441
+ JSON.stringify(actualPayload)
442
+ );
443
+ } else if (payload.action === 'incoming_call' && payload.metadata) {
444
+ // Handle the case where metadata is a string that needs parsing
445
+ try {
446
+ const parsedMetadata =
447
+ typeof payload.metadata === 'string' ? JSON.parse(payload.metadata) : payload.metadata;
448
+ actualPayload = parsedMetadata;
449
+ console.log(
450
+ 'SessionManager: RELEASE DEBUG - Using parsed metadata:',
451
+ JSON.stringify(actualPayload)
452
+ );
453
+ } catch (error) {
454
+ console.warn('SessionManager: Failed to parse metadata:', error);
455
+ }
456
+ }
457
+
458
+ return actualPayload;
459
+ }
460
+
461
+ /**
462
+ * Generate a unique session ID
463
+ */
464
+ private _generateSessionId(): string {
465
+ return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
466
+ }
467
+ }
@@ -0,0 +1,58 @@
1
+ import { VoicePnBridge } from './voice-pn-bridge';
2
+ import { Platform } from 'react-native';
3
+
4
+ /**
5
+ * Helper functions that use VoicePnBridge for UserDefaults functionality on iOS
6
+ * These functions provide the same interface as the UserDefaultsModule from main branch
7
+ * but route through the VoicePnBridge instead
8
+ */
9
+
10
+ export async function getVoipToken(): Promise<string | null> {
11
+ if (Platform.OS !== 'ios') return null;
12
+ try {
13
+ return await VoicePnBridge.getVoipToken();
14
+ } catch (error) {
15
+ console.warn('[UserDefaults] getVoipToken error:', error);
16
+ return null;
17
+ }
18
+ }
19
+
20
+ export async function getPendingVoipPush(): Promise<string | null> {
21
+ if (Platform.OS !== 'ios') return null;
22
+ try {
23
+ return await VoicePnBridge.getPendingVoipPush();
24
+ } catch (error) {
25
+ console.warn('[UserDefaults] getPendingVoipPush error:', error);
26
+ return null;
27
+ }
28
+ }
29
+
30
+ export async function clearPendingVoipPush(): Promise<boolean> {
31
+ if (Platform.OS !== 'ios') return true;
32
+ try {
33
+ return await VoicePnBridge.clearPendingVoipPush();
34
+ } catch (error) {
35
+ console.warn('[UserDefaults] clearPendingVoipPush error:', error);
36
+ return false;
37
+ }
38
+ }
39
+
40
+ export async function getPendingVoipAction(): Promise<string | null> {
41
+ if (Platform.OS !== 'ios') return null;
42
+ try {
43
+ return await VoicePnBridge.getPendingVoipAction();
44
+ } catch (error) {
45
+ console.warn('[UserDefaults] getPendingVoipAction error:', error);
46
+ return null;
47
+ }
48
+ }
49
+
50
+ export async function clearPendingVoipAction(): Promise<boolean> {
51
+ if (Platform.OS !== 'ios') return true;
52
+ try {
53
+ return await VoicePnBridge.clearPendingVoipAction();
54
+ } catch (error) {
55
+ console.warn('[UserDefaults] clearPendingVoipAction error:', error);
56
+ return false;
57
+ }
58
+ }
@@ -0,0 +1,18 @@
1
+ import { NativeModules } from 'react-native';
2
+
3
+ export interface VoicePnBridgeInterface {
4
+ getPendingPushAction(): Promise<{
5
+ action: string | null;
6
+ metadata: string | null;
7
+ }>;
8
+ setPendingPushAction(action: string, metadata: string): Promise<boolean>;
9
+ clearPendingPushAction(): Promise<boolean>;
10
+ // Additional UserDefaults methods
11
+ getVoipToken(): Promise<string | null>;
12
+ getPendingVoipPush(): Promise<string | null>;
13
+ clearPendingVoipPush(): Promise<boolean>;
14
+ getPendingVoipAction(): Promise<string | null>;
15
+ clearPendingVoipAction(): Promise<boolean>;
16
+ }
17
+
18
+ export const VoicePnBridge: VoicePnBridgeInterface = NativeModules.VoicePnBridge;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Represents the state of a call in the Telnyx system.
3
+ *
4
+ * This enum provides a simplified view of call states, abstracting away
5
+ * the complexity of the underlying SIP call states.
6
+ */
7
+ export enum TelnyxCallState {
8
+ /** Call is being initiated (outgoing) or received (incoming) */
9
+ RINGING = 'RINGING',
10
+
11
+ /** Call is connecting after being answered (usually from push notification) */
12
+ CONNECTING = 'CONNECTING',
13
+
14
+ /** Call has been answered and media is flowing */
15
+ ACTIVE = 'ACTIVE',
16
+
17
+ /** Call is on hold */
18
+ HELD = 'HELD',
19
+
20
+ /** Call has ended normally */
21
+ ENDED = 'ENDED',
22
+
23
+ /** Call failed to connect or was rejected */
24
+ FAILED = 'FAILED',
25
+ }
26
+
27
+ /**
28
+ * Type guard to check if a value is a valid TelnyxCallState
29
+ */
30
+ export function isTelnyxCallState(value: any): value is TelnyxCallState {
31
+ return Object.values(TelnyxCallState).includes(value);
32
+ }
33
+
34
+ /**
35
+ * Helper functions to determine what actions are available in each state
36
+ */
37
+ export const CallStateHelpers = {
38
+ /**
39
+ * Can the call be answered in this state?
40
+ */
41
+ canAnswer(state: TelnyxCallState): boolean {
42
+ return state === TelnyxCallState.RINGING;
43
+ },
44
+
45
+ /**
46
+ * Can the call be hung up in this state?
47
+ */
48
+ canHangup(state: TelnyxCallState): boolean {
49
+ return (
50
+ state === TelnyxCallState.RINGING ||
51
+ state === TelnyxCallState.CONNECTING ||
52
+ state === TelnyxCallState.ACTIVE ||
53
+ state === TelnyxCallState.HELD
54
+ );
55
+ },
56
+
57
+ /**
58
+ * Can the call be put on hold in this state?
59
+ */
60
+ canHold(state: TelnyxCallState): boolean {
61
+ return state === TelnyxCallState.ACTIVE;
62
+ },
63
+
64
+ /**
65
+ * Can the call be resumed from hold in this state?
66
+ */
67
+ canResume(state: TelnyxCallState): boolean {
68
+ return state === TelnyxCallState.HELD;
69
+ },
70
+
71
+ /**
72
+ * Can the call be muted/unmuted in this state?
73
+ */
74
+ canToggleMute(state: TelnyxCallState): boolean {
75
+ return state === TelnyxCallState.ACTIVE || state === TelnyxCallState.HELD;
76
+ },
77
+
78
+ /**
79
+ * Is the call in a terminated state?
80
+ */
81
+ isTerminated(state: TelnyxCallState): boolean {
82
+ return state === TelnyxCallState.ENDED || state === TelnyxCallState.FAILED;
83
+ },
84
+
85
+ /**
86
+ * Is the call in an active state (can have media)?
87
+ */
88
+ isActive(state: TelnyxCallState): boolean {
89
+ return state === TelnyxCallState.ACTIVE || state === TelnyxCallState.HELD;
90
+ },
91
+
92
+ /**
93
+ * Is the call in a connecting state (answered but not yet active)?
94
+ */
95
+ isConnecting(state: TelnyxCallState): boolean {
96
+ return state === TelnyxCallState.CONNECTING;
97
+ },
98
+ };