@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,15 +1,72 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
var __createBinding =
|
|
3
|
+
(this && this.__createBinding) ||
|
|
4
|
+
(Object.create
|
|
5
|
+
? function (o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get: function () {
|
|
12
|
+
return m[k];
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}
|
|
18
|
+
: function (o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
});
|
|
22
|
+
var __setModuleDefault =
|
|
23
|
+
(this && this.__setModuleDefault) ||
|
|
24
|
+
(Object.create
|
|
25
|
+
? function (o, v) {
|
|
26
|
+
Object.defineProperty(o, 'default', { enumerable: true, value: v });
|
|
27
|
+
}
|
|
28
|
+
: function (o, v) {
|
|
29
|
+
o['default'] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar =
|
|
32
|
+
(this && this.__importStar) ||
|
|
33
|
+
(function () {
|
|
34
|
+
var ownKeys = function (o) {
|
|
35
|
+
ownKeys =
|
|
36
|
+
Object.getOwnPropertyNames ||
|
|
37
|
+
function (o) {
|
|
38
|
+
var ar = [];
|
|
39
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
40
|
+
return ar;
|
|
41
|
+
};
|
|
42
|
+
return ownKeys(o);
|
|
43
|
+
};
|
|
44
|
+
return function (mod) {
|
|
45
|
+
if (mod && mod.__esModule) return mod;
|
|
46
|
+
var result = {};
|
|
47
|
+
if (mod != null)
|
|
48
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++)
|
|
49
|
+
if (k[i] !== 'default') __createBinding(result, mod, k[i]);
|
|
50
|
+
__setModuleDefault(result, mod);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
})();
|
|
2
54
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
55
|
exports.TelnyxVoipClient = void 0;
|
|
4
56
|
exports.createTelnyxVoipClient = createTelnyxVoipClient;
|
|
5
57
|
exports.destroyTelnyxVoipClient = destroyTelnyxVoipClient;
|
|
6
58
|
exports.createBackgroundTelnyxVoipClient = createBackgroundTelnyxVoipClient;
|
|
59
|
+
const react_native_1 = require('react-native');
|
|
7
60
|
const connection_state_1 = require('./models/connection-state');
|
|
8
61
|
const call_state_1 = require('./models/call-state');
|
|
9
62
|
const config_1 = require('./models/config');
|
|
10
63
|
const session_manager_1 = require('./internal/session/session-manager');
|
|
11
64
|
const call_state_controller_1 = require('./internal/calls/call-state-controller');
|
|
12
65
|
const voice_pn_bridge_1 = require('./internal/voice-pn-bridge');
|
|
66
|
+
const USE_TRICKLE_ICE_STORAGE_KEY = '@use_trickle_ice';
|
|
67
|
+
const PUSH_WHEN_ACTIVE_STORAGE_KEY = '@push_when_active';
|
|
68
|
+
const MISSED_CALL_NOTIFICATIONS_STORAGE_KEY = '@enable_missed_call_notifications';
|
|
69
|
+
const LEGACY_CALLKIT_ANSWER_KEY = '__legacy_callkit_answer__';
|
|
13
70
|
/**
|
|
14
71
|
* The main public interface for the react-voice-commons module.
|
|
15
72
|
*
|
|
@@ -48,10 +105,12 @@ class TelnyxVoipClient {
|
|
|
48
105
|
* @param options Configuration options for the client
|
|
49
106
|
*/
|
|
50
107
|
constructor(options = {}) {
|
|
108
|
+
this._pendingCallKitAnswers = new Map();
|
|
51
109
|
this._disposed = false;
|
|
52
110
|
this._options = {
|
|
53
111
|
enableAppStateManagement: true,
|
|
54
112
|
debug: false,
|
|
113
|
+
useTrickleIce: false,
|
|
55
114
|
...options,
|
|
56
115
|
};
|
|
57
116
|
// Initialize core components
|
|
@@ -64,6 +123,7 @@ class TelnyxVoipClient {
|
|
|
64
123
|
console.log(
|
|
65
124
|
'🔧 TelnyxVoipClient: Client ready, initializing call state controller listeners'
|
|
66
125
|
);
|
|
126
|
+
this._flushPendingCallKitAnswers();
|
|
67
127
|
this._callStateController.initializeClientListeners();
|
|
68
128
|
});
|
|
69
129
|
// Clear any tracked calls when the session disconnects, so ghosts
|
|
@@ -154,6 +214,66 @@ class TelnyxVoipClient {
|
|
|
154
214
|
getCall(callId) {
|
|
155
215
|
return this._callStateController.getCall(callId);
|
|
156
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Explicitly set the active call for multi-call scenarios.
|
|
219
|
+
* @param callId The ID of the call to mark as active
|
|
220
|
+
*/
|
|
221
|
+
setActiveCall(callId) {
|
|
222
|
+
this._throwIfDisposed();
|
|
223
|
+
this._callStateController.setActiveCall(callId);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Clear the explicitly selected active call and return to default selection.
|
|
227
|
+
*/
|
|
228
|
+
clearActiveCall() {
|
|
229
|
+
this._throwIfDisposed();
|
|
230
|
+
this._callStateController.clearActiveCall();
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Swap the current active call with a held call.
|
|
234
|
+
* On iOS this is coordinated through CallKit so native and SDK state stay aligned.
|
|
235
|
+
*
|
|
236
|
+
* @param targetCallId ID of the held call to make active
|
|
237
|
+
*/
|
|
238
|
+
async swapCalls(targetCallId) {
|
|
239
|
+
this._throwIfDisposed();
|
|
240
|
+
const activeCall = this.currentActiveCall;
|
|
241
|
+
const heldCall = this.getCall(targetCallId);
|
|
242
|
+
if (!activeCall || !heldCall || activeCall.callId === heldCall.callId) {
|
|
243
|
+
throw new Error('An active call and a different held call are required to swap calls');
|
|
244
|
+
}
|
|
245
|
+
if (activeCall.currentState !== call_state_1.TelnyxCallState.ACTIVE) {
|
|
246
|
+
throw new Error(`Cannot swap active call in state: ${activeCall.currentState}`);
|
|
247
|
+
}
|
|
248
|
+
if (heldCall.currentState !== call_state_1.TelnyxCallState.HELD) {
|
|
249
|
+
throw new Error(`Cannot swap target call in state: ${heldCall.currentState}`);
|
|
250
|
+
}
|
|
251
|
+
if (react_native_1.Platform.OS === 'ios') {
|
|
252
|
+
const { callKitCoordinator } = await Promise.resolve().then(() =>
|
|
253
|
+
__importStar(require('./callkit/callkit-coordinator'))
|
|
254
|
+
);
|
|
255
|
+
if (callKitCoordinator.isAvailable()) {
|
|
256
|
+
const success = await callKitCoordinator.swapCallsFromUI(
|
|
257
|
+
activeCall.telnyxCall,
|
|
258
|
+
heldCall.telnyxCall
|
|
259
|
+
);
|
|
260
|
+
if (!success) {
|
|
261
|
+
throw new Error('CallKit failed to swap calls');
|
|
262
|
+
}
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
await activeCall.hold();
|
|
267
|
+
try {
|
|
268
|
+
await heldCall.resume();
|
|
269
|
+
this.setActiveCall(heldCall.callId);
|
|
270
|
+
} catch (error) {
|
|
271
|
+
await activeCall.resume().catch((resumeError) => {
|
|
272
|
+
console.error('Failed to restore active call after swap failure', resumeError);
|
|
273
|
+
});
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
157
277
|
/**
|
|
158
278
|
* Current session ID (UUID) for this connection.
|
|
159
279
|
*/
|
|
@@ -185,9 +305,14 @@ class TelnyxVoipClient {
|
|
|
185
305
|
if (this._options.debug) {
|
|
186
306
|
console.log('TelnyxVoipClient: Logging in with credentials for user:', config.sipUser);
|
|
187
307
|
}
|
|
308
|
+
const loginConfig = await this._withNativeVoipPushToken({
|
|
309
|
+
...config,
|
|
310
|
+
useTrickleIce: config.useTrickleIce ?? this._options.useTrickleIce,
|
|
311
|
+
});
|
|
312
|
+
await voice_pn_bridge_1.VoicePnBridge.setIncomingCallRingtone(loginConfig.incomingCallRingtone);
|
|
188
313
|
// Store credentials for future reconnection
|
|
189
|
-
await this._storeCredentials(
|
|
190
|
-
await this._sessionManager.connectWithCredential(
|
|
314
|
+
await this._storeCredentials(loginConfig);
|
|
315
|
+
await this._sessionManager.connectWithCredential(loginConfig);
|
|
191
316
|
}
|
|
192
317
|
/**
|
|
193
318
|
* Connects to the Telnyx platform using token authentication.
|
|
@@ -207,9 +332,14 @@ class TelnyxVoipClient {
|
|
|
207
332
|
if (this._options.debug) {
|
|
208
333
|
console.log('TelnyxVoipClient: Logging in with token');
|
|
209
334
|
}
|
|
335
|
+
const loginConfig = await this._withNativeVoipPushToken({
|
|
336
|
+
...config,
|
|
337
|
+
useTrickleIce: config.useTrickleIce ?? this._options.useTrickleIce,
|
|
338
|
+
});
|
|
339
|
+
await voice_pn_bridge_1.VoicePnBridge.setIncomingCallRingtone(loginConfig.incomingCallRingtone);
|
|
210
340
|
// Store token for future reconnection
|
|
211
|
-
await this._storeToken(
|
|
212
|
-
await this._sessionManager.connectWithToken(
|
|
341
|
+
await this._storeToken(loginConfig);
|
|
342
|
+
await this._sessionManager.connectWithToken(loginConfig);
|
|
213
343
|
}
|
|
214
344
|
/**
|
|
215
345
|
* Disconnects from the Telnyx platform.
|
|
@@ -246,12 +376,32 @@ class TelnyxVoipClient {
|
|
|
246
376
|
const storedPassword = await AsyncStorage.getItem('@telnyx_password');
|
|
247
377
|
const storedCredentialToken = await AsyncStorage.getItem('@credential_token');
|
|
248
378
|
const storedPushToken = await AsyncStorage.getItem('@push_token');
|
|
379
|
+
const storedUseTrickleIce = await AsyncStorage.getItem(USE_TRICKLE_ICE_STORAGE_KEY);
|
|
380
|
+
const storedPushWhenActive = await AsyncStorage.getItem(PUSH_WHEN_ACTIVE_STORAGE_KEY);
|
|
381
|
+
const storedMissedCallNotifications = await AsyncStorage.getItem(
|
|
382
|
+
MISSED_CALL_NOTIFICATIONS_STORAGE_KEY
|
|
383
|
+
);
|
|
384
|
+
const useTrickleIce =
|
|
385
|
+
storedUseTrickleIce === null ? this._options.useTrickleIce : storedUseTrickleIce === 'true';
|
|
386
|
+
const pushWhenActive = storedPushWhenActive === 'true';
|
|
387
|
+
const enableMissedCallNotifications = storedMissedCallNotifications === 'true';
|
|
388
|
+
const nativePushToken =
|
|
389
|
+
react_native_1.Platform.OS === 'ios'
|
|
390
|
+
? (await voice_pn_bridge_1.VoicePnBridge.getVoipToken())?.trim()
|
|
391
|
+
: null;
|
|
392
|
+
const pushNotificationDeviceToken = nativePushToken || storedPushToken;
|
|
393
|
+
if (nativePushToken && nativePushToken !== storedPushToken) {
|
|
394
|
+
await AsyncStorage.setItem('@push_token', nativePushToken);
|
|
395
|
+
}
|
|
249
396
|
// Check if we have credential-based authentication data
|
|
250
397
|
if (storedUsername && storedPassword) {
|
|
251
398
|
// Create credential config from stored data
|
|
252
399
|
const { createCredentialConfig } = require('./models/config');
|
|
253
400
|
const config = createCredentialConfig(storedUsername, storedPassword, {
|
|
254
|
-
pushNotificationDeviceToken
|
|
401
|
+
pushNotificationDeviceToken,
|
|
402
|
+
useTrickleIce,
|
|
403
|
+
pushWhenActive,
|
|
404
|
+
enableMissedCallNotifications,
|
|
255
405
|
});
|
|
256
406
|
if (this._options.debug) {
|
|
257
407
|
console.log(
|
|
@@ -267,7 +417,10 @@ class TelnyxVoipClient {
|
|
|
267
417
|
// Create token config from stored data
|
|
268
418
|
const { createTokenConfig } = require('./models/config');
|
|
269
419
|
const config = createTokenConfig(storedCredentialToken, {
|
|
270
|
-
pushNotificationDeviceToken
|
|
420
|
+
pushNotificationDeviceToken,
|
|
421
|
+
useTrickleIce,
|
|
422
|
+
pushWhenActive,
|
|
423
|
+
enableMissedCallNotifications,
|
|
271
424
|
});
|
|
272
425
|
if (this._options.debug) {
|
|
273
426
|
console.log('TelnyxVoipClient: Reconnecting with stored token');
|
|
@@ -381,38 +534,60 @@ class TelnyxVoipClient {
|
|
|
381
534
|
* This should be called when the user answers from CallKit before the socket connection is established
|
|
382
535
|
* @param customHeaders Optional custom headers to include with the answer
|
|
383
536
|
*/
|
|
384
|
-
queueAnswerFromCallKit(customHeaders = {}) {
|
|
537
|
+
queueAnswerFromCallKit(callKitUUIDOrHeaders, customHeaders = {}) {
|
|
385
538
|
this._throwIfDisposed();
|
|
386
539
|
if (this._options.debug) {
|
|
387
|
-
console.log('TelnyxVoipClient: Queuing answer action from CallKit',
|
|
540
|
+
console.log('TelnyxVoipClient: Queuing answer action from CallKit', {
|
|
541
|
+
callKitUUIDOrHeaders,
|
|
542
|
+
customHeaders,
|
|
543
|
+
});
|
|
388
544
|
}
|
|
389
545
|
const telnyxClient = this._sessionManager.telnyxClient;
|
|
390
546
|
if (telnyxClient && typeof telnyxClient.queueAnswerFromCallKit === 'function') {
|
|
391
|
-
telnyxClient.queueAnswerFromCallKit(customHeaders);
|
|
547
|
+
telnyxClient.queueAnswerFromCallKit(callKitUUIDOrHeaders, customHeaders);
|
|
392
548
|
} else {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
549
|
+
const normalizedUUID =
|
|
550
|
+
typeof callKitUUIDOrHeaders === 'string' ? callKitUUIDOrHeaders.toLowerCase() : undefined;
|
|
551
|
+
const actionKey = normalizedUUID ?? LEGACY_CALLKIT_ANSWER_KEY;
|
|
552
|
+
let retainedUUIDOrHeaders = normalizedUUID;
|
|
553
|
+
if (callKitUUIDOrHeaders && typeof callKitUUIDOrHeaders !== 'string') {
|
|
554
|
+
retainedUUIDOrHeaders = { ...callKitUUIDOrHeaders };
|
|
555
|
+
}
|
|
556
|
+
this._pendingCallKitAnswers.set(actionKey, {
|
|
557
|
+
callKitUUIDOrHeaders: retainedUUIDOrHeaders,
|
|
558
|
+
customHeaders: { ...customHeaders },
|
|
559
|
+
});
|
|
396
560
|
}
|
|
397
561
|
}
|
|
398
562
|
/**
|
|
399
563
|
* Queue an end action for when the call invite arrives (for CallKit integration)
|
|
400
564
|
* This should be called when the user ends from CallKit before the socket connection is established
|
|
401
565
|
*/
|
|
402
|
-
queueEndFromCallKit() {
|
|
566
|
+
queueEndFromCallKit(callKitUUID) {
|
|
403
567
|
this._throwIfDisposed();
|
|
404
568
|
if (this._options.debug) {
|
|
405
569
|
console.log('TelnyxVoipClient: Queuing end action from CallKit');
|
|
406
570
|
}
|
|
407
571
|
const telnyxClient = this._sessionManager.telnyxClient;
|
|
408
572
|
if (telnyxClient && typeof telnyxClient.queueEndFromCallKit === 'function') {
|
|
409
|
-
telnyxClient.queueEndFromCallKit();
|
|
573
|
+
telnyxClient.queueEndFromCallKit(callKitUUID);
|
|
410
574
|
} else {
|
|
411
575
|
console.warn(
|
|
412
576
|
'TelnyxVoipClient: TelnyxRTC client not available or method not found for queueEndFromCallKit'
|
|
413
577
|
);
|
|
414
578
|
}
|
|
415
579
|
}
|
|
580
|
+
/**
|
|
581
|
+
* Associate the next push-delivered INVITE with its app-facing CallKit UUID.
|
|
582
|
+
* The underlying signaling call ID remains unchanged.
|
|
583
|
+
* @internal
|
|
584
|
+
*/
|
|
585
|
+
setPushNotificationCallKitUUID(callKitUUID) {
|
|
586
|
+
const telnyxClient = this._sessionManager.telnyxClient;
|
|
587
|
+
if (telnyxClient && typeof telnyxClient.setPushNotificationCallKitUUID === 'function') {
|
|
588
|
+
telnyxClient.setPushNotificationCallKitUUID(callKitUUID);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
416
591
|
// ========== Lifecycle Methods ==========
|
|
417
592
|
/**
|
|
418
593
|
* Dispose of the client and clean up all resources.
|
|
@@ -421,18 +596,72 @@ class TelnyxVoipClient {
|
|
|
421
596
|
* This is particularly important for background clients that should be
|
|
422
597
|
* disposed after handling push notifications.
|
|
423
598
|
*/
|
|
424
|
-
dispose() {
|
|
425
|
-
if (this.
|
|
426
|
-
return;
|
|
599
|
+
async dispose() {
|
|
600
|
+
if (this._disposePromise) {
|
|
601
|
+
return this._disposePromise;
|
|
427
602
|
}
|
|
428
603
|
if (this._options.debug) {
|
|
429
604
|
console.log('TelnyxVoipClient: Disposing client');
|
|
430
605
|
}
|
|
431
606
|
this._disposed = true;
|
|
432
|
-
this.
|
|
433
|
-
|
|
607
|
+
this._disposePromise = (async () => {
|
|
608
|
+
try {
|
|
609
|
+
await this._sessionManager.dispose();
|
|
610
|
+
} finally {
|
|
611
|
+
this._pendingCallKitAnswers.clear();
|
|
612
|
+
this._callStateController.dispose();
|
|
613
|
+
}
|
|
614
|
+
})();
|
|
615
|
+
return this._disposePromise;
|
|
434
616
|
}
|
|
435
617
|
// ========== Private Methods ==========
|
|
618
|
+
/**
|
|
619
|
+
* Forward answers captured during cold start as soon as SessionManager has
|
|
620
|
+
* created the TelnyxRTC instance. SessionManager invokes its ready callback
|
|
621
|
+
* before connect(), so the UUID-keyed action is present when the INVITE
|
|
622
|
+
* arrives.
|
|
623
|
+
*/
|
|
624
|
+
_flushPendingCallKitAnswers() {
|
|
625
|
+
const telnyxClient = this._sessionManager.telnyxClient;
|
|
626
|
+
if (!telnyxClient || typeof telnyxClient.queueAnswerFromCallKit !== 'function') {
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
for (const [actionKey, pendingAnswer] of this._pendingCallKitAnswers) {
|
|
630
|
+
try {
|
|
631
|
+
telnyxClient.queueAnswerFromCallKit(
|
|
632
|
+
pendingAnswer.callKitUUIDOrHeaders,
|
|
633
|
+
pendingAnswer.customHeaders
|
|
634
|
+
);
|
|
635
|
+
this._pendingCallKitAnswers.delete(actionKey);
|
|
636
|
+
} catch (error) {
|
|
637
|
+
console.error('TelnyxVoipClient: Failed to restore pending CallKit answer', {
|
|
638
|
+
actionKey,
|
|
639
|
+
error,
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Prefer an explicitly supplied token, otherwise hydrate it from PushKit's
|
|
646
|
+
* native storage. PushKit registration starts in AppDelegate before React
|
|
647
|
+
* mounts, so this removes the race between the JS token event and login.
|
|
648
|
+
*/
|
|
649
|
+
async _withNativeVoipPushToken(config) {
|
|
650
|
+
if (react_native_1.Platform.OS !== 'ios' || config.pushNotificationDeviceToken?.trim()) {
|
|
651
|
+
return config;
|
|
652
|
+
}
|
|
653
|
+
const nativePushToken = (await voice_pn_bridge_1.VoicePnBridge.getVoipToken())?.trim();
|
|
654
|
+
if (!nativePushToken) {
|
|
655
|
+
return config;
|
|
656
|
+
}
|
|
657
|
+
if (this._options.debug) {
|
|
658
|
+
console.log('TelnyxVoipClient: Using PushKit token from native storage');
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
661
|
+
...config,
|
|
662
|
+
pushNotificationDeviceToken: nativePushToken,
|
|
663
|
+
};
|
|
664
|
+
}
|
|
436
665
|
/**
|
|
437
666
|
* Store credential configuration for automatic reconnection
|
|
438
667
|
*/
|
|
@@ -441,6 +670,18 @@ class TelnyxVoipClient {
|
|
|
441
670
|
const AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
442
671
|
await AsyncStorage.setItem('@telnyx_username', config.sipUser);
|
|
443
672
|
await AsyncStorage.setItem('@telnyx_password', config.sipPassword);
|
|
673
|
+
await AsyncStorage.setItem(
|
|
674
|
+
USE_TRICKLE_ICE_STORAGE_KEY,
|
|
675
|
+
String(config.useTrickleIce ?? false)
|
|
676
|
+
);
|
|
677
|
+
await AsyncStorage.setItem(
|
|
678
|
+
PUSH_WHEN_ACTIVE_STORAGE_KEY,
|
|
679
|
+
String(config.pushWhenActive ?? false)
|
|
680
|
+
);
|
|
681
|
+
await AsyncStorage.setItem(
|
|
682
|
+
MISSED_CALL_NOTIFICATIONS_STORAGE_KEY,
|
|
683
|
+
String(config.enableMissedCallNotifications ?? false)
|
|
684
|
+
);
|
|
444
685
|
if (config.pushNotificationDeviceToken) {
|
|
445
686
|
await AsyncStorage.setItem('@push_token', config.pushNotificationDeviceToken);
|
|
446
687
|
}
|
|
@@ -463,6 +704,18 @@ class TelnyxVoipClient {
|
|
|
463
704
|
try {
|
|
464
705
|
const AsyncStorage = require('@react-native-async-storage/async-storage').default;
|
|
465
706
|
await AsyncStorage.setItem('@credential_token', config.token);
|
|
707
|
+
await AsyncStorage.setItem(
|
|
708
|
+
USE_TRICKLE_ICE_STORAGE_KEY,
|
|
709
|
+
String(config.useTrickleIce ?? false)
|
|
710
|
+
);
|
|
711
|
+
await AsyncStorage.setItem(
|
|
712
|
+
PUSH_WHEN_ACTIVE_STORAGE_KEY,
|
|
713
|
+
String(config.pushWhenActive ?? false)
|
|
714
|
+
);
|
|
715
|
+
await AsyncStorage.setItem(
|
|
716
|
+
MISSED_CALL_NOTIFICATIONS_STORAGE_KEY,
|
|
717
|
+
String(config.enableMissedCallNotifications ?? false)
|
|
718
|
+
);
|
|
466
719
|
if (config.pushNotificationDeviceToken) {
|
|
467
720
|
await AsyncStorage.setItem('@push_token', config.pushNotificationDeviceToken);
|
|
468
721
|
}
|
|
@@ -511,10 +764,11 @@ function createTelnyxVoipClient(options) {
|
|
|
511
764
|
* Disposes the current singleton so that a subsequent call to
|
|
512
765
|
* `createTelnyxVoipClient()` will create a fresh instance.
|
|
513
766
|
*/
|
|
514
|
-
function destroyTelnyxVoipClient() {
|
|
515
|
-
|
|
516
|
-
|
|
767
|
+
async function destroyTelnyxVoipClient() {
|
|
768
|
+
const sharedInstance = _sharedInstance;
|
|
769
|
+
if (sharedInstance) {
|
|
517
770
|
_sharedInstance = null;
|
|
771
|
+
await sharedInstance.dispose();
|
|
518
772
|
}
|
|
519
773
|
}
|
|
520
774
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telnyx/react-voice-commons-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A high-level, state-agnostic, drop-in module for the Telnyx React Native SDK that simplifies WebRTC voice calling integration",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"dev": "tsc --watch",
|
|
31
31
|
"test": "jest",
|
|
32
32
|
"test:watch": "jest --watch",
|
|
33
|
-
"lint": "eslint src --ext .ts,.tsx",
|
|
33
|
+
"lint": "ESLINT_USE_FLAT_CONFIG=false eslint src --ext .ts,.tsx",
|
|
34
34
|
"clean": "rm -rf lib",
|
|
35
35
|
"docs": "typedoc",
|
|
36
36
|
"docs:watch": "typedoc --watch",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
88
|
"@react-native-community/eslint-config": "^3.2.0",
|
|
89
|
-
"@telnyx/react-native-voice-sdk": ">=0.
|
|
89
|
+
"@telnyx/react-native-voice-sdk": ">=1.0.0",
|
|
90
90
|
"eventemitter3": "^5.0.1",
|
|
91
91
|
"expo": "~53.0.22",
|
|
92
92
|
"react-native-url-polyfill": "^3.0.0",
|
|
@@ -98,11 +98,13 @@
|
|
|
98
98
|
"@types/jest": "^29.5.0",
|
|
99
99
|
"@types/react": "~19.0.14",
|
|
100
100
|
"@types/react-native": "^0.72.8",
|
|
101
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
102
|
-
"@typescript-eslint/parser": "^
|
|
101
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
102
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
103
103
|
"eslint": "^8.0.0",
|
|
104
|
+
"expo-router": "^5.1.0",
|
|
104
105
|
"jest": "^29.5.0",
|
|
105
106
|
"prettier": "^3.0.0",
|
|
107
|
+
"react": "^19.0.0",
|
|
106
108
|
"ts-jest": "^29.1.0",
|
|
107
109
|
"typedoc": "^0.28.14",
|
|
108
110
|
"typedoc-plugin-markdown": "^4.9.0",
|