@telnyx/react-voice-commons-sdk 0.1.2 → 0.1.4

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 (55) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +469 -483
  3. package/ios/CallKitBridge.swift +2 -7
  4. package/lib/callkit/callkit-coordinator.d.ts +110 -117
  5. package/lib/callkit/callkit-coordinator.js +664 -727
  6. package/lib/callkit/callkit.d.ts +41 -41
  7. package/lib/callkit/callkit.js +252 -242
  8. package/lib/callkit/index.js +15 -47
  9. package/lib/callkit/use-callkit.d.ts +19 -19
  10. package/lib/callkit/use-callkit.js +270 -310
  11. package/lib/context/TelnyxVoiceContext.d.ts +9 -9
  12. package/lib/context/TelnyxVoiceContext.js +10 -13
  13. package/lib/hooks/use-callkit-coordinator.d.ts +9 -17
  14. package/lib/hooks/use-callkit-coordinator.js +45 -50
  15. package/lib/hooks/useAppReadyNotifier.js +13 -15
  16. package/lib/hooks/useAppStateHandler.d.ts +6 -11
  17. package/lib/hooks/useAppStateHandler.js +95 -110
  18. package/lib/hooks/useNetworkStateHandler.d.ts +0 -0
  19. package/lib/hooks/useNetworkStateHandler.js +0 -0
  20. package/lib/index.d.ts +3 -21
  21. package/lib/index.js +50 -201
  22. package/lib/internal/CallKitHandler.d.ts +6 -6
  23. package/lib/internal/CallKitHandler.js +96 -104
  24. package/lib/internal/callkit-manager.d.ts +57 -57
  25. package/lib/internal/callkit-manager.js +299 -316
  26. package/lib/internal/calls/call-state-controller.d.ts +73 -86
  27. package/lib/internal/calls/call-state-controller.js +263 -307
  28. package/lib/internal/session/session-manager.d.ts +71 -75
  29. package/lib/internal/session/session-manager.js +360 -424
  30. package/lib/internal/user-defaults-helpers.js +49 -39
  31. package/lib/internal/voice-pn-bridge.d.ts +114 -12
  32. package/lib/internal/voice-pn-bridge.js +212 -5
  33. package/lib/models/call-state.d.ts +46 -44
  34. package/lib/models/call-state.js +70 -68
  35. package/lib/models/call.d.ts +161 -133
  36. package/lib/models/call.js +454 -382
  37. package/lib/models/config.d.ts +11 -18
  38. package/lib/models/config.js +37 -35
  39. package/lib/models/connection-state.d.ts +10 -10
  40. package/lib/models/connection-state.js +16 -16
  41. package/lib/telnyx-voice-app.d.ts +28 -28
  42. package/lib/telnyx-voice-app.js +513 -480
  43. package/lib/telnyx-voip-client.d.ts +167 -167
  44. package/lib/telnyx-voip-client.js +385 -390
  45. package/package.json +115 -104
  46. package/src/callkit/callkit-coordinator.ts +830 -846
  47. package/src/hooks/useNetworkStateHandler.ts +0 -0
  48. package/src/internal/calls/call-state-controller.ts +407 -384
  49. package/src/internal/session/session-manager.ts +483 -467
  50. package/src/internal/voice-pn-bridge.ts +266 -18
  51. package/src/models/call-state.ts +105 -98
  52. package/src/models/call.ts +502 -388
  53. package/src/telnyx-voice-app.tsx +788 -690
  54. package/src/telnyx-voip-client.ts +551 -539
  55. package/src/types/telnyx-sdk.d.ts +93 -79
@@ -1,62 +1,43 @@
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;
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
41
24
  };
42
- return ownKeys(o);
25
+ return ownKeys(o);
43
26
  };
44
27
  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;
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
52
33
  };
53
- })();
54
- Object.defineProperty(exports, '__esModule', { value: true });
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
55
36
  exports.Call = void 0;
