@telnyx/react-voice-commons-sdk 0.1.2 → 0.1.3
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 +42 -0
- package/TelnyxVoiceCommons.podspec +31 -31
- package/ios/CallKitBridge.m +43 -43
- package/ios/CallKitBridge.swift +874 -879
- package/ios/VoicePnBridge.m +30 -30
- package/ios/VoicePnBridge.swift +86 -86
- package/lib/callkit/callkit-coordinator.d.ts +110 -117
- package/lib/callkit/callkit-coordinator.js +664 -727
- package/lib/callkit/callkit.d.ts +41 -41
- package/lib/callkit/callkit.js +252 -242
- package/lib/callkit/index.js +15 -47
- package/lib/callkit/use-callkit.d.ts +19 -19
- package/lib/callkit/use-callkit.js +270 -310
- package/lib/context/TelnyxVoiceContext.d.ts +9 -9
- package/lib/context/TelnyxVoiceContext.js +10 -13
- package/lib/hooks/use-callkit-coordinator.d.ts +9 -17
- package/lib/hooks/use-callkit-coordinator.js +45 -50
- package/lib/hooks/useAppReadyNotifier.js +13 -15
- package/lib/hooks/useAppStateHandler.d.ts +6 -11
- package/lib/hooks/useAppStateHandler.js +95 -110
- package/lib/hooks/useNetworkStateHandler.d.ts +0 -0
- package/lib/hooks/useNetworkStateHandler.js +0 -0
- package/lib/index.d.ts +3 -21
- package/lib/index.js +50 -201
- package/lib/internal/CallKitHandler.d.ts +6 -6
- package/lib/internal/CallKitHandler.js +96 -104
- package/lib/internal/callkit-manager.d.ts +57 -57
- package/lib/internal/callkit-manager.js +299 -316
- package/lib/internal/calls/call-state-controller.d.ts +73 -86
- package/lib/internal/calls/call-state-controller.js +263 -307
- package/lib/internal/session/session-manager.d.ts +71 -75
- package/lib/internal/session/session-manager.js +360 -424
- package/lib/internal/user-defaults-helpers.js +49 -39
- package/lib/internal/voice-pn-bridge.d.ts +114 -12
- package/lib/internal/voice-pn-bridge.js +212 -5
- package/lib/models/call-state.d.ts +46 -44
- package/lib/models/call-state.js +70 -68
- package/lib/models/call.d.ts +161 -133
- package/lib/models/call.js +454 -382
- package/lib/models/config.d.ts +11 -18
- package/lib/models/config.js +37 -35
- package/lib/models/connection-state.d.ts +10 -10
- package/lib/models/connection-state.js +16 -16
- package/lib/telnyx-voice-app.d.ts +28 -28
- package/lib/telnyx-voice-app.js +463 -481
- package/lib/telnyx-voip-client.d.ts +167 -167
- package/lib/telnyx-voip-client.js +385 -390
- package/package.json +11 -4
- package/src/callkit/callkit-coordinator.ts +18 -34
- package/src/hooks/useNetworkStateHandler.ts +0 -0
- package/src/internal/calls/call-state-controller.ts +81 -58
- package/src/internal/session/session-manager.ts +42 -26
- package/src/internal/voice-pn-bridge.ts +250 -2
- package/src/models/call-state.ts +8 -1
- package/src/models/call.ts +119 -5
- package/src/telnyx-voice-app.tsx +87 -40
- package/src/telnyx-voip-client.ts +15 -3
- package/src/types/telnyx-sdk.d.ts +16 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telnyx/react-voice-commons-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"src",
|
|
20
20
|
"ios",
|
|
21
21
|
"TelnyxVoiceCommons.podspec",
|
|
22
|
-
"README.md"
|
|
22
|
+
"README.md",
|
|
23
|
+
"CHANGELOG.md"
|
|
23
24
|
],
|
|
24
25
|
"scripts": {
|
|
25
26
|
"build": "tsc",
|
|
@@ -28,6 +29,10 @@
|
|
|
28
29
|
"test:watch": "jest --watch",
|
|
29
30
|
"lint": "eslint src --ext .ts,.tsx",
|
|
30
31
|
"clean": "rm -rf lib",
|
|
32
|
+
"docs": "typedoc",
|
|
33
|
+
"docs:watch": "typedoc --watch",
|
|
34
|
+
"docs:markdown": "typedoc --options typedoc.markdown.json",
|
|
35
|
+
"docs:all": "npm run docs && npm run docs:markdown",
|
|
31
36
|
"android": "expo run:android",
|
|
32
37
|
"ios": "expo run:ios"
|
|
33
38
|
},
|
|
@@ -55,7 +60,7 @@
|
|
|
55
60
|
"peerDependencies": {
|
|
56
61
|
"@react-native-async-storage/async-storage": "^2.1.0",
|
|
57
62
|
"expo-router": "^5.1.0",
|
|
58
|
-
"react": "
|
|
63
|
+
"react": "19.0.0",
|
|
59
64
|
"react-native": "^0.79.0",
|
|
60
65
|
"react-native-webrtc": "^124.0.5"
|
|
61
66
|
},
|
|
@@ -78,7 +83,7 @@
|
|
|
78
83
|
},
|
|
79
84
|
"dependencies": {
|
|
80
85
|
"@react-native-community/eslint-config": "^3.2.0",
|
|
81
|
-
"@telnyx/react-native-voice-sdk": "^0.1.
|
|
86
|
+
"@telnyx/react-native-voice-sdk": "^0.1.2",
|
|
82
87
|
"eventemitter3": "^5.0.1",
|
|
83
88
|
"expo": "~53.0.22",
|
|
84
89
|
"react-native-voip-push-notification": "^3.3.3",
|
|
@@ -93,6 +98,8 @@
|
|
|
93
98
|
"eslint": "^8.0.0",
|
|
94
99
|
"jest": "^29.5.0",
|
|
95
100
|
"ts-jest": "^29.1.0",
|
|
101
|
+
"typedoc": "^0.28.14",
|
|
102
|
+
"typedoc-plugin-markdown": "^4.9.0",
|
|
96
103
|
"typescript": "^5.0.0"
|
|
97
104
|
},
|
|
98
105
|
"engines": {
|
|
@@ -3,9 +3,9 @@ import CallKit, { CallEndReason } from './callkit';
|
|
|
3
3
|
import { Call } from '@telnyx/react-native-voice-sdk';
|
|
4
4
|
import { VoicePnBridge } from '../internal/voice-pn-bridge';
|
|
5
5
|
import { router } from 'expo-router';
|
|
6
|
-
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
7
6
|
import { TelnyxVoipClient } from '../telnyx-voip-client';
|
|
8
7
|
import { TelnyxConnectionState } from '../models/connection-state';
|
|
8
|
+
import { act } from 'react';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* CallKit Coordinator - Manages the proper CallKit-first flow for iOS
|
|
@@ -31,6 +31,9 @@ class CallKitCoordinator {
|
|
|
31
31
|
|
|
32
32
|
private isCallFromPush = false;
|
|
33
33
|
|
|
34
|
+
// Flag to auto-answer the next incoming call (set when answering push notifications via CallKit)
|
|
35
|
+
private shouldAutoAnswerNextCall = false;
|
|
36
|
+
|
|
34
37
|
// Reference to the VoIP client for triggering reconnection when needed
|
|
35
38
|
private voipClient: TelnyxVoipClient | null = null;
|
|
36
39
|
|
|
@@ -266,10 +269,8 @@ class CallKitCoordinator {
|
|
|
266
269
|
currentState: call.state,
|
|
267
270
|
});
|
|
268
271
|
|
|
269
|
-
if (call.state === 'active'
|
|
270
|
-
console.log(
|
|
271
|
-
'CallKitCoordinator: Call already active/connecting, skipping duplicate answer action'
|
|
272
|
-
);
|
|
272
|
+
if (call.state === 'active') {
|
|
273
|
+
console.log('CallKitCoordinator: Call already active, skipping duplicate answer action');
|
|
273
274
|
return;
|
|
274
275
|
}
|
|
275
276
|
|
|
@@ -385,7 +386,7 @@ class CallKitCoordinator {
|
|
|
385
386
|
}
|
|
386
387
|
|
|
387
388
|
/**
|
|
388
|
-
* Handle CallKit push received event
|
|
389
|
+
* Handle CallKit push received event
|
|
389
390
|
* This allows us to coordinate between the push notification and any subsequent WebRTC calls
|
|
390
391
|
*/
|
|
391
392
|
async handleCallKitPushReceived(callKitUUID: string, event?: any): Promise<void> {
|
|
@@ -438,15 +439,16 @@ class CallKitCoordinator {
|
|
|
438
439
|
};
|
|
439
440
|
|
|
440
441
|
// Check if auto-answer is set and add from_notification flag
|
|
441
|
-
const
|
|
442
|
-
const shouldAddFromNotification = autoAnswerFlag === 'true';
|
|
442
|
+
const shouldAddFromNotification = this.shouldAutoAnswerNextCall;
|
|
443
443
|
|
|
444
444
|
let pushData;
|
|
445
445
|
if (shouldAddFromNotification) {
|
|
446
446
|
pushData = {
|
|
447
447
|
metadata: enhancedMetadata,
|
|
448
448
|
from_notification: true,
|
|
449
|
+
action: 'answer',
|
|
449
450
|
};
|
|
451
|
+
voipClient.queueAnswerFromCallKit();
|
|
450
452
|
} else {
|
|
451
453
|
pushData = {
|
|
452
454
|
metadata: enhancedMetadata,
|
|
@@ -476,13 +478,9 @@ class CallKitCoordinator {
|
|
|
476
478
|
console.log('CallKitCoordinator: Processing iOS push notification answer');
|
|
477
479
|
|
|
478
480
|
// Set auto-answer flag so when the WebRTC call comes in, it will be answered automatically
|
|
479
|
-
|
|
481
|
+
this.shouldAutoAnswerNextCall = true;
|
|
480
482
|
console.log('CallKitCoordinator: ✅ Set auto-answer flag for next incoming call');
|
|
481
483
|
|
|
482
|
-
// Store the CallKit UUID so we can link it when the WebRTC call arrives
|
|
483
|
-
await AsyncStorage.setItem('@pending_callkit_uuid', callKitUUID);
|
|
484
|
-
console.log('CallKitCoordinator: ✅ Stored pending CallKit UUID for linking');
|
|
485
|
-
|
|
486
484
|
// Get VoIP client and trigger reconnection
|
|
487
485
|
const voipClient = this.getSDKClient();
|
|
488
486
|
if (!voipClient) {
|
|
@@ -534,9 +532,9 @@ class CallKitCoordinator {
|
|
|
534
532
|
});
|
|
535
533
|
}
|
|
536
534
|
|
|
537
|
-
// Set the pending push action
|
|
535
|
+
// Set the pending push action to be handled when app comes to foreground
|
|
538
536
|
await VoicePnBridge.setPendingPushAction(pushAction, pushMetadata);
|
|
539
|
-
console.log('CallKitCoordinator: ✅ Set pending push action
|
|
537
|
+
console.log('CallKitCoordinator: ✅ Set pending push action');
|
|
540
538
|
|
|
541
539
|
return;
|
|
542
540
|
}
|
|
@@ -742,29 +740,12 @@ class CallKitCoordinator {
|
|
|
742
740
|
this.voipClient = voipClient;
|
|
743
741
|
}
|
|
744
742
|
|
|
745
|
-
/**
|
|
746
|
-
* Helper method to handle auto-answer logic for push notification calls
|
|
747
|
-
*/
|
|
748
|
-
private async handleAutoAnswer(call: Call): Promise<void> {
|
|
749
|
-
const shouldAutoAnswer = await AsyncStorage.getItem('@auto_answer_next_call');
|
|
750
|
-
if (shouldAutoAnswer === 'true') {
|
|
751
|
-
console.log('CallKitCoordinator: Auto-answering call from push notification');
|
|
752
|
-
await AsyncStorage.removeItem('@auto_answer_next_call');
|
|
753
|
-
|
|
754
|
-
// Auto-answer the call after a brief delay to ensure CallKit is ready
|
|
755
|
-
setTimeout(() => {
|
|
756
|
-
call.answer();
|
|
757
|
-
}, 100);
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
743
|
/**
|
|
762
744
|
* Helper method to clean up push notification state
|
|
763
745
|
*/
|
|
764
746
|
private async cleanupPushNotificationState(): Promise<void> {
|
|
765
|
-
console.log('CallKitCoordinator: ✅ Cleared
|
|
766
|
-
|
|
767
|
-
await AsyncStorage.removeItem('@auto_answer_next_call');
|
|
747
|
+
console.log('CallKitCoordinator: ✅ Cleared auto-answer flag');
|
|
748
|
+
this.shouldAutoAnswerNextCall = false;
|
|
768
749
|
}
|
|
769
750
|
|
|
770
751
|
/**
|
|
@@ -814,6 +795,9 @@ class CallKitCoordinator {
|
|
|
814
795
|
// Reset push notification flag
|
|
815
796
|
this.isCallFromPush = false;
|
|
816
797
|
|
|
798
|
+
// Reset auto-answer flag
|
|
799
|
+
this.shouldAutoAnswerNextCall = false;
|
|
800
|
+
|
|
817
801
|
console.log('CallKitCoordinator: ✅ Coordinator flags reset');
|
|
818
802
|
}
|
|
819
803
|
|
|
File without changes
|
|
@@ -22,6 +22,7 @@ export class CallStateController {
|
|
|
22
22
|
private _onInviteAutoAccepted?: () => void;
|
|
23
23
|
|
|
24
24
|
constructor(private readonly _sessionManager: SessionManager) {
|
|
25
|
+
console.log('🔧 CallStateController: Constructor called - instance created');
|
|
25
26
|
// Don't set up client listeners here - client doesn't exist yet
|
|
26
27
|
// Will be called when client is available
|
|
27
28
|
}
|
|
@@ -110,6 +111,10 @@ export class CallStateController {
|
|
|
110
111
|
*/
|
|
111
112
|
initializeClientListeners(): void {
|
|
112
113
|
console.log('🔧 CallStateController: initializeClientListeners called');
|
|
114
|
+
console.log(
|
|
115
|
+
'🔧 CallStateController: Current client exists:',
|
|
116
|
+
!!this._sessionManager.telnyxClient
|
|
117
|
+
);
|
|
113
118
|
this._setupClientListeners();
|
|
114
119
|
|
|
115
120
|
// CallKit integration now handled by CallKitCoordinator
|
|
@@ -123,7 +128,7 @@ export class CallStateController {
|
|
|
123
128
|
destination: string,
|
|
124
129
|
callerName?: string,
|
|
125
130
|
callerNumber?: string,
|
|
126
|
-
|
|
131
|
+
customHeaders?: Record<string, string>
|
|
127
132
|
): Promise<Call> {
|
|
128
133
|
if (this._disposed) {
|
|
129
134
|
throw new Error('CallStateController has been disposed');
|
|
@@ -139,6 +144,7 @@ export class CallStateController {
|
|
|
139
144
|
destinationNumber: destination,
|
|
140
145
|
callerIdName: callerName,
|
|
141
146
|
callerIdNumber: callerNumber,
|
|
147
|
+
customHeaders,
|
|
142
148
|
};
|
|
143
149
|
const telnyxCall = await this._sessionManager.telnyxClient.newCall(callOptions);
|
|
144
150
|
|
|
@@ -147,7 +153,10 @@ export class CallStateController {
|
|
|
147
153
|
telnyxCall,
|
|
148
154
|
telnyxCall.callId || this._generateCallId(),
|
|
149
155
|
destination,
|
|
150
|
-
false // outgoing call
|
|
156
|
+
false, // outgoing call
|
|
157
|
+
false, // not reattached
|
|
158
|
+
callerName || destination, // use destination as fallback for caller name
|
|
159
|
+
callerNumber // original caller number
|
|
151
160
|
);
|
|
152
161
|
|
|
153
162
|
// Add to our call tracking
|
|
@@ -202,16 +211,41 @@ export class CallStateController {
|
|
|
202
211
|
}
|
|
203
212
|
|
|
204
213
|
console.log('🔧 CallStateController: TelnyxClient found, setting up incoming call listener');
|
|
214
|
+
console.log(
|
|
215
|
+
'🔧 CallStateController: Client instance:',
|
|
216
|
+
this._sessionManager.telnyxClient.constructor.name
|
|
217
|
+
);
|
|
205
218
|
|
|
206
219
|
// Listen for incoming calls
|
|
207
220
|
this._sessionManager.telnyxClient.on(
|
|
208
221
|
'telnyx.call.incoming',
|
|
209
222
|
(telnyxCall: TelnyxCall, msg: any) => {
|
|
210
223
|
console.log('📞 CallStateController: Incoming call received:', telnyxCall.callId);
|
|
211
|
-
this._handleIncomingCall(telnyxCall, msg);
|
|
224
|
+
this._handleIncomingCall(telnyxCall, msg, false);
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
// Listen for reattached calls (after network reconnection)
|
|
229
|
+
this._sessionManager.telnyxClient.on(
|
|
230
|
+
'telnyx.call.reattached',
|
|
231
|
+
(telnyxCall: TelnyxCall, msg: any) => {
|
|
232
|
+
console.log('📞 CallStateController: Reattached call received:', telnyxCall.callId);
|
|
233
|
+
this._handleIncomingCall(telnyxCall, msg, true);
|
|
212
234
|
}
|
|
213
235
|
);
|
|
214
236
|
|
|
237
|
+
// Verify listeners are set up
|
|
238
|
+
const incomingListeners =
|
|
239
|
+
this._sessionManager.telnyxClient.listenerCount('telnyx.call.incoming');
|
|
240
|
+
const reattachedListeners =
|
|
241
|
+
this._sessionManager.telnyxClient.listenerCount('telnyx.call.reattached');
|
|
242
|
+
console.log(
|
|
243
|
+
'🔧 CallStateController: Listeners registered - incoming:',
|
|
244
|
+
incomingListeners,
|
|
245
|
+
'reattached:',
|
|
246
|
+
reattachedListeners
|
|
247
|
+
);
|
|
248
|
+
|
|
215
249
|
// Listen for other call events if needed
|
|
216
250
|
// this._sessionManager.telnyxClient.on('telnyx.call.stateChange', this._handleCallStateChange.bind(this));
|
|
217
251
|
|
|
@@ -219,28 +253,50 @@ export class CallStateController {
|
|
|
219
253
|
}
|
|
220
254
|
|
|
221
255
|
/**
|
|
222
|
-
* Handle incoming call
|
|
256
|
+
* Handle incoming call or reattached call
|
|
223
257
|
*/
|
|
224
|
-
private _handleIncomingCall(
|
|
258
|
+
private _handleIncomingCall(
|
|
259
|
+
telnyxCall: TelnyxCall,
|
|
260
|
+
inviteMsg?: any,
|
|
261
|
+
isReattached: boolean = false
|
|
262
|
+
): void {
|
|
225
263
|
const callId = telnyxCall.callId || this._generateCallId();
|
|
226
264
|
|
|
227
|
-
console.log(
|
|
265
|
+
console.log(
|
|
266
|
+
'📞 CallStateController: Handling incoming call:',
|
|
267
|
+
callId,
|
|
268
|
+
'isReattached:',
|
|
269
|
+
isReattached
|
|
270
|
+
);
|
|
228
271
|
console.log('📞 CallStateController: TelnyxCall object:', telnyxCall);
|
|
229
272
|
console.log('📞 CallStateController: Invite message:', inviteMsg);
|
|
230
273
|
|
|
231
|
-
//
|
|
232
|
-
if (this._callMap.has(callId)) {
|
|
274
|
+
// For reattached calls, remove existing call and create new one
|
|
275
|
+
if (isReattached && this._callMap.has(callId)) {
|
|
276
|
+
console.log('📞 CallStateController: Removing existing call for reattachment');
|
|
277
|
+
const existingCall = this._callMap.get(callId);
|
|
278
|
+
if (existingCall) {
|
|
279
|
+
console.log(
|
|
280
|
+
'📞 CallStateController: Existing call state before removal:',
|
|
281
|
+
existingCall.currentState
|
|
282
|
+
);
|
|
283
|
+
this._removeCall(callId);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Check if we already have this call (for non-reattached calls)
|
|
288
|
+
if (this._callMap.has(callId) && !isReattached) {
|
|
233
289
|
console.log('Call already exists:', callId);
|
|
234
290
|
return;
|
|
235
291
|
}
|
|
236
292
|
|
|
237
293
|
// Get caller information from the invite message (preferred) or fallback to TelnyxCall
|
|
238
|
-
let callerNumber = '
|
|
239
|
-
let callerName = '
|
|
294
|
+
let callerNumber = '';
|
|
295
|
+
let callerName = '';
|
|
240
296
|
|
|
241
297
|
if (inviteMsg && inviteMsg.params) {
|
|
242
|
-
callerNumber = inviteMsg.params.caller_id_number || '
|
|
243
|
-
callerName = inviteMsg.params.caller_id_name ||
|
|
298
|
+
callerNumber = inviteMsg.params.caller_id_number || '';
|
|
299
|
+
callerName = inviteMsg.params.caller_id_name || '';
|
|
244
300
|
console.log(
|
|
245
301
|
'📞 CallStateController: Extracted caller info from invite - Number:',
|
|
246
302
|
callerNumber,
|
|
@@ -249,8 +305,8 @@ export class CallStateController {
|
|
|
249
305
|
);
|
|
250
306
|
} else {
|
|
251
307
|
// Fallback to TelnyxCall properties
|
|
252
|
-
callerNumber = telnyxCall.remoteCallerIdNumber || '
|
|
253
|
-
callerName = telnyxCall.remoteCallerIdName ||
|
|
308
|
+
callerNumber = telnyxCall.remoteCallerIdNumber || '';
|
|
309
|
+
callerName = telnyxCall.remoteCallerIdName || '';
|
|
254
310
|
console.log(
|
|
255
311
|
'📞 CallStateController: Extracted caller info from TelnyxCall - Number:',
|
|
256
312
|
callerNumber,
|
|
@@ -259,60 +315,25 @@ export class CallStateController {
|
|
|
259
315
|
);
|
|
260
316
|
}
|
|
261
317
|
|
|
318
|
+
// Use smart fallbacks - prefer caller number over "Unknown"
|
|
319
|
+
const finalCallerNumber = callerNumber || 'Unknown Number';
|
|
320
|
+
const finalCallerName = callerName || callerNumber || 'Unknown Caller';
|
|
321
|
+
|
|
262
322
|
// Create our wrapper Call object
|
|
263
323
|
const call = new Call(
|
|
264
324
|
telnyxCall,
|
|
265
325
|
callId,
|
|
266
|
-
|
|
267
|
-
true // incoming call
|
|
326
|
+
finalCallerNumber, // Use caller number as destination for incoming calls
|
|
327
|
+
true, // incoming call
|
|
328
|
+
isReattached, // pass the reattached flag
|
|
329
|
+
finalCallerName, // use caller name or fallback to number
|
|
330
|
+
finalCallerNumber // use caller number
|
|
268
331
|
);
|
|
269
332
|
|
|
270
|
-
// Check if we're waiting for an invite (push notification scenario)
|
|
271
|
-
if (this._isWaitingForInvite && this._isWaitingForInvite()) {
|
|
272
|
-
console.log('Auto-accepting call from push notification');
|
|
273
|
-
call.answer().catch((error) => {
|
|
274
|
-
console.error('Failed to auto-accept call:', error);
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
if (this._onInviteAutoAccepted) {
|
|
278
|
-
this._onInviteAutoAccepted();
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
333
|
// Add to our call tracking - CallKit integration happens in _addCall
|
|
283
334
|
this._addCall(call);
|
|
284
335
|
}
|
|
285
336
|
|
|
286
|
-
/**
|
|
287
|
-
* Handle call state changes from the Telnyx client
|
|
288
|
-
*/
|
|
289
|
-
private _handleCallStateChange(event: any): void {
|
|
290
|
-
const callId = event.callId || event.id;
|
|
291
|
-
const call = this._callMap.get(callId);
|
|
292
|
-
|
|
293
|
-
if (call) {
|
|
294
|
-
// The Call object will handle its own state updates through its listeners
|
|
295
|
-
console.log(`Call ${callId} state changed to ${event.state}`);
|
|
296
|
-
} else {
|
|
297
|
-
console.warn(`Received state change for unknown call: ${callId}`);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Handle call updates from notifications
|
|
303
|
-
*/
|
|
304
|
-
private _handleCallUpdate(callData: any): void {
|
|
305
|
-
const callId = callData.id;
|
|
306
|
-
const call = this._callMap.get(callId);
|
|
307
|
-
|
|
308
|
-
if (call) {
|
|
309
|
-
// Update call state based on the notification
|
|
310
|
-
console.log(`Call ${callId} updated:`, callData);
|
|
311
|
-
} else {
|
|
312
|
-
console.warn(`Received update for unknown call: ${callId}`);
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
337
|
/**
|
|
317
338
|
* Add a call to our tracking
|
|
318
339
|
*/
|
|
@@ -365,6 +386,8 @@ export class CallStateController {
|
|
|
365
386
|
private _removeCall(callId: string): void {
|
|
366
387
|
const call = this._callMap.get(callId);
|
|
367
388
|
if (call) {
|
|
389
|
+
console.log('CallStateController: Removing call:', callId);
|
|
390
|
+
|
|
368
391
|
// CallKit cleanup is handled automatically by CallKitCoordinator
|
|
369
392
|
|
|
370
393
|
call.dispose();
|
|
@@ -276,7 +276,7 @@ export class SessionManager {
|
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
/**
|
|
279
|
-
* Internal method to establish connection
|
|
279
|
+
* Internal method to establish connection with or without push notification handling
|
|
280
280
|
*/
|
|
281
281
|
private async _connect(): Promise<void> {
|
|
282
282
|
if (!this._currentConfig) {
|
|
@@ -363,8 +363,13 @@ export class SessionManager {
|
|
|
363
363
|
console.log(
|
|
364
364
|
'🔧 SessionManager: Setting up CallStateController listeners before connection...'
|
|
365
365
|
);
|
|
366
|
+
console.log('🔧 SessionManager: _onClientReady callback exists:', !!this._onClientReady);
|
|
366
367
|
if (this._onClientReady) {
|
|
368
|
+
console.log('🔧 SessionManager: Calling _onClientReady callback now...');
|
|
367
369
|
this._onClientReady();
|
|
370
|
+
console.log('🔧 SessionManager: _onClientReady callback completed');
|
|
371
|
+
} else {
|
|
372
|
+
console.log('🔧 SessionManager: No _onClientReady callback found');
|
|
368
373
|
}
|
|
369
374
|
|
|
370
375
|
// Connect to the platform AFTER processing push notification
|
|
@@ -393,6 +398,21 @@ export class SessionManager {
|
|
|
393
398
|
this._telnyxClient.on('telnyx.client.ready', () => {
|
|
394
399
|
console.log('Telnyx client ready');
|
|
395
400
|
this._connectionState.next(TelnyxConnectionState.CONNECTED);
|
|
401
|
+
|
|
402
|
+
// Ensure CallStateController listeners are set up when client becomes ready
|
|
403
|
+
// This handles both initial connection and automatic reconnection
|
|
404
|
+
console.log(
|
|
405
|
+
'🔧 SessionManager: Client ready event - reinitializing CallStateController listeners'
|
|
406
|
+
);
|
|
407
|
+
if (this._onClientReady) {
|
|
408
|
+
console.log(
|
|
409
|
+
'🔧 SessionManager: Calling _onClientReady callback from client ready event...'
|
|
410
|
+
);
|
|
411
|
+
this._onClientReady();
|
|
412
|
+
console.log('🔧 SessionManager: _onClientReady callback completed from client ready event');
|
|
413
|
+
} else {
|
|
414
|
+
console.log('🔧 SessionManager: No _onClientReady callback found in client ready event');
|
|
415
|
+
}
|
|
396
416
|
});
|
|
397
417
|
|
|
398
418
|
this._telnyxClient.on('telnyx.client.error', (error: Error) => {
|
|
@@ -404,28 +424,6 @@ export class SessionManager {
|
|
|
404
424
|
// We'll rely on the client-level events for now
|
|
405
425
|
}
|
|
406
426
|
|
|
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
427
|
/**
|
|
430
428
|
* Extract the actual payload metadata from wrapped push notification payload
|
|
431
429
|
*/
|
|
@@ -434,10 +432,19 @@ export class SessionManager {
|
|
|
434
432
|
let actualPayload = payload;
|
|
435
433
|
|
|
436
434
|
if (payload.metadata && typeof payload.metadata === 'object') {
|
|
437
|
-
// If there's a metadata wrapper, use that
|
|
435
|
+
// If there's a metadata wrapper, use that but preserve wrapper-level flags
|
|
438
436
|
actualPayload = payload.metadata;
|
|
437
|
+
|
|
438
|
+
// Preserve important flags from the wrapper level
|
|
439
|
+
if (payload.from_notification !== undefined) {
|
|
440
|
+
actualPayload.from_notification = payload.from_notification;
|
|
441
|
+
}
|
|
442
|
+
if (payload.action !== undefined) {
|
|
443
|
+
actualPayload.action = payload.action;
|
|
444
|
+
}
|
|
445
|
+
|
|
439
446
|
console.log(
|
|
440
|
-
'SessionManager: RELEASE DEBUG - Using metadata portion of payload:',
|
|
447
|
+
'SessionManager: RELEASE DEBUG - Using metadata portion of payload with preserved flags:',
|
|
441
448
|
JSON.stringify(actualPayload)
|
|
442
449
|
);
|
|
443
450
|
} else if (payload.action === 'incoming_call' && payload.metadata) {
|
|
@@ -446,8 +453,17 @@ export class SessionManager {
|
|
|
446
453
|
const parsedMetadata =
|
|
447
454
|
typeof payload.metadata === 'string' ? JSON.parse(payload.metadata) : payload.metadata;
|
|
448
455
|
actualPayload = parsedMetadata;
|
|
456
|
+
|
|
457
|
+
// Preserve important flags from the wrapper level
|
|
458
|
+
if (payload.from_notification !== undefined) {
|
|
459
|
+
actualPayload.from_notification = payload.from_notification;
|
|
460
|
+
}
|
|
461
|
+
if (payload.action !== undefined) {
|
|
462
|
+
actualPayload.action = payload.action;
|
|
463
|
+
}
|
|
464
|
+
|
|
449
465
|
console.log(
|
|
450
|
-
'SessionManager: RELEASE DEBUG - Using parsed metadata:',
|
|
466
|
+
'SessionManager: RELEASE DEBUG - Using parsed metadata with preserved flags:',
|
|
451
467
|
JSON.stringify(actualPayload)
|
|
452
468
|
);
|
|
453
469
|
} catch (error) {
|