@telnyx/react-voice-commons-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/TelnyxVoiceCommons.podspec +32 -0
  2. package/ios/CallKitBridge.m +44 -0
  3. package/ios/CallKitBridge.swift +879 -0
  4. package/ios/README.md +211 -0
  5. package/ios/VoicePnBridge.m +31 -0
  6. package/ios/VoicePnBridge.swift +87 -0
  7. package/lib/callkit/callkit-coordinator.d.ts +126 -0
  8. package/lib/callkit/callkit-coordinator.js +728 -0
  9. package/lib/callkit/callkit.d.ts +49 -0
  10. package/lib/callkit/callkit.js +262 -0
  11. package/lib/callkit/index.d.ts +4 -0
  12. package/lib/callkit/index.js +15 -0
  13. package/lib/callkit/use-callkit-coordinator.d.ts +21 -0
  14. package/lib/callkit/use-callkit-coordinator.js +53 -0
  15. package/lib/callkit/use-callkit.d.ts +28 -0
  16. package/lib/callkit/use-callkit.js +279 -0
  17. package/lib/context/TelnyxVoiceContext.d.ts +18 -0
  18. package/lib/context/TelnyxVoiceContext.js +18 -0
  19. package/lib/hooks/use-callkit-coordinator.d.ts +13 -0
  20. package/lib/hooks/use-callkit-coordinator.js +48 -0
  21. package/lib/hooks/useAppReadyNotifier.d.ts +9 -0
  22. package/lib/hooks/useAppReadyNotifier.js +25 -0
  23. package/lib/hooks/useAppStateHandler.d.ts +16 -0
  24. package/lib/hooks/useAppStateHandler.js +105 -0
  25. package/lib/index.d.ts +24 -0
  26. package/lib/index.js +66 -0
  27. package/lib/internal/CallKitHandler.d.ts +17 -0
  28. package/lib/internal/CallKitHandler.js +110 -0
  29. package/lib/internal/callkit-manager.d.ts +69 -0
  30. package/lib/internal/callkit-manager.js +326 -0
  31. package/lib/internal/calls/call-state-controller.d.ts +92 -0
  32. package/lib/internal/calls/call-state-controller.js +294 -0
  33. package/lib/internal/session/session-manager.d.ts +87 -0
  34. package/lib/internal/session/session-manager.js +385 -0
  35. package/lib/internal/user-defaults-helpers.d.ts +10 -0
  36. package/lib/internal/user-defaults-helpers.js +69 -0
  37. package/lib/internal/voice-pn-bridge.d.ts +14 -0
  38. package/lib/internal/voice-pn-bridge.js +5 -0
  39. package/lib/models/call-state.d.ts +61 -0
  40. package/lib/models/call-state.js +87 -0
  41. package/lib/models/call.d.ts +145 -0
  42. package/lib/models/call.js +372 -0
  43. package/lib/models/config.d.ts +64 -0
  44. package/lib/models/config.js +92 -0
  45. package/lib/models/connection-state.d.ts +34 -0
  46. package/lib/models/connection-state.js +50 -0
  47. package/lib/telnyx-voice-app.d.ts +48 -0
  48. package/lib/telnyx-voice-app.js +486 -0
  49. package/lib/telnyx-voip-client.d.ts +184 -0
  50. package/lib/telnyx-voip-client.js +386 -0
  51. package/package.json +104 -0
  52. package/src/callkit/callkit-coordinator.ts +846 -0
  53. package/src/callkit/callkit.ts +322 -0
  54. package/src/callkit/index.ts +4 -0
  55. package/src/callkit/use-callkit.ts +345 -0
  56. package/src/context/TelnyxVoiceContext.tsx +33 -0
  57. package/src/hooks/use-callkit-coordinator.ts +60 -0
  58. package/src/hooks/useAppReadyNotifier.ts +25 -0
  59. package/src/hooks/useAppStateHandler.ts +134 -0
  60. package/src/index.ts +56 -0
  61. package/src/internal/CallKitHandler.tsx +149 -0
  62. package/src/internal/callkit-manager.ts +335 -0
  63. package/src/internal/calls/call-state-controller.ts +384 -0
  64. package/src/internal/session/session-manager.ts +467 -0
  65. package/src/internal/user-defaults-helpers.ts +58 -0
  66. package/src/internal/voice-pn-bridge.ts +18 -0
  67. package/src/models/call-state.ts +98 -0
  68. package/src/models/call.ts +388 -0
  69. package/src/models/config.ts +125 -0
  70. package/src/models/connection-state.ts +50 -0
  71. package/src/telnyx-voice-app.tsx +690 -0
  72. package/src/telnyx-voip-client.ts +475 -0
  73. package/src/types/telnyx-sdk.d.ts +79 -0
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CallKitHandler = void 0;
7
+ const react_1 = require("react");
8
+ const react_native_1 = require("react-native");
9
+ const expo_router_1 = require("expo-router");
10
+ const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
11
+ const TelnyxVoiceContext_1 = require("../context/TelnyxVoiceContext");
12
+ // Global flag to ensure only one CallKitHandler is active
13
+ let isCallKitHandlerActive = false;
14
+ /**
15
+ * Internal CallKit handler for iOS push notifications
16
+ * This component is automatically included in TelnyxVoiceApp
17
+ *
18
+ * @internal - Users should not use this component directly
19
+ */
20
+ const CallKitHandler = ({ onLoginRequired, onNavigateToDialer, onNavigateBack, }) => {
21
+ const router = (0, expo_router_1.useRouter)();
22
+ const { voipClient } = (0, TelnyxVoiceContext_1.useTelnyxVoice)();
23
+ // Store active calls by CallKit UUID for coordination
24
+ const activeCallsRef = (0, react_1.useRef)(new Map());
25
+ (0, react_1.useEffect)(() => {
26
+ if (react_native_1.Platform.OS !== 'ios')
27
+ return;
28
+ if (isCallKitHandlerActive) {
29
+ console.log('CallKitHandler: Another instance is already active, skipping setup');
30
+ return;
31
+ }
32
+ isCallKitHandlerActive = true;
33
+ console.log('CallKitHandler: Setting up DeviceEventEmitter listeners (singleton instance)...');
34
+ react_native_1.DeviceEventEmitter.removeAllListeners('incomingVoIPCall');
35
+ react_native_1.DeviceEventEmitter.removeAllListeners('callKitAction');
36
+ const incomingCallListener = react_native_1.DeviceEventEmitter.addListener('incomingVoIPCall', async (eventData) => {
37
+ console.log('🔥 CallKitHandler: Incoming VoIP call received:', eventData);
38
+ if (eventData.action === 'connect_webrtc') {
39
+ await handleIncomingCall(eventData);
40
+ }
41
+ });
42
+ const callKitActionListener = react_native_1.DeviceEventEmitter.addListener('callKitAction', async (eventData) => {
43
+ console.log('🔥 CallKitHandler: CallKit action received:', eventData);
44
+ switch (eventData.action) {
45
+ case 'answer':
46
+ await handleAnswerCall(eventData);
47
+ break;
48
+ case 'end':
49
+ await handleEndCall(eventData);
50
+ break;
51
+ }
52
+ });
53
+ console.log('CallKitHandler: DeviceEventEmitter listeners set up successfully');
54
+ return () => {
55
+ console.log('CallKitHandler: Cleaning up DeviceEventEmitter listeners');
56
+ incomingCallListener.remove();
57
+ callKitActionListener.remove();
58
+ isCallKitHandlerActive = false;
59
+ };
60
+ }, []);
61
+ const handleIncomingCall = async (eventData) => {
62
+ console.log('CallKitHandler: Handling incoming call', {
63
+ callUUID: eventData.callUUID,
64
+ hasClient: !!voipClient,
65
+ });
66
+ // Store the push notification payload
67
+ await async_storage_1.default.setItem('@push_notification_payload', JSON.stringify(eventData.payload));
68
+ // Mark this call as being processed
69
+ activeCallsRef.current.set(eventData.callUUID, {
70
+ processing: true,
71
+ timestamp: Date.now(),
72
+ });
73
+ // Trigger login required callback if provided
74
+ if (onLoginRequired) {
75
+ onLoginRequired(eventData.payload);
76
+ }
77
+ };
78
+ const handleAnswerCall = async (eventData) => {
79
+ console.log('CallKitHandler: User answered call via CallKit', {
80
+ callUUID: eventData.callUUID,
81
+ isTrackedCall: activeCallsRef.current.has(eventData.callUUID),
82
+ });
83
+ // Navigate to dialer after answering
84
+ if (onNavigateToDialer) {
85
+ onNavigateToDialer();
86
+ }
87
+ else {
88
+ router.replace('/dialer');
89
+ }
90
+ };
91
+ const handleEndCall = async (eventData) => {
92
+ console.log('CallKitHandler: User ended call via CallKit', {
93
+ callUUID: eventData.callUUID,
94
+ isTrackedCall: activeCallsRef.current.has(eventData.callUUID),
95
+ });
96
+ // Clean up our local tracking info
97
+ activeCallsRef.current.delete(eventData.callUUID);
98
+ await async_storage_1.default.removeItem('@push_notification_payload');
99
+ // Navigate back after call ends
100
+ if (onNavigateBack) {
101
+ onNavigateBack();
102
+ }
103
+ else {
104
+ router.replace('/dialer');
105
+ }
106
+ };
107
+ // This component doesn't render anything, it just handles events
108
+ return null;
109
+ };
110
+ exports.CallKitHandler = CallKitHandler;
@@ -0,0 +1,69 @@
1
+ import { Call } from '../models/call';
2
+ import { TelnyxCallState } from '../models/call-state';
3
+ /**
4
+ * Internal CallKit integration manager for react-voice-commons.
5
+ *
6
+ * This class automatically handles CallKit integration for all calls
7
+ * without exposing complexity to the consumer components.
8
+ *
9
+ * @internal
10
+ */
11
+ export declare class CallKitManager {
12
+ private _callMappings;
13
+ private _reverseCallMappings;
14
+ private _isEnabled;
15
+ private _callMap?;
16
+ constructor();
17
+ /**
18
+ * Set up CallKit event listeners to handle user actions
19
+ */
20
+ private _setupCallKitEventListeners;
21
+ /**
22
+ * Initialize CallKit integration
23
+ */
24
+ initialize(onAnswerCall: (callId: string) => void, onEndCall: (callId: string) => void): void;
25
+ /**
26
+ * Handle outgoing call - start CallKit session
27
+ */
28
+ handleOutgoingCall(call: Call): Promise<string | null>;
29
+ /**
30
+ * Handle incoming call - show CallKit UI
31
+ */
32
+ handleIncomingCall(call: Call): Promise<string | null>;
33
+ /**
34
+ * Update call state in CallKit
35
+ */
36
+ updateCallState(call: Call, state: TelnyxCallState): Promise<void>;
37
+ /**
38
+ * End CallKit session
39
+ */
40
+ endCall(call: Call): Promise<void>;
41
+ /**
42
+ * Handle CallKit answer action
43
+ */
44
+ private _handleCallKitAnswer;
45
+ /**
46
+ * Handle CallKit end action
47
+ */
48
+ private _handleCallKitEnd;
49
+ /**
50
+ * Set the call map reference for handling CallKit actions
51
+ */
52
+ setCallMap(callMap: Map<string, Call>): void;
53
+ /**
54
+ * Check if CallKit is available
55
+ */
56
+ get isAvailable(): boolean;
57
+ /**
58
+ * Get CallKit UUID for a call
59
+ */
60
+ getCallKitUUID(callId: string): string | null;
61
+ /**
62
+ * Generate a UUID for CallKit
63
+ */
64
+ private generateUUID;
65
+ /**
66
+ * Dispose of the CallKit manager
67
+ */
68
+ dispose(): void;
69
+ }
@@ -0,0 +1,326 @@
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;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
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;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CallKitManager = void 0;
37
+ const react_native_1 = require("react-native");
38
+ const callkit_1 = __importStar(require("../callkit/callkit"));
39
+ const call_state_1 = require("../models/call-state");
40
+ /**
41
+ * Internal CallKit integration manager for react-voice-commons.
42
+ *
43
+ * This class automatically handles CallKit integration for all calls
44
+ * without exposing complexity to the consumer components.
45
+ *
46
+ * @internal
47
+ */
48
+ class CallKitManager {
49
+ constructor() {
50
+ this._callMappings = new Map(); // Call ID -> CallKit UUID
51
+ this._reverseCallMappings = new Map(); // CallKit UUID -> Call ID
52
+ this._isEnabled = false;
53
+ // DISABLED: Using external CallKit implementations (useCallKit, useCallKitCoordinator)
54
+ this._isEnabled = false; // Disable to prevent conflicts
55
+ console.log('🔧 CallKitManager: Initialized');
56
+ console.log('🔧 CallKitManager: Platform.OS =', react_native_1.Platform.OS);
57
+ console.log('🔧 CallKitManager: CallKit.isAvailable() =', callkit_1.default.isAvailable());
58
+ console.log('🔧 CallKitManager: _isEnabled =', this._isEnabled, '(DISABLED to use external CallKit)');
59
+ // DISABLED: Conflicts with external CallKit implementations (useCallKit, useCallKitCoordinator)
60
+ // if (this._isEnabled) {
61
+ // this._setupCallKitEventListeners();
62
+ // }
63
+ console.log('🔧 CallKitManager: Automatic CallKit integration DISABLED - using external CallKit implementations');
64
+ }
65
+ /**
66
+ * Set up CallKit event listeners to handle user actions
67
+ */
68
+ _setupCallKitEventListeners() {
69
+ console.log('🔧 CallKitManager: Setting up CallKit event listeners');
70
+ // Listen for answer actions from CallKit
71
+ callkit_1.default.onAnswerCall((event) => {
72
+ console.log('📞 CallKitManager: CallKit answer event received:', event);
73
+ this._handleCallKitAnswer(event.callUUID);
74
+ });
75
+ // Listen for end actions from CallKit
76
+ callkit_1.default.onEndCall((event) => {
77
+ console.log('📞 CallKitManager: CallKit end event received:', event);
78
+ this._handleCallKitEnd(event.callUUID);
79
+ });
80
+ console.log('🔧 CallKitManager: CallKit event listeners set up');
81
+ }
82
+ /**
83
+ * Initialize CallKit integration
84
+ */
85
+ initialize(onAnswerCall, onEndCall) {
86
+ if (!this._isEnabled)
87
+ return;
88
+ try {
89
+ // This would need to be called from a React component context
90
+ // For now, we'll handle this differently in the call-state-controller
91
+ console.log('CallKitManager: CallKit integration initialized');
92
+ }
93
+ catch (error) {
94
+ console.warn('CallKitManager: Failed to initialize CallKit:', error);
95
+ this._isEnabled = false;
96
+ }
97
+ }
98
+ /**
99
+ * Handle outgoing call - start CallKit session
100
+ */
101
+ async handleOutgoingCall(call) {
102
+ console.log('🔵 CallKitManager.handleOutgoingCall() CALLED');
103
+ console.log('🔵 CallKitManager: call.callId =', call.callId);
104
+ console.log('🔵 CallKitManager: call.destination =', call.destination);
105
+ console.log('🔵 CallKitManager: call.isIncoming =', call.isIncoming);
106
+ console.log('🔵 CallKitManager: _isEnabled =', this._isEnabled);
107
+ if (!this._isEnabled) {
108
+ console.log('🔴 CallKitManager: CallKit NOT ENABLED - skipping outgoing call');
109
+ return null;
110
+ }
111
+ try {
112
+ console.log('🔵 CallKitManager: Starting CallKit session for outgoing call:', call.callId);
113
+ const callKitUUID = callkit_1.default.generateCallUUID();
114
+ console.log('🔵 CallKitManager: Generated CallKit UUID:', callKitUUID);
115
+ // Call the native CallKit bridge
116
+ console.log('🔵 CallKitManager: Calling native CallKit.startOutgoingCall...');
117
+ const success = await callkit_1.default.startOutgoingCall(callKitUUID, call.destination, call.destination // Using destination as display name for now
118
+ );
119
+ if (success) {
120
+ console.log('� CallKitManager: Native CallKit.startOutgoingCall SUCCESS!');
121
+ this._callMappings.set(call.callId, callKitUUID);
122
+ console.log('� CallKitManager: Stored call mapping:', call.callId, '->', callKitUUID);
123
+ return callKitUUID;
124
+ }
125
+ else {
126
+ console.log('� CallKitManager: Native CallKit.startOutgoingCall FAILED');
127
+ return null;
128
+ }
129
+ }
130
+ catch (error) {
131
+ console.error('🔴 CallKitManager: Failed to start outgoing call:', error);
132
+ return null;
133
+ }
134
+ }
135
+ /**
136
+ * Handle incoming call - show CallKit UI
137
+ */
138
+ async handleIncomingCall(call) {
139
+ console.log('🟡 CallKitManager.handleIncomingCall() CALLED');
140
+ console.log('🟡 CallKitManager: call.callId =', call.callId);
141
+ console.log('🟡 CallKitManager: call.destination =', call.destination);
142
+ console.log('🟡 CallKitManager: call.isIncoming =', call.isIncoming);
143
+ console.log('🟡 CallKitManager: _isEnabled =', this._isEnabled);
144
+ if (!this._isEnabled) {
145
+ console.log('🔴 CallKitManager: CallKit NOT ENABLED - skipping incoming call');
146
+ return null;
147
+ }
148
+ try {
149
+ console.log('🟡 CallKitManager: Showing CallKit UI for incoming call:', call.callId);
150
+ const callKitUUID = callkit_1.default.generateCallUUID();
151
+ console.log('🟡 CallKitManager: Generated CallKit UUID for incoming call:', callKitUUID);
152
+ // Call the native CallKit bridge
153
+ console.log('🟡 CallKitManager: Calling native CallKit.reportIncomingCall...');
154
+ // For incoming calls, the call.destination contains the caller number
155
+ // We should use a more descriptive name if available
156
+ const handle = call.destination;
157
+ const displayName = call.destination; // For now, use the number as display name too
158
+ console.log('🟡 CallKitManager: Using handle:', handle, 'displayName:', displayName);
159
+ const success = await callkit_1.default.reportIncomingCall(callKitUUID, handle, displayName);
160
+ if (success) {
161
+ console.log('� CallKitManager: Native CallKit.reportIncomingCall SUCCESS!');
162
+ this._callMappings.set(call.callId, callKitUUID);
163
+ console.log('� CallKitManager: Stored incoming call mapping:', call.callId, '->', callKitUUID);
164
+ return callKitUUID;
165
+ }
166
+ else {
167
+ console.log('� CallKitManager: Native CallKit.reportIncomingCall FAILED');
168
+ return null;
169
+ }
170
+ }
171
+ catch (error) {
172
+ console.error('🔴 CallKitManager: Failed to show incoming call:', error);
173
+ return null;
174
+ }
175
+ }
176
+ /**
177
+ * Update call state in CallKit
178
+ */
179
+ async updateCallState(call, state) {
180
+ if (!this._isEnabled)
181
+ return;
182
+ const callKitUUID = this._callMappings.get(call.callId);
183
+ if (!callKitUUID)
184
+ return;
185
+ try {
186
+ console.log(`🔧 CallKitManager: Updating CallKit state for call ${call.callId} to ${state}`);
187
+ console.log(`🔧 CallKitManager: CallKit UUID: ${callKitUUID}`);
188
+ switch (state) {
189
+ case call_state_1.TelnyxCallState.ACTIVE:
190
+ console.log('🟢 CallKitManager: Reporting call connected to CallKit');
191
+ await callkit_1.default.reportCallConnected(callKitUUID);
192
+ break;
193
+ case call_state_1.TelnyxCallState.ENDED:
194
+ console.log('🔴 CallKitManager: Reporting call ended to CallKit (remote ended)');
195
+ await callkit_1.default.reportCallEnded(callKitUUID, callkit_1.CallEndReason.RemoteEnded);
196
+ this._callMappings.delete(call.callId);
197
+ break;
198
+ case call_state_1.TelnyxCallState.FAILED:
199
+ console.log('🔴 CallKitManager: Reporting call ended to CallKit (failed)');
200
+ await callkit_1.default.reportCallEnded(callKitUUID, callkit_1.CallEndReason.Failed);
201
+ this._callMappings.delete(call.callId);
202
+ break;
203
+ case call_state_1.TelnyxCallState.RINGING:
204
+ console.log('🟡 CallKitManager: Call ringing - no CallKit update needed');
205
+ break;
206
+ default:
207
+ console.log(`🔧 CallKitManager: Unhandled state: ${state}`);
208
+ break;
209
+ }
210
+ }
211
+ catch (error) {
212
+ console.error('🔴 CallKitManager: Failed to update call state:', error);
213
+ }
214
+ }
215
+ /**
216
+ * End CallKit session
217
+ */
218
+ async endCall(call) {
219
+ if (!this._isEnabled)
220
+ return;
221
+ const callKitUUID = this._callMappings.get(call.callId);
222
+ if (!callKitUUID)
223
+ return;
224
+ try {
225
+ console.log('🔴 CallKitManager: Ending CallKit session for call:', call.callId);
226
+ console.log('🔴 CallKitManager: CallKit UUID:', callKitUUID);
227
+ // Report call ended with user-initiated reason
228
+ await callkit_1.default.reportCallEnded(callKitUUID, callkit_1.CallEndReason.RemoteEnded);
229
+ this._callMappings.delete(call.callId);
230
+ console.log('🔴 CallKitManager: CallKit session ended successfully');
231
+ }
232
+ catch (error) {
233
+ console.error('🔴 CallKitManager: Failed to end call:', error);
234
+ }
235
+ }
236
+ /**
237
+ * Handle CallKit answer action
238
+ */
239
+ _handleCallKitAnswer(callKitUUID) {
240
+ console.log('📞 CallKitManager: Handling CallKit answer for UUID:', callKitUUID);
241
+ // Find the call ID from the CallKit UUID
242
+ const callId = this._reverseCallMappings.get(callKitUUID);
243
+ if (!callId) {
244
+ console.error('🔴 CallKitManager: No call found for CallKit UUID:', callKitUUID);
245
+ return;
246
+ }
247
+ console.log('📞 CallKitManager: Found call ID:', callId);
248
+ // Find the call object and answer it
249
+ if (this._callMap) {
250
+ const call = this._callMap.get(callId);
251
+ if (call) {
252
+ console.log('📞 CallKitManager: Answering call:', callId);
253
+ call.answer();
254
+ }
255
+ else {
256
+ console.error('🔴 CallKitManager: Call object not found for ID:', callId);
257
+ }
258
+ }
259
+ else {
260
+ console.error('🔴 CallKitManager: Call map not available');
261
+ }
262
+ }
263
+ /**
264
+ * Handle CallKit end action
265
+ */
266
+ _handleCallKitEnd(callKitUUID) {
267
+ console.log('📞 CallKitManager: Handling CallKit end for UUID:', callKitUUID);
268
+ // Find the call ID from the CallKit UUID
269
+ const callId = this._reverseCallMappings.get(callKitUUID);
270
+ if (!callId) {
271
+ console.error('🔴 CallKitManager: No call found for CallKit UUID:', callKitUUID);
272
+ return;
273
+ }
274
+ console.log('📞 CallKitManager: Found call ID:', callId);
275
+ // Find the call object and hang up
276
+ if (this._callMap) {
277
+ const call = this._callMap.get(callId);
278
+ if (call) {
279
+ console.log('📞 CallKitManager: Hanging up call:', callId);
280
+ call.hangup();
281
+ }
282
+ else {
283
+ console.error('🔴 CallKitManager: Call object not found for ID:', callId);
284
+ }
285
+ }
286
+ else {
287
+ console.error('🔴 CallKitManager: Call map not available');
288
+ }
289
+ }
290
+ /**
291
+ * Set the call map reference for handling CallKit actions
292
+ */
293
+ setCallMap(callMap) {
294
+ this._callMap = callMap;
295
+ console.log('🔧 CallKitManager: Call map reference set');
296
+ }
297
+ /**
298
+ * Check if CallKit is available
299
+ */
300
+ get isAvailable() {
301
+ return this._isEnabled;
302
+ }
303
+ /**
304
+ * Get CallKit UUID for a call
305
+ */
306
+ getCallKitUUID(callId) {
307
+ return this._callMappings.get(callId) || null;
308
+ }
309
+ /**
310
+ * Generate a UUID for CallKit
311
+ */
312
+ generateUUID() {
313
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
314
+ const r = (Math.random() * 16) | 0;
315
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
316
+ return v.toString(16);
317
+ });
318
+ }
319
+ /**
320
+ * Dispose of the CallKit manager
321
+ */
322
+ dispose() {
323
+ this._callMappings.clear();
324
+ }
325
+ }
326
+ exports.CallKitManager = CallKitManager;
@@ -0,0 +1,92 @@
1
+ import { Observable } from 'rxjs';
2
+ import { Call } from '../../models/call';
3
+ import { SessionManager } from '../session/session-manager';
4
+ /**
5
+ * Central state machine for call management.
6
+ *
7
+ * This class manages all active calls, handles call state transitions,
8
+ * and provides reactive streams for call-related state changes.
9
+ */
10
+ export declare class CallStateController {
11
+ private readonly _sessionManager;
12
+ private readonly _calls;
13
+ private readonly _callMap;
14
+ private _disposed;
15
+ private _isWaitingForInvite?;
16
+ private _onInviteAutoAccepted?;
17
+ constructor(_sessionManager: SessionManager);
18
+ /**
19
+ * Observable stream of all current calls
20
+ */
21
+ get calls$(): Observable<Call[]>;
22
+ /**
23
+ * Observable stream of the currently active call
24
+ */
25
+ get activeCall$(): Observable<Call | null>;
26
+ /**
27
+ * Current list of calls (synchronous access)
28
+ */
29
+ get currentCalls(): Call[];
30
+ /**
31
+ * Current active call (synchronous access)
32
+ */
33
+ get currentActiveCall(): Call | null;
34
+ /**
35
+ * Set a call to connecting state (used for push notification calls when answered via CallKit)
36
+ * @param callId The ID of the call to set to connecting state
37
+ */
38
+ setCallConnecting(callId: string): void;
39
+ /**
40
+ * Find a call by its underlying Telnyx call ID
41
+ * @param telnyxCall The Telnyx call object to find
42
+ */
43
+ findCallByTelnyxCall(telnyxCall: any): Call | null;
44
+ /**
45
+ * Initialize client listeners when the Telnyx client becomes available
46
+ * This should be called by the session manager after client creation
47
+ */
48
+ initializeClientListeners(): void;
49
+ /**
50
+ * Initiate a new outgoing call
51
+ */
52
+ newCall(destination: string, callerName?: string, callerNumber?: string, debug?: boolean): Promise<Call>;
53
+ /**
54
+ * Set callbacks for waiting for invite logic (used for push notifications)
55
+ */
56
+ setWaitingForInviteCallbacks(callbacks: {
57
+ isWaitingForInvite: () => boolean;
58
+ onInviteAutoAccepted: () => void;
59
+ }): void;
60
+ /**
61
+ * Dispose of the controller and clean up resources
62
+ */
63
+ dispose(): void;
64
+ /**
65
+ * Set up event listeners for the Telnyx client
66
+ */
67
+ private _setupClientListeners;
68
+ /**
69
+ * Handle incoming call
70
+ */
71
+ private _handleIncomingCall;
72
+ /**
73
+ * Handle call state changes from the Telnyx client
74
+ */
75
+ private _handleCallStateChange;
76
+ /**
77
+ * Handle call updates from notifications
78
+ */
79
+ private _handleCallUpdate;
80
+ /**
81
+ * Add a call to our tracking
82
+ */
83
+ private _addCall;
84
+ /**
85
+ * Remove a call from our tracking
86
+ */
87
+ private _removeCall;
88
+ /**
89
+ * Generate a unique call ID
90
+ */
91
+ private _generateCallId;
92
+ }