@telnyx/react-voice-commons-sdk 0.4.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +28 -7
- package/android/src/main/java/com/telnyx/react_voice_commons/CallForegroundService.kt +16 -2
- package/android/src/main/java/com/telnyx/react_voice_commons/TelnyxFirebaseMessagingService.kt +16 -2
- package/android/src/main/java/com/telnyx/react_voice_commons/TelnyxNotificationHelper.kt +345 -24
- package/android/src/main/java/com/telnyx/react_voice_commons/VoicePnBridgeModule.kt +74 -1
- package/android/src/main/java/com/telnyx/react_voice_commons/VoicePnManager.kt +36 -1
- package/ios/CallKitBridge.m +20 -1
- package/ios/CallKitBridge.swift +935 -60
- package/ios/README.md +2 -2
- package/ios/VoicePnBridge.m +21 -1
- package/ios/VoicePnBridge.swift +62 -1
- package/lib/callkit/callkit-coordinator.d.ts +39 -3
- package/lib/callkit/callkit-coordinator.js +561 -148
- package/lib/callkit/callkit.d.ts +7 -0
- package/lib/callkit/callkit.js +83 -11
- package/lib/hooks/useAppStateHandler.js +6 -2
- package/lib/index.d.ts +1 -0
- package/lib/index.js +9 -0
- package/lib/internal/CallKitHandler.d.ts +3 -0
- package/lib/internal/CallKitHandler.js +52 -46
- package/lib/internal/calls/call-state-controller.d.ts +39 -1
- package/lib/internal/calls/call-state-controller.js +152 -64
- package/lib/internal/session/session-manager.d.ts +16 -1
- package/lib/internal/session/session-manager.js +229 -58
- package/lib/internal/voice-pn-bridge.d.ts +44 -0
- package/lib/internal/voice-pn-bridge.js +94 -0
- package/lib/models/call.js +35 -0
- package/lib/models/config.d.ts +18 -0
- package/lib/models/config.js +2 -0
- package/lib/telnyx-voice-app.js +85 -43
- package/lib/telnyx-voip-client.d.ts +48 -5
- package/lib/telnyx-voip-client.js +276 -22
- package/package.json +7 -5
- package/src/callkit/callkit-coordinator.ts +642 -160
- package/src/callkit/callkit.ts +110 -12
- package/src/hooks/useAppStateHandler.ts +7 -2
- package/src/index.ts +3 -0
- package/src/internal/CallKitHandler.tsx +60 -55
- package/src/internal/calls/call-state-controller.ts +173 -74
- package/src/internal/session/session-manager.ts +277 -61
- package/src/internal/voice-pn-bridge.ts +111 -0
- package/src/models/call.ts +34 -0
- package/src/models/config.ts +18 -0
- package/src/telnyx-voice-app.tsx +96 -45
- package/src/telnyx-voip-client.ts +260 -24
- package/src/types/telnyx-sdk.d.ts +12 -1
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
2
3
|
import { TelnyxConnectionState } from './models/connection-state';
|
|
3
4
|
import { Call } from './models/call';
|
|
4
5
|
import { TelnyxCallState } from './models/call-state';
|
|
5
6
|
import { Config, CredentialConfig, TokenConfig, validateConfig } from './models/config';
|
|
6
7
|
import { SessionManager } from './internal/session/session-manager';
|
|
7
|
-
import { CallStateController } from './internal/calls/call-state-controller';
|
|
8
|
+
import { CallStateController, type CustomHeaders } from './internal/calls/call-state-controller';
|
|
8
9
|
import { VoicePnBridge } from './internal/voice-pn-bridge';
|
|
9
10
|
|
|
11
|
+
const USE_TRICKLE_ICE_STORAGE_KEY = '@use_trickle_ice';
|
|
12
|
+
const PUSH_WHEN_ACTIVE_STORAGE_KEY = '@push_when_active';
|
|
13
|
+
const MISSED_CALL_NOTIFICATIONS_STORAGE_KEY = '@enable_missed_call_notifications';
|
|
14
|
+
const LEGACY_CALLKIT_ANSWER_KEY = '__legacy_callkit_answer__';
|
|
15
|
+
|
|
16
|
+
interface PendingCallKitAnswer {
|
|
17
|
+
callKitUUIDOrHeaders?: string | Record<string, string>;
|
|
18
|
+
customHeaders: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
|
|
10
21
|
/**
|
|
11
22
|
* Configuration options for TelnyxVoipClient
|
|
12
23
|
*/
|
|
@@ -16,6 +27,9 @@ export interface TelnyxVoipClientOptions {
|
|
|
16
27
|
|
|
17
28
|
/** Enable debug logging */
|
|
18
29
|
debug?: boolean;
|
|
30
|
+
|
|
31
|
+
/** Enable Trickle ICE for calls created by this client */
|
|
32
|
+
useTrickleIce?: boolean;
|
|
19
33
|
}
|
|
20
34
|
|
|
21
35
|
/**
|
|
@@ -33,7 +47,9 @@ export class TelnyxVoipClient {
|
|
|
33
47
|
private readonly _sessionManager: SessionManager;
|
|
34
48
|
private readonly _callStateController: CallStateController;
|
|
35
49
|
private readonly _options: Required<TelnyxVoipClientOptions>;
|
|
50
|
+
private readonly _pendingCallKitAnswers = new Map<string, PendingCallKitAnswer>();
|
|
36
51
|
private _disposed = false;
|
|
52
|
+
private _disposePromise?: Promise<void>;
|
|
37
53
|
|
|
38
54
|
/**
|
|
39
55
|
* Check if the app was launched from a push notification.
|
|
@@ -67,6 +83,7 @@ export class TelnyxVoipClient {
|
|
|
67
83
|
this._options = {
|
|
68
84
|
enableAppStateManagement: true,
|
|
69
85
|
debug: false,
|
|
86
|
+
useTrickleIce: false,
|
|
70
87
|
...options,
|
|
71
88
|
};
|
|
72
89
|
|
|
@@ -79,6 +96,7 @@ export class TelnyxVoipClient {
|
|
|
79
96
|
console.log(
|
|
80
97
|
'🔧 TelnyxVoipClient: Client ready, initializing call state controller listeners'
|
|
81
98
|
);
|
|
99
|
+
this._flushPendingCallKitAnswers();
|
|
82
100
|
this._callStateController.initializeClientListeners();
|
|
83
101
|
});
|
|
84
102
|
|
|
@@ -181,6 +199,71 @@ export class TelnyxVoipClient {
|
|
|
181
199
|
return this._callStateController.getCall(callId);
|
|
182
200
|
}
|
|
183
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Explicitly set the active call for multi-call scenarios.
|
|
204
|
+
* @param callId The ID of the call to mark as active
|
|
205
|
+
*/
|
|
206
|
+
setActiveCall(callId: string): void {
|
|
207
|
+
this._throwIfDisposed();
|
|
208
|
+
this._callStateController.setActiveCall(callId);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Clear the explicitly selected active call and return to default selection.
|
|
213
|
+
*/
|
|
214
|
+
clearActiveCall(): void {
|
|
215
|
+
this._throwIfDisposed();
|
|
216
|
+
this._callStateController.clearActiveCall();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Swap the current active call with a held call.
|
|
221
|
+
* On iOS this is coordinated through CallKit so native and SDK state stay aligned.
|
|
222
|
+
*
|
|
223
|
+
* @param targetCallId ID of the held call to make active
|
|
224
|
+
*/
|
|
225
|
+
async swapCalls(targetCallId: string): Promise<void> {
|
|
226
|
+
this._throwIfDisposed();
|
|
227
|
+
|
|
228
|
+
const activeCall = this.currentActiveCall;
|
|
229
|
+
const heldCall = this.getCall(targetCallId);
|
|
230
|
+
|
|
231
|
+
if (!activeCall || !heldCall || activeCall.callId === heldCall.callId) {
|
|
232
|
+
throw new Error('An active call and a different held call are required to swap calls');
|
|
233
|
+
}
|
|
234
|
+
if (activeCall.currentState !== TelnyxCallState.ACTIVE) {
|
|
235
|
+
throw new Error(`Cannot swap active call in state: ${activeCall.currentState}`);
|
|
236
|
+
}
|
|
237
|
+
if (heldCall.currentState !== TelnyxCallState.HELD) {
|
|
238
|
+
throw new Error(`Cannot swap target call in state: ${heldCall.currentState}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (Platform.OS === 'ios') {
|
|
242
|
+
const { callKitCoordinator } = await import('./callkit/callkit-coordinator');
|
|
243
|
+
if (callKitCoordinator.isAvailable()) {
|
|
244
|
+
const success = await callKitCoordinator.swapCallsFromUI(
|
|
245
|
+
activeCall.telnyxCall,
|
|
246
|
+
heldCall.telnyxCall
|
|
247
|
+
);
|
|
248
|
+
if (!success) {
|
|
249
|
+
throw new Error('CallKit failed to swap calls');
|
|
250
|
+
}
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
await activeCall.hold();
|
|
256
|
+
try {
|
|
257
|
+
await heldCall.resume();
|
|
258
|
+
this.setActiveCall(heldCall.callId);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
await activeCall.resume().catch((resumeError) => {
|
|
261
|
+
console.error('Failed to restore active call after swap failure', resumeError);
|
|
262
|
+
});
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
184
267
|
/**
|
|
185
268
|
* Current session ID (UUID) for this connection.
|
|
186
269
|
*/
|
|
@@ -218,10 +301,17 @@ export class TelnyxVoipClient {
|
|
|
218
301
|
console.log('TelnyxVoipClient: Logging in with credentials for user:', config.sipUser);
|
|
219
302
|
}
|
|
220
303
|
|
|
304
|
+
const loginConfig = await this._withNativeVoipPushToken({
|
|
305
|
+
...config,
|
|
306
|
+
useTrickleIce: config.useTrickleIce ?? this._options.useTrickleIce,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
await VoicePnBridge.setIncomingCallRingtone(loginConfig.incomingCallRingtone);
|
|
310
|
+
|
|
221
311
|
// Store credentials for future reconnection
|
|
222
|
-
await this._storeCredentials(
|
|
312
|
+
await this._storeCredentials(loginConfig);
|
|
223
313
|
|
|
224
|
-
await this._sessionManager.connectWithCredential(
|
|
314
|
+
await this._sessionManager.connectWithCredential(loginConfig);
|
|
225
315
|
}
|
|
226
316
|
|
|
227
317
|
/**
|
|
@@ -245,10 +335,17 @@ export class TelnyxVoipClient {
|
|
|
245
335
|
console.log('TelnyxVoipClient: Logging in with token');
|
|
246
336
|
}
|
|
247
337
|
|
|
338
|
+
const loginConfig = await this._withNativeVoipPushToken({
|
|
339
|
+
...config,
|
|
340
|
+
useTrickleIce: config.useTrickleIce ?? this._options.useTrickleIce,
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
await VoicePnBridge.setIncomingCallRingtone(loginConfig.incomingCallRingtone);
|
|
344
|
+
|
|
248
345
|
// Store token for future reconnection
|
|
249
|
-
await this._storeToken(
|
|
346
|
+
await this._storeToken(loginConfig);
|
|
250
347
|
|
|
251
|
-
await this._sessionManager.connectWithToken(
|
|
348
|
+
await this._sessionManager.connectWithToken(loginConfig);
|
|
252
349
|
}
|
|
253
350
|
|
|
254
351
|
/**
|
|
@@ -292,13 +389,32 @@ export class TelnyxVoipClient {
|
|
|
292
389
|
const storedPassword = await AsyncStorage.getItem('@telnyx_password');
|
|
293
390
|
const storedCredentialToken = await AsyncStorage.getItem('@credential_token');
|
|
294
391
|
const storedPushToken = await AsyncStorage.getItem('@push_token');
|
|
392
|
+
const storedUseTrickleIce = await AsyncStorage.getItem(USE_TRICKLE_ICE_STORAGE_KEY);
|
|
393
|
+
const storedPushWhenActive = await AsyncStorage.getItem(PUSH_WHEN_ACTIVE_STORAGE_KEY);
|
|
394
|
+
const storedMissedCallNotifications = await AsyncStorage.getItem(
|
|
395
|
+
MISSED_CALL_NOTIFICATIONS_STORAGE_KEY
|
|
396
|
+
);
|
|
397
|
+
const useTrickleIce =
|
|
398
|
+
storedUseTrickleIce === null ? this._options.useTrickleIce : storedUseTrickleIce === 'true';
|
|
399
|
+
const pushWhenActive = storedPushWhenActive === 'true';
|
|
400
|
+
const enableMissedCallNotifications = storedMissedCallNotifications === 'true';
|
|
401
|
+
const nativePushToken =
|
|
402
|
+
Platform.OS === 'ios' ? (await VoicePnBridge.getVoipToken())?.trim() : null;
|
|
403
|
+
const pushNotificationDeviceToken = nativePushToken || storedPushToken;
|
|
404
|
+
|
|
405
|
+
if (nativePushToken && nativePushToken !== storedPushToken) {
|
|
406
|
+
await AsyncStorage.setItem('@push_token', nativePushToken);
|
|
407
|
+
}
|
|
295
408
|
|
|
296
409
|
// Check if we have credential-based authentication data
|
|
297
410
|
if (storedUsername && storedPassword) {
|
|
298
411
|
// Create credential config from stored data
|
|
299
412
|
const { createCredentialConfig } = require('./models/config');
|
|
300
413
|
const config = createCredentialConfig(storedUsername, storedPassword, {
|
|
301
|
-
pushNotificationDeviceToken
|
|
414
|
+
pushNotificationDeviceToken,
|
|
415
|
+
useTrickleIce,
|
|
416
|
+
pushWhenActive,
|
|
417
|
+
enableMissedCallNotifications,
|
|
302
418
|
});
|
|
303
419
|
|
|
304
420
|
if (this._options.debug) {
|
|
@@ -317,7 +433,10 @@ export class TelnyxVoipClient {
|
|
|
317
433
|
// Create token config from stored data
|
|
318
434
|
const { createTokenConfig } = require('./models/config');
|
|
319
435
|
const config = createTokenConfig(storedCredentialToken, {
|
|
320
|
-
pushNotificationDeviceToken
|
|
436
|
+
pushNotificationDeviceToken,
|
|
437
|
+
useTrickleIce,
|
|
438
|
+
pushWhenActive,
|
|
439
|
+
enableMissedCallNotifications,
|
|
321
440
|
});
|
|
322
441
|
|
|
323
442
|
if (this._options.debug) {
|
|
@@ -358,7 +477,7 @@ export class TelnyxVoipClient {
|
|
|
358
477
|
destination: string,
|
|
359
478
|
callerName?: string,
|
|
360
479
|
callerNumber?: string,
|
|
361
|
-
customHeaders?:
|
|
480
|
+
customHeaders?: CustomHeaders
|
|
362
481
|
): Promise<Call> {
|
|
363
482
|
this._throwIfDisposed();
|
|
364
483
|
|
|
@@ -456,20 +575,35 @@ export class TelnyxVoipClient {
|
|
|
456
575
|
* This should be called when the user answers from CallKit before the socket connection is established
|
|
457
576
|
* @param customHeaders Optional custom headers to include with the answer
|
|
458
577
|
*/
|
|
459
|
-
queueAnswerFromCallKit(
|
|
578
|
+
queueAnswerFromCallKit(
|
|
579
|
+
callKitUUIDOrHeaders?: string | Record<string, string>,
|
|
580
|
+
customHeaders: Record<string, string> = {}
|
|
581
|
+
): void {
|
|
460
582
|
this._throwIfDisposed();
|
|
461
583
|
|
|
462
584
|
if (this._options.debug) {
|
|
463
|
-
console.log('TelnyxVoipClient: Queuing answer action from CallKit',
|
|
585
|
+
console.log('TelnyxVoipClient: Queuing answer action from CallKit', {
|
|
586
|
+
callKitUUIDOrHeaders,
|
|
587
|
+
customHeaders,
|
|
588
|
+
});
|
|
464
589
|
}
|
|
465
590
|
|
|
466
591
|
const telnyxClient = this._sessionManager.telnyxClient;
|
|
467
592
|
if (telnyxClient && typeof (telnyxClient as any).queueAnswerFromCallKit === 'function') {
|
|
468
|
-
(telnyxClient as any).queueAnswerFromCallKit(customHeaders);
|
|
593
|
+
(telnyxClient as any).queueAnswerFromCallKit(callKitUUIDOrHeaders, customHeaders);
|
|
469
594
|
} else {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
595
|
+
const normalizedUUID =
|
|
596
|
+
typeof callKitUUIDOrHeaders === 'string' ? callKitUUIDOrHeaders.toLowerCase() : undefined;
|
|
597
|
+
const actionKey = normalizedUUID ?? LEGACY_CALLKIT_ANSWER_KEY;
|
|
598
|
+
let retainedUUIDOrHeaders: string | Record<string, string> | undefined = normalizedUUID;
|
|
599
|
+
if (callKitUUIDOrHeaders && typeof callKitUUIDOrHeaders !== 'string') {
|
|
600
|
+
retainedUUIDOrHeaders = { ...callKitUUIDOrHeaders };
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
this._pendingCallKitAnswers.set(actionKey, {
|
|
604
|
+
callKitUUIDOrHeaders: retainedUUIDOrHeaders,
|
|
605
|
+
customHeaders: { ...customHeaders },
|
|
606
|
+
});
|
|
473
607
|
}
|
|
474
608
|
}
|
|
475
609
|
|
|
@@ -477,7 +611,7 @@ export class TelnyxVoipClient {
|
|
|
477
611
|
* Queue an end action for when the call invite arrives (for CallKit integration)
|
|
478
612
|
* This should be called when the user ends from CallKit before the socket connection is established
|
|
479
613
|
*/
|
|
480
|
-
queueEndFromCallKit(): void {
|
|
614
|
+
queueEndFromCallKit(callKitUUID?: string): void {
|
|
481
615
|
this._throwIfDisposed();
|
|
482
616
|
|
|
483
617
|
if (this._options.debug) {
|
|
@@ -486,7 +620,7 @@ export class TelnyxVoipClient {
|
|
|
486
620
|
|
|
487
621
|
const telnyxClient = this._sessionManager.telnyxClient;
|
|
488
622
|
if (telnyxClient && typeof (telnyxClient as any).queueEndFromCallKit === 'function') {
|
|
489
|
-
(telnyxClient as any).queueEndFromCallKit();
|
|
623
|
+
(telnyxClient as any).queueEndFromCallKit(callKitUUID);
|
|
490
624
|
} else {
|
|
491
625
|
console.warn(
|
|
492
626
|
'TelnyxVoipClient: TelnyxRTC client not available or method not found for queueEndFromCallKit'
|
|
@@ -494,6 +628,21 @@ export class TelnyxVoipClient {
|
|
|
494
628
|
}
|
|
495
629
|
}
|
|
496
630
|
|
|
631
|
+
/**
|
|
632
|
+
* Associate the next push-delivered INVITE with its app-facing CallKit UUID.
|
|
633
|
+
* The underlying signaling call ID remains unchanged.
|
|
634
|
+
* @internal
|
|
635
|
+
*/
|
|
636
|
+
setPushNotificationCallKitUUID(callKitUUID: string | null): void {
|
|
637
|
+
const telnyxClient = this._sessionManager.telnyxClient;
|
|
638
|
+
if (
|
|
639
|
+
telnyxClient &&
|
|
640
|
+
typeof (telnyxClient as any).setPushNotificationCallKitUUID === 'function'
|
|
641
|
+
) {
|
|
642
|
+
(telnyxClient as any).setPushNotificationCallKitUUID(callKitUUID);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
497
646
|
// ========== Lifecycle Methods ==========
|
|
498
647
|
|
|
499
648
|
/**
|
|
@@ -503,9 +652,9 @@ export class TelnyxVoipClient {
|
|
|
503
652
|
* This is particularly important for background clients that should be
|
|
504
653
|
* disposed after handling push notifications.
|
|
505
654
|
*/
|
|
506
|
-
dispose(): void {
|
|
507
|
-
if (this.
|
|
508
|
-
return;
|
|
655
|
+
async dispose(): Promise<void> {
|
|
656
|
+
if (this._disposePromise) {
|
|
657
|
+
return this._disposePromise;
|
|
509
658
|
}
|
|
510
659
|
|
|
511
660
|
if (this._options.debug) {
|
|
@@ -513,12 +662,73 @@ export class TelnyxVoipClient {
|
|
|
513
662
|
}
|
|
514
663
|
|
|
515
664
|
this._disposed = true;
|
|
516
|
-
this.
|
|
517
|
-
|
|
665
|
+
this._disposePromise = (async () => {
|
|
666
|
+
try {
|
|
667
|
+
await this._sessionManager.dispose();
|
|
668
|
+
} finally {
|
|
669
|
+
this._pendingCallKitAnswers.clear();
|
|
670
|
+
this._callStateController.dispose();
|
|
671
|
+
}
|
|
672
|
+
})();
|
|
673
|
+
|
|
674
|
+
return this._disposePromise;
|
|
518
675
|
}
|
|
519
676
|
|
|
520
677
|
// ========== Private Methods ==========
|
|
521
678
|
|
|
679
|
+
/**
|
|
680
|
+
* Forward answers captured during cold start as soon as SessionManager has
|
|
681
|
+
* created the TelnyxRTC instance. SessionManager invokes its ready callback
|
|
682
|
+
* before connect(), so the UUID-keyed action is present when the INVITE
|
|
683
|
+
* arrives.
|
|
684
|
+
*/
|
|
685
|
+
private _flushPendingCallKitAnswers(): void {
|
|
686
|
+
const telnyxClient = this._sessionManager.telnyxClient;
|
|
687
|
+
if (!telnyxClient || typeof (telnyxClient as any).queueAnswerFromCallKit !== 'function') {
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
for (const [actionKey, pendingAnswer] of this._pendingCallKitAnswers) {
|
|
692
|
+
try {
|
|
693
|
+
(telnyxClient as any).queueAnswerFromCallKit(
|
|
694
|
+
pendingAnswer.callKitUUIDOrHeaders,
|
|
695
|
+
pendingAnswer.customHeaders
|
|
696
|
+
);
|
|
697
|
+
this._pendingCallKitAnswers.delete(actionKey);
|
|
698
|
+
} catch (error) {
|
|
699
|
+
console.error('TelnyxVoipClient: Failed to restore pending CallKit answer', {
|
|
700
|
+
actionKey,
|
|
701
|
+
error,
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Prefer an explicitly supplied token, otherwise hydrate it from PushKit's
|
|
709
|
+
* native storage. PushKit registration starts in AppDelegate before React
|
|
710
|
+
* mounts, so this removes the race between the JS token event and login.
|
|
711
|
+
*/
|
|
712
|
+
private async _withNativeVoipPushToken<T extends Config>(config: T): Promise<T> {
|
|
713
|
+
if (Platform.OS !== 'ios' || config.pushNotificationDeviceToken?.trim()) {
|
|
714
|
+
return config;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const nativePushToken = (await VoicePnBridge.getVoipToken())?.trim();
|
|
718
|
+
if (!nativePushToken) {
|
|
719
|
+
return config;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
if (this._options.debug) {
|
|
723
|
+
console.log('TelnyxVoipClient: Using PushKit token from native storage');
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
return {
|
|
727
|
+
...config,
|
|
728
|
+
pushNotificationDeviceToken: nativePushToken,
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
|
|
522
732
|
/**
|
|
523
733
|
* Store credential configuration for automatic reconnection
|
|
524
734
|
*/
|
|
@@ -528,6 +738,18 @@ export class TelnyxVoipClient {
|
|
|
528
738
|
|
|
529
739
|
await AsyncStorage.setItem('@telnyx_username', config.sipUser);
|
|
530
740
|
await AsyncStorage.setItem('@telnyx_password', config.sipPassword);
|
|
741
|
+
await AsyncStorage.setItem(
|
|
742
|
+
USE_TRICKLE_ICE_STORAGE_KEY,
|
|
743
|
+
String(config.useTrickleIce ?? false)
|
|
744
|
+
);
|
|
745
|
+
await AsyncStorage.setItem(
|
|
746
|
+
PUSH_WHEN_ACTIVE_STORAGE_KEY,
|
|
747
|
+
String(config.pushWhenActive ?? false)
|
|
748
|
+
);
|
|
749
|
+
await AsyncStorage.setItem(
|
|
750
|
+
MISSED_CALL_NOTIFICATIONS_STORAGE_KEY,
|
|
751
|
+
String(config.enableMissedCallNotifications ?? false)
|
|
752
|
+
);
|
|
531
753
|
|
|
532
754
|
if (config.pushNotificationDeviceToken) {
|
|
533
755
|
await AsyncStorage.setItem('@push_token', config.pushNotificationDeviceToken);
|
|
@@ -555,6 +777,18 @@ export class TelnyxVoipClient {
|
|
|
555
777
|
const AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
556
778
|
|
|
557
779
|
await AsyncStorage.setItem('@credential_token', config.token);
|
|
780
|
+
await AsyncStorage.setItem(
|
|
781
|
+
USE_TRICKLE_ICE_STORAGE_KEY,
|
|
782
|
+
String(config.useTrickleIce ?? false)
|
|
783
|
+
);
|
|
784
|
+
await AsyncStorage.setItem(
|
|
785
|
+
PUSH_WHEN_ACTIVE_STORAGE_KEY,
|
|
786
|
+
String(config.pushWhenActive ?? false)
|
|
787
|
+
);
|
|
788
|
+
await AsyncStorage.setItem(
|
|
789
|
+
MISSED_CALL_NOTIFICATIONS_STORAGE_KEY,
|
|
790
|
+
String(config.enableMissedCallNotifications ?? false)
|
|
791
|
+
);
|
|
558
792
|
|
|
559
793
|
if (config.pushNotificationDeviceToken) {
|
|
560
794
|
await AsyncStorage.setItem('@push_token', config.pushNotificationDeviceToken);
|
|
@@ -610,10 +844,12 @@ export function createTelnyxVoipClient(options?: TelnyxVoipClientOptions): Telny
|
|
|
610
844
|
* Disposes the current singleton so that a subsequent call to
|
|
611
845
|
* `createTelnyxVoipClient()` will create a fresh instance.
|
|
612
846
|
*/
|
|
613
|
-
export function destroyTelnyxVoipClient(): void {
|
|
614
|
-
|
|
615
|
-
|
|
847
|
+
export async function destroyTelnyxVoipClient(): Promise<void> {
|
|
848
|
+
const sharedInstance = _sharedInstance;
|
|
849
|
+
|
|
850
|
+
if (sharedInstance) {
|
|
616
851
|
_sharedInstance = null;
|
|
852
|
+
await sharedInstance.dispose();
|
|
617
853
|
}
|
|
618
854
|
}
|
|
619
855
|
|
|
@@ -9,11 +9,19 @@ declare module '@telnyx/react-native-voice-sdk' {
|
|
|
9
9
|
export interface CallOptions {
|
|
10
10
|
callerIdName?: string;
|
|
11
11
|
callerIdNumber?: string;
|
|
12
|
-
customHeaders?:
|
|
12
|
+
customHeaders?: { name: string; value: string }[];
|
|
13
13
|
clientState?: string;
|
|
14
14
|
destinationNumber?: string;
|
|
15
15
|
audio?: boolean;
|
|
16
16
|
video?: boolean;
|
|
17
|
+
peerConnectionOptions?: {
|
|
18
|
+
prefetchIceCandidates?: boolean;
|
|
19
|
+
useTrickleIce?: boolean;
|
|
20
|
+
iceServers?: any[];
|
|
21
|
+
iceTransportPolicy?: string;
|
|
22
|
+
bundlePolicy?: string;
|
|
23
|
+
rtcpMuxPolicy?: string;
|
|
24
|
+
};
|
|
17
25
|
}
|
|
18
26
|
|
|
19
27
|
export interface ClientOptions {
|
|
@@ -26,6 +34,9 @@ declare module '@telnyx/react-native-voice-sdk' {
|
|
|
26
34
|
debug?: boolean;
|
|
27
35
|
logLevel?: string;
|
|
28
36
|
pushNotificationDeviceToken?: string;
|
|
37
|
+
pushWhenActive?: boolean;
|
|
38
|
+
enableMissedCallNotifications?: boolean;
|
|
39
|
+
useTrickleIce?: boolean;
|
|
29
40
|
enableCallReports?: boolean;
|
|
30
41
|
callReportInterval?: number;
|
|
31
42
|
callReportLogLevel?: string;
|