@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.
- package/TelnyxVoiceCommons.podspec +32 -0
- package/ios/CallKitBridge.m +44 -0
- package/ios/CallKitBridge.swift +879 -0
- package/ios/README.md +211 -0
- package/ios/VoicePnBridge.m +31 -0
- package/ios/VoicePnBridge.swift +87 -0
- package/lib/callkit/callkit-coordinator.d.ts +126 -0
- package/lib/callkit/callkit-coordinator.js +728 -0
- package/lib/callkit/callkit.d.ts +49 -0
- package/lib/callkit/callkit.js +262 -0
- package/lib/callkit/index.d.ts +4 -0
- package/lib/callkit/index.js +15 -0
- package/lib/callkit/use-callkit-coordinator.d.ts +21 -0
- package/lib/callkit/use-callkit-coordinator.js +53 -0
- package/lib/callkit/use-callkit.d.ts +28 -0
- package/lib/callkit/use-callkit.js +279 -0
- package/lib/context/TelnyxVoiceContext.d.ts +18 -0
- package/lib/context/TelnyxVoiceContext.js +18 -0
- package/lib/hooks/use-callkit-coordinator.d.ts +13 -0
- package/lib/hooks/use-callkit-coordinator.js +48 -0
- package/lib/hooks/useAppReadyNotifier.d.ts +9 -0
- package/lib/hooks/useAppReadyNotifier.js +25 -0
- package/lib/hooks/useAppStateHandler.d.ts +16 -0
- package/lib/hooks/useAppStateHandler.js +105 -0
- package/lib/index.d.ts +24 -0
- package/lib/index.js +66 -0
- package/lib/internal/CallKitHandler.d.ts +17 -0
- package/lib/internal/CallKitHandler.js +110 -0
- package/lib/internal/callkit-manager.d.ts +69 -0
- package/lib/internal/callkit-manager.js +326 -0
- package/lib/internal/calls/call-state-controller.d.ts +92 -0
- package/lib/internal/calls/call-state-controller.js +294 -0
- package/lib/internal/session/session-manager.d.ts +87 -0
- package/lib/internal/session/session-manager.js +385 -0
- package/lib/internal/user-defaults-helpers.d.ts +10 -0
- package/lib/internal/user-defaults-helpers.js +69 -0
- package/lib/internal/voice-pn-bridge.d.ts +14 -0
- package/lib/internal/voice-pn-bridge.js +5 -0
- package/lib/models/call-state.d.ts +61 -0
- package/lib/models/call-state.js +87 -0
- package/lib/models/call.d.ts +145 -0
- package/lib/models/call.js +372 -0
- package/lib/models/config.d.ts +64 -0
- package/lib/models/config.js +92 -0
- package/lib/models/connection-state.d.ts +34 -0
- package/lib/models/connection-state.js +50 -0
- package/lib/telnyx-voice-app.d.ts +48 -0
- package/lib/telnyx-voice-app.js +486 -0
- package/lib/telnyx-voip-client.d.ts +184 -0
- package/lib/telnyx-voip-client.js +386 -0
- package/package.json +104 -0
- package/src/callkit/callkit-coordinator.ts +846 -0
- package/src/callkit/callkit.ts +322 -0
- package/src/callkit/index.ts +4 -0
- package/src/callkit/use-callkit.ts +345 -0
- package/src/context/TelnyxVoiceContext.tsx +33 -0
- package/src/hooks/use-callkit-coordinator.ts +60 -0
- package/src/hooks/useAppReadyNotifier.ts +25 -0
- package/src/hooks/useAppStateHandler.ts +134 -0
- package/src/index.ts +56 -0
- package/src/internal/CallKitHandler.tsx +149 -0
- package/src/internal/callkit-manager.ts +335 -0
- package/src/internal/calls/call-state-controller.ts +384 -0
- package/src/internal/session/session-manager.ts +467 -0
- package/src/internal/user-defaults-helpers.ts +58 -0
- package/src/internal/voice-pn-bridge.ts +18 -0
- package/src/models/call-state.ts +98 -0
- package/src/models/call.ts +388 -0
- package/src/models/config.ts +125 -0
- package/src/models/connection-state.ts +50 -0
- package/src/telnyx-voice-app.tsx +690 -0
- package/src/telnyx-voip-client.ts +475 -0
- 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
|
+
}
|