@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,335 @@
1
+ import { Platform } from 'react-native';
2
+ import CallKit, { CallEndReason } from '../callkit/callkit';
3
+ import { Call } from '../models/call';
4
+ import { TelnyxCallState } from '../models/call-state';
5
+
6
+ /**
7
+ * Internal CallKit integration manager for react-voice-commons.
8
+ *
9
+ * This class automatically handles CallKit integration for all calls
10
+ * without exposing complexity to the consumer components.
11
+ *
12
+ * @internal
13
+ */
14
+ export class CallKitManager {
15
+ private _callMappings = new Map<string, string>(); // Call ID -> CallKit UUID
16
+ private _reverseCallMappings = new Map<string, string>(); // CallKit UUID -> Call ID
17
+ private _isEnabled = false;
18
+ private _callMap?: Map<string, Call>; // Reference to call map from call state controller
19
+
20
+ constructor() {
21
+ // DISABLED: Using external CallKit implementations (useCallKit, useCallKitCoordinator)
22
+ this._isEnabled = false; // Disable to prevent conflicts
23
+ console.log('🔧 CallKitManager: Initialized');
24
+ console.log('🔧 CallKitManager: Platform.OS =', Platform.OS);
25
+ console.log('🔧 CallKitManager: CallKit.isAvailable() =', CallKit.isAvailable());
26
+ console.log(
27
+ '🔧 CallKitManager: _isEnabled =',
28
+ this._isEnabled,
29
+ '(DISABLED to use external CallKit)'
30
+ );
31
+
32
+ // DISABLED: Conflicts with external CallKit implementations (useCallKit, useCallKitCoordinator)
33
+ // if (this._isEnabled) {
34
+ // this._setupCallKitEventListeners();
35
+ // }
36
+ console.log(
37
+ '🔧 CallKitManager: Automatic CallKit integration DISABLED - using external CallKit implementations'
38
+ );
39
+ }
40
+
41
+ /**
42
+ * Set up CallKit event listeners to handle user actions
43
+ */
44
+ private _setupCallKitEventListeners(): void {
45
+ console.log('🔧 CallKitManager: Setting up CallKit event listeners');
46
+
47
+ // Listen for answer actions from CallKit
48
+ CallKit.onAnswerCall((event) => {
49
+ console.log('📞 CallKitManager: CallKit answer event received:', event);
50
+ this._handleCallKitAnswer(event.callUUID);
51
+ });
52
+
53
+ // Listen for end actions from CallKit
54
+ CallKit.onEndCall((event) => {
55
+ console.log('📞 CallKitManager: CallKit end event received:', event);
56
+ this._handleCallKitEnd(event.callUUID);
57
+ });
58
+
59
+ console.log('🔧 CallKitManager: CallKit event listeners set up');
60
+ }
61
+
62
+ /**
63
+ * Initialize CallKit integration
64
+ */
65
+ initialize(onAnswerCall: (callId: string) => void, onEndCall: (callId: string) => void) {
66
+ if (!this._isEnabled) return;
67
+
68
+ try {
69
+ // This would need to be called from a React component context
70
+ // For now, we'll handle this differently in the call-state-controller
71
+ console.log('CallKitManager: CallKit integration initialized');
72
+ } catch (error) {
73
+ console.warn('CallKitManager: Failed to initialize CallKit:', error);
74
+ this._isEnabled = false;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Handle outgoing call - start CallKit session
80
+ */
81
+ async handleOutgoingCall(call: Call): Promise<string | null> {
82
+ console.log('🔵 CallKitManager.handleOutgoingCall() CALLED');
83
+ console.log('🔵 CallKitManager: call.callId =', call.callId);
84
+ console.log('🔵 CallKitManager: call.destination =', call.destination);
85
+ console.log('🔵 CallKitManager: call.isIncoming =', call.isIncoming);
86
+ console.log('🔵 CallKitManager: _isEnabled =', this._isEnabled);
87
+
88
+ if (!this._isEnabled) {
89
+ console.log('🔴 CallKitManager: CallKit NOT ENABLED - skipping outgoing call');
90
+ return null;
91
+ }
92
+
93
+ try {
94
+ console.log('🔵 CallKitManager: Starting CallKit session for outgoing call:', call.callId);
95
+
96
+ const callKitUUID = CallKit.generateCallUUID();
97
+ console.log('🔵 CallKitManager: Generated CallKit UUID:', callKitUUID);
98
+
99
+ // Call the native CallKit bridge
100
+ console.log('🔵 CallKitManager: Calling native CallKit.startOutgoingCall...');
101
+ const success = await CallKit.startOutgoingCall(
102
+ callKitUUID,
103
+ call.destination,
104
+ call.destination // Using destination as display name for now
105
+ );
106
+
107
+ if (success) {
108
+ console.log('� CallKitManager: Native CallKit.startOutgoingCall SUCCESS!');
109
+ this._callMappings.set(call.callId, callKitUUID);
110
+ console.log('� CallKitManager: Stored call mapping:', call.callId, '->', callKitUUID);
111
+ return callKitUUID;
112
+ } else {
113
+ console.log('� CallKitManager: Native CallKit.startOutgoingCall FAILED');
114
+ return null;
115
+ }
116
+ } catch (error) {
117
+ console.error('🔴 CallKitManager: Failed to start outgoing call:', error);
118
+ return null;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Handle incoming call - show CallKit UI
124
+ */
125
+ async handleIncomingCall(call: Call): Promise<string | null> {
126
+ console.log('🟡 CallKitManager.handleIncomingCall() CALLED');
127
+ console.log('🟡 CallKitManager: call.callId =', call.callId);
128
+ console.log('🟡 CallKitManager: call.destination =', call.destination);
129
+ console.log('🟡 CallKitManager: call.isIncoming =', call.isIncoming);
130
+ console.log('🟡 CallKitManager: _isEnabled =', this._isEnabled);
131
+
132
+ if (!this._isEnabled) {
133
+ console.log('🔴 CallKitManager: CallKit NOT ENABLED - skipping incoming call');
134
+ return null;
135
+ }
136
+
137
+ try {
138
+ console.log('🟡 CallKitManager: Showing CallKit UI for incoming call:', call.callId);
139
+
140
+ const callKitUUID = CallKit.generateCallUUID();
141
+ console.log('🟡 CallKitManager: Generated CallKit UUID for incoming call:', callKitUUID);
142
+
143
+ // Call the native CallKit bridge
144
+ console.log('🟡 CallKitManager: Calling native CallKit.reportIncomingCall...');
145
+
146
+ // For incoming calls, the call.destination contains the caller number
147
+ // We should use a more descriptive name if available
148
+ const handle = call.destination;
149
+ const displayName = call.destination; // For now, use the number as display name too
150
+
151
+ console.log('🟡 CallKitManager: Using handle:', handle, 'displayName:', displayName);
152
+
153
+ const success = await CallKit.reportIncomingCall(callKitUUID, handle, displayName);
154
+
155
+ if (success) {
156
+ console.log('� CallKitManager: Native CallKit.reportIncomingCall SUCCESS!');
157
+ this._callMappings.set(call.callId, callKitUUID);
158
+ console.log(
159
+ '� CallKitManager: Stored incoming call mapping:',
160
+ call.callId,
161
+ '->',
162
+ callKitUUID
163
+ );
164
+ return callKitUUID;
165
+ } else {
166
+ console.log('� CallKitManager: Native CallKit.reportIncomingCall FAILED');
167
+ return null;
168
+ }
169
+ } catch (error) {
170
+ console.error('🔴 CallKitManager: Failed to show incoming call:', error);
171
+ return null;
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Update call state in CallKit
177
+ */
178
+ async updateCallState(call: Call, state: TelnyxCallState): Promise<void> {
179
+ if (!this._isEnabled) return;
180
+
181
+ const callKitUUID = this._callMappings.get(call.callId);
182
+ if (!callKitUUID) return;
183
+
184
+ try {
185
+ console.log(`🔧 CallKitManager: Updating CallKit state for call ${call.callId} to ${state}`);
186
+ console.log(`🔧 CallKitManager: CallKit UUID: ${callKitUUID}`);
187
+
188
+ switch (state) {
189
+ case TelnyxCallState.ACTIVE:
190
+ console.log('🟢 CallKitManager: Reporting call connected to CallKit');
191
+ await CallKit.reportCallConnected(callKitUUID);
192
+ break;
193
+ case TelnyxCallState.ENDED:
194
+ console.log('🔴 CallKitManager: Reporting call ended to CallKit (remote ended)');
195
+ await CallKit.reportCallEnded(callKitUUID, CallEndReason.RemoteEnded);
196
+ this._callMappings.delete(call.callId);
197
+ break;
198
+ case TelnyxCallState.FAILED:
199
+ console.log('🔴 CallKitManager: Reporting call ended to CallKit (failed)');
200
+ await CallKit.reportCallEnded(callKitUUID, CallEndReason.Failed);
201
+ this._callMappings.delete(call.callId);
202
+ break;
203
+ case 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
+ } catch (error) {
211
+ console.error('🔴 CallKitManager: Failed to update call state:', error);
212
+ }
213
+ }
214
+
215
+ /**
216
+ * End CallKit session
217
+ */
218
+ async endCall(call: Call): Promise<void> {
219
+ if (!this._isEnabled) return;
220
+
221
+ const callKitUUID = this._callMappings.get(call.callId);
222
+ if (!callKitUUID) return;
223
+
224
+ try {
225
+ console.log('🔴 CallKitManager: Ending CallKit session for call:', call.callId);
226
+ console.log('🔴 CallKitManager: CallKit UUID:', callKitUUID);
227
+
228
+ // Report call ended with user-initiated reason
229
+ await CallKit.reportCallEnded(callKitUUID, CallEndReason.RemoteEnded);
230
+ this._callMappings.delete(call.callId);
231
+
232
+ console.log('🔴 CallKitManager: CallKit session ended successfully');
233
+ } catch (error) {
234
+ console.error('🔴 CallKitManager: Failed to end call:', error);
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Handle CallKit answer action
240
+ */
241
+ private _handleCallKitAnswer(callKitUUID: string): void {
242
+ console.log('📞 CallKitManager: Handling CallKit answer for UUID:', callKitUUID);
243
+
244
+ // Find the call ID from the CallKit UUID
245
+ const callId = this._reverseCallMappings.get(callKitUUID);
246
+ if (!callId) {
247
+ console.error('🔴 CallKitManager: No call found for CallKit UUID:', callKitUUID);
248
+ return;
249
+ }
250
+
251
+ console.log('📞 CallKitManager: Found call ID:', callId);
252
+
253
+ // Find the call object and answer it
254
+ if (this._callMap) {
255
+ const call = this._callMap.get(callId);
256
+ if (call) {
257
+ console.log('📞 CallKitManager: Answering call:', callId);
258
+ call.answer();
259
+ } else {
260
+ console.error('🔴 CallKitManager: Call object not found for ID:', callId);
261
+ }
262
+ } else {
263
+ console.error('🔴 CallKitManager: Call map not available');
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Handle CallKit end action
269
+ */
270
+ private _handleCallKitEnd(callKitUUID: string): void {
271
+ console.log('📞 CallKitManager: Handling CallKit end for UUID:', callKitUUID);
272
+
273
+ // Find the call ID from the CallKit UUID
274
+ const callId = this._reverseCallMappings.get(callKitUUID);
275
+ if (!callId) {
276
+ console.error('🔴 CallKitManager: No call found for CallKit UUID:', callKitUUID);
277
+ return;
278
+ }
279
+
280
+ console.log('📞 CallKitManager: Found call ID:', callId);
281
+
282
+ // Find the call object and hang up
283
+ if (this._callMap) {
284
+ const call = this._callMap.get(callId);
285
+ if (call) {
286
+ console.log('📞 CallKitManager: Hanging up call:', callId);
287
+ call.hangup();
288
+ } else {
289
+ console.error('🔴 CallKitManager: Call object not found for ID:', callId);
290
+ }
291
+ } else {
292
+ console.error('🔴 CallKitManager: Call map not available');
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Set the call map reference for handling CallKit actions
298
+ */
299
+ setCallMap(callMap: Map<string, Call>): void {
300
+ this._callMap = callMap;
301
+ console.log('🔧 CallKitManager: Call map reference set');
302
+ }
303
+
304
+ /**
305
+ * Check if CallKit is available
306
+ */
307
+ get isAvailable(): boolean {
308
+ return this._isEnabled;
309
+ }
310
+
311
+ /**
312
+ * Get CallKit UUID for a call
313
+ */
314
+ getCallKitUUID(callId: string): string | null {
315
+ return this._callMappings.get(callId) || null;
316
+ }
317
+
318
+ /**
319
+ * Generate a UUID for CallKit
320
+ */
321
+ private generateUUID(): string {
322
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
323
+ const r = (Math.random() * 16) | 0;
324
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
325
+ return v.toString(16);
326
+ });
327
+ }
328
+
329
+ /**
330
+ * Dispose of the CallKit manager
331
+ */
332
+ dispose() {
333
+ this._callMappings.clear();
334
+ }
335
+ }