56
- const rxjs_1 = require('rxjs');
57
- const operators_1 = require('rxjs/operators');
58
- const call_state_1 = require('./call-state');
59
- const react_native_1 = require('react-native');
37
+ const rxjs_1 = require("rxjs");
38
+ const operators_1 = require("rxjs/operators");
39
+ const call_state_1 = require("./call-state");
40
+ const react_native_1 = require("react-native");
60
41
  /**
61
42
  * Represents a call with reactive state streams.
62
43
  *
@@ -65,336 +46,427 @@ const react_native_1 = require('react-native');
65
46
  * integrate with any state management solution.
66
47
  */
67
48
  class Call {
68
- constructor(_telnyxCall, _callId, _destination, _isIncoming) {
69
- this._telnyxCall = _telnyxCall;
70
- this._callId = _callId;
71
- this._destination = _destination;
72
- this._isIncoming = _isIncoming;
73
- this._callState = new rxjs_1.BehaviorSubject(call_state_1.TelnyxCallState.RINGING);
74
- this._isMuted = new rxjs_1.BehaviorSubject(false);
75
- this._isHeld = new rxjs_1.BehaviorSubject(false);
76
- this._duration = new rxjs_1.BehaviorSubject(0);
77
- this._setupCallListeners();
78
- }
79
- /**
80
- * Unique identifier for this call
81
- */
82
- get callId() {
83
- return this._callId;
84
- }
85
- /**
86
- * The destination number or SIP URI
87
- */
88
- get destination() {
89
- return this._destination;
90
- }
91
- /**
92
- * Whether this is an incoming call
93
- */
94
- get isIncoming() {
95
- return this._isIncoming;
96
- }
97
- /**
98
- * Whether this is an outgoing call
99
- */
100
- get isOutgoing() {
101
- return !this._isIncoming;
102
- }
103
- /**
104
- * Current call state (synchronous access)
105
- */
106
- get currentState() {
107
- return this._callState.value;
108
- }
109
- /**
110
- * Current mute state (synchronous access)
111
- */
112
- get currentIsMuted() {
113
- return this._isMuted.value;
114
- }
115
- /**
116
- * Current hold state (synchronous access)
117
- */
118
- get currentIsHeld() {
119
- return this._isHeld.value;
120
- }
121
- /**
122
- * Current call duration in seconds (synchronous access)
123
- */
124
- get currentDuration() {
125
- return this._duration.value;
126
- }
127
- /**
128
- * Get the underlying Telnyx Call object (for internal use)
129
- * @internal
130
- */
131
- get telnyxCall() {
132
- return this._telnyxCall;
133
- }
134
- /**
135
- * Observable stream of call state changes
136
- */
137
- get callState$() {
138
- return this._callState.asObservable().pipe((0, operators_1.distinctUntilChanged)());
139
- }
140
- /**
141
- * Observable stream of mute state changes
142
- */
143
- get isMuted$() {
144
- return this._isMuted.asObservable().pipe((0, operators_1.distinctUntilChanged)());
145
- }
146
- /**
147
- * Observable stream of hold state changes
148
- */
149
- get isHeld$() {
150
- return this._isHeld.asObservable().pipe((0, operators_1.distinctUntilChanged)());
151
- }
152
- /**
153
- * Observable stream of call duration changes (in seconds)
154
- */
155
- get duration$() {
156
- return this._duration.asObservable().pipe((0, operators_1.distinctUntilChanged)());
157
- }
158
- /**
159
- * Observable that emits true when the call can be answered
160
- */
161
- get canAnswer$() {
162
- return this.callState$.pipe(
163
- (0, operators_1.map)((state) => call_state_1.CallStateHelpers.canAnswer(state)),
164
- (0, operators_1.distinctUntilChanged)()
165
- );
166
- }
167
- /**
168
- * Observable that emits true when the call can be hung up
169
- */
170
- get canHangup$() {
171
- return this.callState$.pipe(
172
- (0, operators_1.map)((state) => call_state_1.CallStateHelpers.canHangup(state)),
173
- (0, operators_1.distinctUntilChanged)()
174
- );
175
- }
176
- /**
177
- * Observable that emits true when the call can be put on hold
178
- */
179
- get canHold$() {
180
- return this.callState$.pipe(
181
- (0, operators_1.map)((state) => call_state_1.CallStateHelpers.canHold(state)),
182
- (0, operators_1.distinctUntilChanged)()
183
- );
184
- }
185
- /**
186
- * Observable that emits true when the call can be resumed from hold
187
- */
188
- get canResume$() {
189
- return this.callState$.pipe(
190
- (0, operators_1.map)((state) => call_state_1.CallStateHelpers.canResume(state)),
191
- (0, operators_1.distinctUntilChanged)()
192
- );
193
- }
194
- /**
195
- * Answer the incoming call
196
- */
197
- async answer() {
198
- if (!call_state_1.CallStateHelpers.canAnswer(this.currentState)) {
199
- throw new Error(`Cannot answer call in state: ${this.currentState}`);
200
- }
201
- try {
202
- // On iOS, use CallKit coordinator for proper audio session handling
203
- if (react_native_1.Platform.OS === 'ios') {
204
- const { callKitCoordinator } = await Promise.resolve().then(() =>
205
- __importStar(require('../callkit/callkit-coordinator'))
206
- );
207
- if (callKitCoordinator.isAvailable()) {
208
- console.log('Call: Using CallKit coordinator to answer call (iOS)');
209
- await callKitCoordinator.answerCallFromUI(this._telnyxCall);
210
- return;
49
+ constructor(_telnyxCall, _callId, _destination, _isIncoming, isReattached = false, _originalCallerName, _originalCallerNumber) {
50
+ this._telnyxCall = _telnyxCall;
51
+ this._callId = _callId;
52
+ this._destination = _destination;
53
+ this._isIncoming = _isIncoming;
54
+ this._originalCallerName = _originalCallerName;
55
+ this._originalCallerNumber = _originalCallerNumber;
56
+ this._callState = new rxjs_1.BehaviorSubject(call_state_1.TelnyxCallState.RINGING);
57
+ this._isMuted = new rxjs_1.BehaviorSubject(false);
58
+ this._isHeld = new rxjs_1.BehaviorSubject(false);
59
+ this._duration = new rxjs_1.BehaviorSubject(0);
60
+ // Set initial state based on whether this is a reattached call
61
+ if (isReattached) {
62
+ console.log('Call: Setting initial state to ACTIVE for reattached call');
63
+ this._callState.next(call_state_1.TelnyxCallState.ACTIVE);
64
+ }
65
+ this._setupCallListeners();
66
+ }
67
+ /**
68
+ * Unique identifier for this call
69
+ */
70
+ get callId() {
71
+ return this._callId;
72
+ }
73
+ /**
74
+ * The destination number or SIP URI
75
+ */
76
+ get destination() {
77
+ return this._destination;
78
+ }
79
+ /**
80
+ * Whether this is an incoming call
81
+ */
82
+ get isIncoming() {
83
+ return this._isIncoming;
84
+ }
85
+ /**
86
+ * Whether this is an outgoing call
87
+ */
88
+ get isOutgoing() {
89
+ return !this._isIncoming;
90
+ }
91
+ /**
92
+ * Current call state (synchronous access)
93
+ */
94
+ get currentState() {
95
+ return this._callState.value;
96
+ }
97
+ /**
98
+ * Current mute state (synchronous access)
99
+ */
100
+ get currentIsMuted() {
101
+ return this._isMuted.value;
102
+ }
103
+ /**
104
+ * Current hold state (synchronous access)
105
+ */
106
+ get currentIsHeld() {
107
+ return this._isHeld.value;
108
+ }
109
+ /**
110
+ * Current call duration in seconds (synchronous access)
111
+ */
112
+ get currentDuration() {
113
+ return this._duration.value;
114
+ }
115
+ /**
116
+ * Custom headers received from the WebRTC INVITE message.
117
+ * These headers are passed during call initiation and can contain application-specific information.
118
+ * Format should be [{"name": "X-Header-Name", "value": "Value"}] where header names must start with "X-".
119
+ */
120
+ get inviteCustomHeaders() {
121
+ return this._telnyxCall.inviteCustomHeaders;
122
+ }
123
+ /**
124
+ * Custom headers received from the WebRTC ANSWER message.
125
+ * These headers are passed during call acceptance and can contain application-specific information.
126
+ * Format should be [{"name": "X-Header-Name", "value": "Value"}] where header names must start with "X-".
127
+ */
128
+ get answerCustomHeaders() {
129
+ return this._telnyxCall.answerCustomHeaders;
130
+ }
131
+ /**
132
+ * Get the underlying Telnyx Call object (for internal use)
133
+ * @internal
134
+ */
135
+ get telnyxCall() {
136
+ return this._telnyxCall;
137
+ }
138
+ /**
139
+ * Observable stream of call state changes
140
+ */
141
+ get callState$() {
142
+ return this._callState.asObservable().pipe((0, operators_1.distinctUntilChanged)());
143
+ }
144
+ /**
145
+ * Observable stream of mute state changes
146
+ */
147
+ get isMuted$() {
148
+ return this._isMuted.asObservable().pipe((0, operators_1.distinctUntilChanged)());
149
+ }
150
+ /**
151
+ * Observable stream of hold state changes
152
+ */
153
+ get isHeld$() {
154
+ return this._isHeld.asObservable().pipe((0, operators_1.distinctUntilChanged)());
155
+ }
156
+ /**
157
+ * Observable stream of call duration changes (in seconds)
158
+ */
159
+ get duration$() {
160
+ return this._duration.asObservable().pipe((0, operators_1.distinctUntilChanged)());
161
+ }
162
+ /**
163
+ * Observable that emits true when the call can be answered
164
+ */
165
+ get canAnswer$() {
166
+ return this.callState$.pipe((0, operators_1.map)((state) => call_state_1.CallStateHelpers.canAnswer(state)), (0, operators_1.distinctUntilChanged)());
167
+ }
168
+ /**
169
+ * Observable that emits true when the call can be hung up
170
+ */
171
+ get canHangup$() {
172
+ return this.callState$.pipe((0, operators_1.map)((state) => call_state_1.CallStateHelpers.canHangup(state)), (0, operators_1.distinctUntilChanged)());
173
+ }
174
+ /**
175
+ * Observable that emits true when the call can be put on hold
176
+ */
177
+ get canHold$() {
178
+ return this.callState$.pipe((0, operators_1.map)((state) => call_state_1.CallStateHelpers.canHold(state)), (0, operators_1.distinctUntilChanged)());
179
+ }
180
+ /**
181
+ * Observable that emits true when the call can be resumed from hold
182
+ */
183
+ get canResume$() {
184
+ return this.callState$.pipe((0, operators_1.map)((state) => call_state_1.CallStateHelpers.canResume(state)), (0, operators_1.distinctUntilChanged)());
185
+ }
186
+ /**
187
+ * Answer the incoming call
188
+ * @param customHeaders Optional custom headers to include with the answer
189
+ */
190
+ async answer(customHeaders) {
191
+ if (!call_state_1.CallStateHelpers.canAnswer(this.currentState)) {
192
+ throw new Error(`Cannot answer call in state: ${this.currentState}`);
193
+ }
194
+ try {
195
+ // On iOS, use CallKit coordinator for proper audio session handling
196
+ if (react_native_1.Platform.OS === 'ios') {
197
+ const { callKitCoordinator } = await Promise.resolve().then(() => __importStar(require('../callkit/callkit-coordinator')));
198
+ if (callKitCoordinator.isAvailable()) {
199
+ console.log('Call: Using CallKit coordinator to answer call (iOS)');
200
+ await callKitCoordinator.answerCallFromUI(this._telnyxCall);
201
+ return;
202
+ }
203
+ }
204
+ // Fallback for Android or when CallKit is not available
205
+ console.log('Call: Setting state to CONNECTING before answering');
206
+ this._callState.next(call_state_1.TelnyxCallState.CONNECTING);
207
+ // Pass custom headers to the underlying Telnyx call
208
+ await this._telnyxCall.answer(customHeaders);
209
+ }
210
+ catch (error) {
211
+ console.error('Failed to answer call:', error);
212
+ throw error;
213
+ }
214
+ }
215
+ /**
216
+ * Hang up the call
217
+ * @param customHeaders Optional custom headers to include with the hangup request
218
+ */
219
+ async hangup(customHeaders) {
220
+ if (!call_state_1.CallStateHelpers.canHangup(this.currentState)) {
221
+ throw new Error(`Cannot hang up call in state: ${this.currentState}`);
222
+ }
223
+ try {
224
+ // On iOS, use CallKit coordinator for proper CallKit cleanup
225
+ if (react_native_1.Platform.OS === 'ios') {
226
+ const { callKitCoordinator } = await Promise.resolve().then(() => __importStar(require('../callkit/callkit-coordinator')));
227
+ if (callKitCoordinator.isAvailable()) {
228
+ console.log('Call: Using CallKit coordinator to end call (iOS)');
229
+ await callKitCoordinator.endCallFromUI(this._telnyxCall);
230
+ return;
231
+ }
232
+ }
233
+ // Fallback for Android or when CallKit is not available
234
+ await this._telnyxCall.hangup(customHeaders);
235
+ // On Android, also notify the native side to hide ongoing notification
236
+ if (react_native_1.Platform.OS === 'android') {
237
+ try {
238
+ const { VoicePnBridge } = await Promise.resolve().then(() => __importStar(require('../internal/voice-pn-bridge')));
239
+ await VoicePnBridge.endCall(this._callId);
240
+ console.log('Call: Notified Android to hide ongoing notification');
241
+ }
242
+ catch (error) {
243
+ console.error('Call: Failed to notify Android about call end:', error);
244
+ // Don't fail the hangup if notification hiding fails
245
+ }
246
+ }
211
247
  }
212
- }
213
- // Fallback for Android or when CallKit is not available
214
- console.log('Call: Setting state to CONNECTING before answering');
215
- this._callState.next(call_state_1.TelnyxCallState.CONNECTING);
216
- await this._telnyxCall.answer();
217
- } catch (error) {
218
- console.error('Failed to answer call:', error);
219
- throw error;
220
- }
221
- }
222
- /**
223
- * Hang up the call
224
- */
225
- async hangup() {
226
- if (!call_state_1.CallStateHelpers.canHangup(this.currentState)) {
227
- throw new Error(`Cannot hang up call in state: ${this.currentState}`);
228
- }
229
- try {
230
- // On iOS, use CallKit coordinator for proper CallKit cleanup
231
- if (react_native_1.Platform.OS === 'ios') {
232
- const { callKitCoordinator } = await Promise.resolve().then(() =>
233
- __importStar(require('../callkit/callkit-coordinator'))
234
- );
235
- if (callKitCoordinator.isAvailable()) {
236
- console.log('Call: Using CallKit coordinator to end call (iOS)');
237
- await callKitCoordinator.endCallFromUI(this._telnyxCall);
238
- return;
248
+ catch (error) {
249
+ console.error('Failed to hang up call:', error);
250
+ throw error;
239
251
  }
240
- }
241
- // Fallback for Android or when CallKit is not available
242
- await this._telnyxCall.hangup();
243
- } catch (error) {
244
- console.error('Failed to hang up call:', error);
245
- throw error;
246
- }
247
- }
248
- /**
249
- * Put the call on hold
250
- */
251
- async hold() {
252
- if (!call_state_1.CallStateHelpers.canHold(this.currentState)) {
253
- throw new Error(`Cannot hold call in state: ${this.currentState}`);
254
- }
255
- try {
256
- await this._telnyxCall.hold();
257
- } catch (error) {
258
- console.error('Failed to hold call:', error);
259
- throw error;
260
- }
261
- }
262
- /**
263
- * Resume the call from hold
264
- */
265
- async resume() {
266
- if (!call_state_1.CallStateHelpers.canResume(this.currentState)) {
267
- throw new Error(`Cannot resume call in state: ${this.currentState}`);
268
- }
269
- try {
270
- await this._telnyxCall.unhold();
271
- } catch (error) {
272
- console.error('Failed to resume call:', error);
273
- throw error;
274
- }
275
- }
276
- /**
277
- * Mute the call
278
- */
279
- async mute() {
280
- if (!call_state_1.CallStateHelpers.canToggleMute(this.currentState)) {
281
- throw new Error(`Cannot mute call in state: ${this.currentState}`);
282
- }
283
- try {
284
- this._telnyxCall.mute();
285
- this._isMuted.next(true);
286
- } catch (error) {
287
- console.error('Failed to mute call:', error);
288
- throw error;
289
- }
290
- }
291
- /**
292
- * Unmute the call
293
- */
294
- async unmute() {
295
- if (!call_state_1.CallStateHelpers.canToggleMute(this.currentState)) {
296
- throw new Error(`Cannot unmute call in state: ${this.currentState}`);
297
- }
298
- try {
299
- this._telnyxCall.unmute();
300
- this._isMuted.next(false);
301
- } catch (error) {
302
- console.error('Failed to unmute call:', error);
303
- throw error;
304
- }
305
- }
306
- /**
307
- * Toggle mute state
308
- */
309
- async toggleMute() {
310
- if (this.currentIsMuted) {
311
- await this.unmute();
312
- } else {
313
- await this.mute();
314
- }
315
- }
316
- /**
317
- * Set the call to connecting state (used for push notification calls when answered via CallKit)
318
- * @internal
319
- */
320
- setConnecting() {
321
- console.log('Call: Setting state to CONNECTING for push notification answer');
322
- this._callState.next(call_state_1.TelnyxCallState.CONNECTING);
323
- }
324
- /**
325
- * Clean up resources when the call is disposed
326
- */
327
- dispose() {
328
- this._stopDurationTimer();
329
- this._callState.complete();
330
- this._isMuted.complete();
331
- this._isHeld.complete();
332
- this._duration.complete();
333
- }
334
- /**
335
- * Set up listeners for the underlying Telnyx call
336
- */
337
- _setupCallListeners() {
338
- // Map Telnyx call states to our simplified states
339
- this._telnyxCall.on('telnyx.call.state', (call, state) => {
340
- const telnyxState = this._mapToTelnyxCallState(state);
341
- this._callState.next(telnyxState);
342
- // Start duration timer when call becomes active
343
- if (telnyxState === call_state_1.TelnyxCallState.ACTIVE && !this._startTime) {
344
- this._startDurationTimer();
345
- }
346
- // Stop duration timer when call ends
347
- if (call_state_1.CallStateHelpers.isTerminated(telnyxState)) {
252
+ }
253
+ /**
254
+ * Put the call on hold
255
+ */
256
+ async hold() {
257
+ if (!call_state_1.CallStateHelpers.canHold(this.currentState)) {
258
+ throw new Error(`Cannot hold call in state: ${this.currentState}`);
259
+ }
260
+ try {
261
+ await this._telnyxCall.hold();
262
+ }
263
+ catch (error) {
264
+ console.error('Failed to hold call:', error);
265
+ throw error;
266
+ }
267
+ }
268
+ /**
269
+ * Resume the call from hold
270
+ */
271
+ async resume() {
272
+ if (!call_state_1.CallStateHelpers.canResume(this.currentState)) {
273
+ throw new Error(`Cannot resume call in state: ${this.currentState}`);
274
+ }
275
+ try {
276
+ await this._telnyxCall.unhold();
277
+ }
278
+ catch (error) {
279
+ console.error('Failed to resume call:', error);
280
+ throw error;
281
+ }
282
+ }
283
+ /**
284
+ * Mute the call
285
+ */
286
+ async mute() {
287
+ if (!call_state_1.CallStateHelpers.canToggleMute(this.currentState)) {
288
+ throw new Error(`Cannot mute call in state: ${this.currentState}`);
289
+ }
290
+ try {
291
+ this._telnyxCall.mute();
292
+ this._isMuted.next(true);
293
+ }
294
+ catch (error) {
295
+ console.error('Failed to mute call:', error);
296
+ throw error;
297
+ }
298
+ }
299
+ /**
300
+ * Unmute the call
301
+ */
302
+ async unmute() {
303
+ if (!call_state_1.CallStateHelpers.canToggleMute(this.currentState)) {
304
+ throw new Error(`Cannot unmute call in state: ${this.currentState}`);
305
+ }
306
+ try {
307
+ this._telnyxCall.unmute();
308
+ this._isMuted.next(false);
309
+ }
310
+ catch (error) {
311
+ console.error('Failed to unmute call:', error);
312
+ throw error;
313
+ }
314
+ }
315
+ /**
316
+ * Toggle mute state
317
+ */
318
+ async toggleMute() {
319
+ if (this.currentIsMuted) {
320
+ await this.unmute();
321
+ }
322
+ else {
323
+ await this.mute();
324
+ }
325
+ }
326
+ /**
327
+ * Set the call to connecting state (used for push notification calls when answered via CallKit)
328
+ * @internal
329
+ */
330
+ setConnecting() {
331
+ console.log('Call: Setting state to CONNECTING for push notification answer');
332
+ this._callState.next(call_state_1.TelnyxCallState.CONNECTING);
333
+ }
334
+ /**
335
+ * Clean up resources when the call is disposed
336
+ */
337
+ dispose() {
348
338
  this._stopDurationTimer();
349
- }
350
- });
351
- }
352
- /**
353
- * Map Telnyx SDK call states to our simplified call states
354
- */
355
- _mapToTelnyxCallState(telnyxState) {
356
- // This mapping will depend on the actual Telnyx SDK call states
357
- // For now, using a basic mapping - this should be updated based on actual SDK
358
- switch (telnyxState) {
359
- case 'ringing':
360
- case 'new':
361
- return call_state_1.TelnyxCallState.RINGING;
362
- case 'active':
363
- case 'answered':
364
- return call_state_1.TelnyxCallState.ACTIVE;
365
- case 'held':
366
- return call_state_1.TelnyxCallState.HELD;
367
- case 'ended':
368
- case 'hangup':
369
- return call_state_1.TelnyxCallState.ENDED;
370
- case 'failed':
371
- case 'rejected':
372
- return call_state_1.TelnyxCallState.FAILED;
373
- default:
374
- console.warn(`Unknown call state: ${telnyxState}`);
375
- return call_state_1.TelnyxCallState.RINGING;
376
- }
377
- }
378
- /**
379
- * Start the duration timer
380
- */
381
- _startDurationTimer() {
382
- this._startTime = new Date();
383
- this._durationTimer = setInterval(() => {
384
- if (this._startTime) {
385
- const duration = Math.floor((Date.now() - this._startTime.getTime()) / 1000);
386
- this._duration.next(duration);
387
- }
388
- }, 1000);
389
- }
390
- /**
391
- * Stop the duration timer
392
- */
393
- _stopDurationTimer() {
394
- if (this._durationTimer) {
395
- clearInterval(this._durationTimer);
396
- this._durationTimer = undefined;
397
- }
398
- }
339
+ this._callState.complete();
340
+ this._isMuted.complete();
341
+ this._isHeld.complete();
342
+ this._duration.complete();
343
+ }
344
+ /**
345
+ * Set up listeners for the underlying Telnyx call
346
+ */
347
+ _setupCallListeners() {
348
+ // Map Telnyx call states to our simplified states
349
+ this._telnyxCall.on('telnyx.call.state', (call, state) => {
350
+ const telnyxState = this._mapToTelnyxCallState(state);
351
+ this._callState.next(telnyxState);
352
+ // Start duration timer when call becomes active
353
+ if (telnyxState === call_state_1.TelnyxCallState.ACTIVE && !this._startTime) {
354
+ this._startDurationTimer();
355
+ // Show ongoing call notification on Android when call becomes active
356
+ // This covers both locally answered calls and calls that become active from remote side
357
+ if (react_native_1.Platform.OS === 'android') {
358
+ (async () => {
359
+ try {
360
+ const { VoicePnBridge } = await Promise.resolve().then(() => __importStar(require('../internal/voice-pn-bridge')));
361
+ // Extract caller information based on call direction
362
+ let callerNumber;
363
+ let callerName;
364
+ if (this._isIncoming) {
365
+ // For incoming calls, use the remote caller ID (who's calling us)
366
+ callerNumber = this._telnyxCall.remoteCallerIdNumber;
367
+ callerName = this._telnyxCall.remoteCallerIdName;
368
+ }
369
+ else {
370
+ // For outgoing calls, use our own caller ID (what we're showing to them)
371
+ // These are the values we set when making the call
372
+ callerNumber = this._telnyxCall.localCallerIdNumber || this._originalCallerNumber;
373
+ callerName = this._telnyxCall.localCallerIdName || this._originalCallerName;
374
+ }
375
+ // Fallback logic for better notification display - avoid "Unknown" when possible
376
+ let displayName;
377
+ let displayNumber;
378
+ if (this._isIncoming) {
379
+ // For incoming calls: use caller name or fall back to caller number, then destination
380
+ displayName = callerName || callerNumber || this._destination;
381
+ displayNumber = callerNumber || this._destination;
382
+ }
383
+ else {
384
+ // For outgoing calls: use our caller ID or descriptive text
385
+ displayName = callerName || `${this._destination}`;
386
+ displayNumber = callerNumber || this._destination;
387
+ }
388
+ await VoicePnBridge.showOngoingCallNotification(displayName, displayNumber, this._callId);
389
+ console.log('Call: Showed ongoing call notification on Android (call active)', {
390
+ isIncoming: this._isIncoming,
391
+ callerName: displayName,
392
+ callerNumber: displayNumber,
393
+ });
394
+ }
395
+ catch (error) {
396
+ console.error('Call: Failed to show ongoing call notification on active:', error);
397
+ }
398
+ })();
399
+ }
400
+ }
401
+ // Stop duration timer when call ends
402
+ if (call_state_1.CallStateHelpers.isTerminated(telnyxState)) {
403
+ this._stopDurationTimer();
404
+ // Clean up ongoing call notification on Android when call ends
405
+ if (react_native_1.Platform.OS === 'android') {
406
+ (async () => {
407
+ try {
408
+ const { VoicePnBridge } = await Promise.resolve().then(() => __importStar(require('../internal/voice-pn-bridge')));
409
+ await VoicePnBridge.endCall(this._callId);
410
+ console.log('Call: Cleaned up ongoing call notification (call terminated)');
411
+ }
412
+ catch (error) {
413
+ console.error('Call: Failed to clean up ongoing call notification on termination:', error);
414
+ }
415
+ })();
416
+ }
417
+ }
418
+ });
419
+ }
420
+ /**
421
+ * Map Telnyx SDK call states to our simplified call states
422
+ */
423
+ _mapToTelnyxCallState(telnyxState) {
424
+ // This mapping will depend on the actual Telnyx SDK call states
425
+ // For now, using a basic mapping - this should be updated based on actual SDK
426
+ switch (telnyxState) {
427
+ case 'ringing':
428
+ case 'new':
429
+ return call_state_1.TelnyxCallState.RINGING;
430
+ case 'connecting':
431
+ return call_state_1.TelnyxCallState.CONNECTING;
432
+ case 'active':
433
+ case 'answered':
434
+ return call_state_1.TelnyxCallState.ACTIVE;
435
+ case 'held':
436
+ return call_state_1.TelnyxCallState.HELD;
437
+ case 'ended':
438
+ case 'hangup':
439
+ return call_state_1.TelnyxCallState.ENDED;
440
+ case 'failed':
441
+ case 'rejected':
442
+ return call_state_1.TelnyxCallState.FAILED;
443
+ case 'dropped':
444
+ return call_state_1.TelnyxCallState.DROPPED;
445
+ default:
446
+ console.warn(`Unknown call state: ${telnyxState}`);
447
+ return call_state_1.TelnyxCallState.RINGING;
448
+ }
449
+ }
450
+ /**
451
+ * Start the duration timer
452
+ */
453
+ _startDurationTimer() {
454
+ this._startTime = new Date();
455
+ this._durationTimer = setInterval(() => {
456
+ if (this._startTime) {
457
+ const duration = Math.floor((Date.now() - this._startTime.getTime()) / 1000);
458
+ this._duration.next(duration);
459
+ }
460
+ }, 1000);
461
+ }
462
+ /**
463
+ * Stop the duration timer
464
+ */
465
+ _stopDurationTimer() {
466
+ if (this._durationTimer) {
467
+ clearInterval(this._durationTimer);
468
+ this._durationTimer = undefined;
469
+ }
470
+ }
399
471
  }
400
472
  exports.Call = Call;