@telnyx/react-voice-commons-sdk 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +483 -0
- package/TelnyxVoiceCommons.podspec +31 -31
- package/ios/CallKitBridge.m +43 -43
- package/ios/CallKitBridge.swift +874 -879
- package/ios/README.md +211 -211
- package/ios/VoicePnBridge.m +30 -30
- package/ios/VoicePnBridge.swift +86 -86
- package/lib/callkit/callkit-coordinator.d.ts +2 -5
- package/lib/callkit/callkit-coordinator.js +15 -32
- package/lib/callkit/use-callkit-coordinator.d.ts +21 -21
- package/lib/callkit/use-callkit-coordinator.js +53 -53
- package/lib/hooks/useNetworkStateHandler.d.ts +0 -0
- package/lib/hooks/useNetworkStateHandler.js +0 -0
- package/lib/internal/calls/call-state-controller.d.ts +2 -10
- package/lib/internal/calls/call-state-controller.js +48 -54
- package/lib/internal/session/session-manager.d.ts +1 -5
- package/lib/internal/session/session-manager.js +35 -25
- package/lib/internal/voice-pn-bridge.d.ts +103 -1
- package/lib/internal/voice-pn-bridge.js +209 -2
- package/lib/models/call-state.d.ts +3 -1
- package/lib/models/call-state.js +5 -1
- package/lib/models/call.d.ts +31 -3
- package/lib/models/call.js +105 -5
- package/lib/telnyx-voice-app.js +78 -38
- package/lib/telnyx-voip-client.d.ts +4 -2
- package/lib/telnyx-voip-client.js +5 -3
- package/package.json +111 -104
- package/src/callkit/callkit-coordinator.ts +830 -846
- package/src/callkit/callkit.ts +322 -322
- package/src/callkit/index.ts +4 -4
- package/src/callkit/use-callkit.ts +345 -345
- package/src/context/TelnyxVoiceContext.tsx +33 -33
- package/src/hooks/use-callkit-coordinator.ts +60 -60
- package/src/hooks/useAppReadyNotifier.ts +25 -25
- package/src/hooks/useAppStateHandler.ts +134 -134
- package/src/hooks/useNetworkStateHandler.ts +0 -0
- package/src/index.ts +56 -56
- package/src/internal/CallKitHandler.tsx +149 -149
- package/src/internal/callkit-manager.ts +335 -335
- package/src/internal/calls/call-state-controller.ts +407 -384
- package/src/internal/session/session-manager.ts +483 -467
- package/src/internal/user-defaults-helpers.ts +58 -58
- package/src/internal/voice-pn-bridge.ts +266 -18
- package/src/models/call-state.ts +105 -98
- package/src/models/call.ts +502 -388
- package/src/models/config.ts +125 -125
- package/src/models/connection-state.ts +50 -50
- package/src/telnyx-voice-app.tsx +737 -690
- package/src/telnyx-voip-client.ts +551 -539
- package/src/types/telnyx-sdk.d.ts +93 -79
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { EmitterSubscription } from 'react-native';
|
|
2
|
+
export interface CallActionEvent {
|
|
3
|
+
action: string;
|
|
4
|
+
callId?: string;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
}
|
|
1
7
|
export interface VoicePnBridgeInterface {
|
|
2
8
|
getPendingPushAction(): Promise<{
|
|
3
9
|
action: string | null;
|
|
@@ -5,10 +11,106 @@ export interface VoicePnBridgeInterface {
|
|
|
5
11
|
}>;
|
|
6
12
|
setPendingPushAction(action: string, metadata: string): Promise<boolean>;
|
|
7
13
|
clearPendingPushAction(): Promise<boolean>;
|
|
14
|
+
getPendingCallAction(): Promise<{
|
|
15
|
+
action: string | null;
|
|
16
|
+
callId: string | null;
|
|
17
|
+
timestamp: number | null;
|
|
18
|
+
}>;
|
|
19
|
+
clearPendingCallAction(): Promise<boolean>;
|
|
20
|
+
endCall(callId: string | null): Promise<boolean>;
|
|
21
|
+
showOngoingCallNotification(callerName: string | null, callerNumber: string | null, callId: string | null): Promise<boolean>;
|
|
22
|
+
hideOngoingCallNotification(): Promise<boolean>;
|
|
23
|
+
hideIncomingCallNotification(): Promise<boolean>;
|
|
8
24
|
getVoipToken(): Promise<string | null>;
|
|
9
25
|
getPendingVoipPush(): Promise<string | null>;
|
|
10
26
|
clearPendingVoipPush(): Promise<boolean>;
|
|
11
27
|
getPendingVoipAction(): Promise<string | null>;
|
|
12
28
|
clearPendingVoipAction(): Promise<boolean>;
|
|
13
29
|
}
|
|
14
|
-
|
|
30
|
+
declare const NativeBridge: VoicePnBridgeInterface;
|
|
31
|
+
/**
|
|
32
|
+
* Enhanced VoicePnBridge with call control and event handling capabilities
|
|
33
|
+
*/
|
|
34
|
+
export declare class VoicePnBridge {
|
|
35
|
+
/**
|
|
36
|
+
* Get any pending push notification action from native side
|
|
37
|
+
*/
|
|
38
|
+
static getPendingPushAction(): Promise<{
|
|
39
|
+
action?: string;
|
|
40
|
+
metadata?: string;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Set a pending push notification action to native side
|
|
44
|
+
*/
|
|
45
|
+
static setPendingPushAction(action: string, metadata: string): Promise<boolean>;
|
|
46
|
+
/**
|
|
47
|
+
* Get any pending call action from native side (reliable polling pattern)
|
|
48
|
+
*/
|
|
49
|
+
static getPendingCallAction(): Promise<{
|
|
50
|
+
action?: string;
|
|
51
|
+
callId?: string;
|
|
52
|
+
timestamp?: number;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* Clear any pending call action
|
|
56
|
+
*/
|
|
57
|
+
static clearPendingCallAction(): Promise<boolean>;
|
|
58
|
+
/**
|
|
59
|
+
* Clear any pending push notification action
|
|
60
|
+
*/
|
|
61
|
+
static clearPendingPushAction(): Promise<boolean>;
|
|
62
|
+
/**
|
|
63
|
+
* React Native → Android: End/hang up the current call
|
|
64
|
+
* This will hide the ongoing call notification and notify the native side
|
|
65
|
+
*/
|
|
66
|
+
static endCall(callId?: string): Promise<boolean>;
|
|
67
|
+
/**
|
|
68
|
+
* React Native → Android: Show ongoing call notification to keep app alive
|
|
69
|
+
* Should be called when a call becomes active to prevent background termination
|
|
70
|
+
*/
|
|
71
|
+
static showOngoingCallNotification(callerName?: string, callerNumber?: string, callId?: string): Promise<boolean>;
|
|
72
|
+
/**
|
|
73
|
+
* React Native → Android: Hide ongoing call notification
|
|
74
|
+
* Should be called when a call ends to clean up notifications
|
|
75
|
+
*/
|
|
76
|
+
static hideOngoingCallNotification(): Promise<boolean>;
|
|
77
|
+
/**
|
|
78
|
+
* React Native → Android: Hide incoming call notification
|
|
79
|
+
* Useful for dismissing notifications when call is answered/rejected in app
|
|
80
|
+
*/
|
|
81
|
+
static hideIncomingCallNotification(): Promise<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Get VoIP token from native storage
|
|
84
|
+
*/
|
|
85
|
+
static getVoipToken(): Promise<string | null>;
|
|
86
|
+
/**
|
|
87
|
+
* Get pending VoIP push from native storage
|
|
88
|
+
*/
|
|
89
|
+
static getPendingVoipPush(): Promise<string | null>;
|
|
90
|
+
/**
|
|
91
|
+
* Clear pending VoIP push from native storage
|
|
92
|
+
*/
|
|
93
|
+
static clearPendingVoipPush(): Promise<boolean>;
|
|
94
|
+
/**
|
|
95
|
+
* Get pending VoIP action from native storage
|
|
96
|
+
*/
|
|
97
|
+
static getPendingVoipAction(): Promise<string | null>;
|
|
98
|
+
/**
|
|
99
|
+
* Clear pending VoIP action from native storage
|
|
100
|
+
*/
|
|
101
|
+
static clearPendingVoipAction(): Promise<boolean>;
|
|
102
|
+
/**
|
|
103
|
+
* Android → React Native: Listen for immediate call action events from notification buttons
|
|
104
|
+
* Use this for active calls where immediate response is needed (e.g., ending ongoing calls)
|
|
105
|
+
*/
|
|
106
|
+
static addCallActionListener(listener: (event: CallActionEvent) => void): EmitterSubscription;
|
|
107
|
+
/**
|
|
108
|
+
* Remove call action listener
|
|
109
|
+
*/
|
|
110
|
+
static removeCallActionListener(subscription: EmitterSubscription): void;
|
|
111
|
+
/**
|
|
112
|
+
* Remove all call action listeners
|
|
113
|
+
*/
|
|
114
|
+
static removeAllCallActionListeners(): void;
|
|
115
|
+
}
|
|
116
|
+
export { NativeBridge as VoicePnBridgeNative };
|
|
@@ -1,5 +1,212 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.VoicePnBridge = void 0;
|
|
3
|
+
exports.VoicePnBridgeNative = exports.VoicePnBridge = void 0;
|
|
4
4
|
const react_native_1 = require("react-native");
|
|
5
|
-
|
|
5
|
+
const NativeBridge = react_native_1.NativeModules.VoicePnBridge;
|
|
6
|
+
exports.VoicePnBridgeNative = NativeBridge;
|
|
7
|
+
/**
|
|
8
|
+
* Enhanced VoicePnBridge with call control and event handling capabilities
|
|
9
|
+
*/
|
|
10
|
+
class VoicePnBridge {
|
|
11
|
+
/**
|
|
12
|
+
* Get any pending push notification action from native side
|
|
13
|
+
*/
|
|
14
|
+
static async getPendingPushAction() {
|
|
15
|
+
try {
|
|
16
|
+
const result = await NativeBridge.getPendingPushAction();
|
|
17
|
+
return {
|
|
18
|
+
action: result?.action || undefined,
|
|
19
|
+
metadata: result?.metadata || undefined,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error('VoicePnBridge: Error getting pending push action:', error);
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Set a pending push notification action to native side
|
|
29
|
+
*/
|
|
30
|
+
static async setPendingPushAction(action, metadata) {
|
|
31
|
+
try {
|
|
32
|
+
return await NativeBridge.setPendingPushAction(action, metadata);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
console.error('VoicePnBridge: Error setting pending push action:', error);
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get any pending call action from native side (reliable polling pattern)
|
|
41
|
+
*/
|
|
42
|
+
static async getPendingCallAction() {
|
|
43
|
+
try {
|
|
44
|
+
const result = await NativeBridge.getPendingCallAction();
|
|
45
|
+
return {
|
|
46
|
+
action: result?.action || undefined,
|
|
47
|
+
callId: result?.callId || undefined,
|
|
48
|
+
timestamp: result?.timestamp || undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error('VoicePnBridge: Error getting pending call action:', error);
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Clear any pending call action
|
|
58
|
+
*/
|
|
59
|
+
static async clearPendingCallAction() {
|
|
60
|
+
try {
|
|
61
|
+
return await NativeBridge.clearPendingCallAction();
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error('VoicePnBridge: Error clearing pending call action:', error);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Clear any pending push notification action
|
|
70
|
+
*/
|
|
71
|
+
static async clearPendingPushAction() {
|
|
72
|
+
try {
|
|
73
|
+
return await NativeBridge.clearPendingPushAction();
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.error('VoicePnBridge: Error clearing pending push action:', error);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* React Native → Android: End/hang up the current call
|
|
82
|
+
* This will hide the ongoing call notification and notify the native side
|
|
83
|
+
*/
|
|
84
|
+
static async endCall(callId) {
|
|
85
|
+
try {
|
|
86
|
+
return await NativeBridge.endCall(callId || null);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.error('VoicePnBridge: Error ending call:', error);
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* React Native → Android: Show ongoing call notification to keep app alive
|
|
95
|
+
* Should be called when a call becomes active to prevent background termination
|
|
96
|
+
*/
|
|
97
|
+
static async showOngoingCallNotification(callerName, callerNumber, callId) {
|
|
98
|
+
try {
|
|
99
|
+
return await NativeBridge.showOngoingCallNotification(callerName || null, callerNumber || null, callId || null);
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error('VoicePnBridge: Error showing ongoing call notification:', error);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* React Native → Android: Hide ongoing call notification
|
|
108
|
+
* Should be called when a call ends to clean up notifications
|
|
109
|
+
*/
|
|
110
|
+
static async hideOngoingCallNotification() {
|
|
111
|
+
try {
|
|
112
|
+
return await NativeBridge.hideOngoingCallNotification();
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error('VoicePnBridge: Error hiding ongoing call notification:', error);
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* React Native → Android: Hide incoming call notification
|
|
121
|
+
* Useful for dismissing notifications when call is answered/rejected in app
|
|
122
|
+
*/
|
|
123
|
+
static async hideIncomingCallNotification() {
|
|
124
|
+
try {
|
|
125
|
+
return await NativeBridge.hideIncomingCallNotification();
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
console.error('VoicePnBridge: Error hiding incoming call notification:', error);
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get VoIP token from native storage
|
|
134
|
+
*/
|
|
135
|
+
static async getVoipToken() {
|
|
136
|
+
try {
|
|
137
|
+
return await NativeBridge.getVoipToken();
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.error('VoicePnBridge: Error getting VoIP token:', error);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get pending VoIP push from native storage
|
|
146
|
+
*/
|
|
147
|
+
static async getPendingVoipPush() {
|
|
148
|
+
try {
|
|
149
|
+
return await NativeBridge.getPendingVoipPush();
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
console.error('VoicePnBridge: Error getting pending VoIP push:', error);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Clear pending VoIP push from native storage
|
|
158
|
+
*/
|
|
159
|
+
static async clearPendingVoipPush() {
|
|
160
|
+
try {
|
|
161
|
+
return await NativeBridge.clearPendingVoipPush();
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
console.error('VoicePnBridge: Error clearing pending VoIP push:', error);
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get pending VoIP action from native storage
|
|
170
|
+
*/
|
|
171
|
+
static async getPendingVoipAction() {
|
|
172
|
+
try {
|
|
173
|
+
return await NativeBridge.getPendingVoipAction();
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.error('VoicePnBridge: Error getting pending VoIP action:', error);
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Clear pending VoIP action from native storage
|
|
182
|
+
*/
|
|
183
|
+
static async clearPendingVoipAction() {
|
|
184
|
+
try {
|
|
185
|
+
return await NativeBridge.clearPendingVoipAction();
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error('VoicePnBridge: Error clearing pending VoIP action:', error);
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Android → React Native: Listen for immediate call action events from notification buttons
|
|
194
|
+
* Use this for active calls where immediate response is needed (e.g., ending ongoing calls)
|
|
195
|
+
*/
|
|
196
|
+
static addCallActionListener(listener) {
|
|
197
|
+
return react_native_1.DeviceEventEmitter.addListener('TelnyxCallAction', listener);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Remove call action listener
|
|
201
|
+
*/
|
|
202
|
+
static removeCallActionListener(subscription) {
|
|
203
|
+
subscription.remove();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Remove all call action listeners
|
|
207
|
+
*/
|
|
208
|
+
static removeAllCallActionListeners() {
|
|
209
|
+
react_native_1.DeviceEventEmitter.removeAllListeners('TelnyxCallAction');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
exports.VoicePnBridge = VoicePnBridge;
|
|
@@ -16,7 +16,9 @@ export declare enum TelnyxCallState {
|
|
|
16
16
|
/** Call has ended normally */
|
|
17
17
|
ENDED = "ENDED",
|
|
18
18
|
/** Call failed to connect or was rejected */
|
|
19
|
-
FAILED = "FAILED"
|
|
19
|
+
FAILED = "FAILED",
|
|
20
|
+
/** Call was dropped due to network issues */
|
|
21
|
+
DROPPED = "DROPPED"
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
24
|
* Type guard to check if a value is a valid TelnyxCallState
|
package/lib/models/call-state.js
CHANGED
|
@@ -22,6 +22,8 @@ var TelnyxCallState;
|
|
|
22
22
|
TelnyxCallState["ENDED"] = "ENDED";
|
|
23
23
|
/** Call failed to connect or was rejected */
|
|
24
24
|
TelnyxCallState["FAILED"] = "FAILED";
|
|
25
|
+
/** Call was dropped due to network issues */
|
|
26
|
+
TelnyxCallState["DROPPED"] = "DROPPED";
|
|
25
27
|
})(TelnyxCallState || (exports.TelnyxCallState = TelnyxCallState = {}));
|
|
26
28
|
/**
|
|
27
29
|
* Type guard to check if a value is a valid TelnyxCallState
|
|
@@ -70,7 +72,9 @@ exports.CallStateHelpers = {
|
|
|
70
72
|
* Is the call in a terminated state?
|
|
71
73
|
*/
|
|
72
74
|
isTerminated(state) {
|
|
73
|
-
return state === TelnyxCallState.ENDED ||
|
|
75
|
+
return (state === TelnyxCallState.ENDED ||
|
|
76
|
+
state === TelnyxCallState.FAILED ||
|
|
77
|
+
state === TelnyxCallState.DROPPED);
|
|
74
78
|
},
|
|
75
79
|
/**
|
|
76
80
|
* Is the call in an active state (can have media)?
|
package/lib/models/call.d.ts
CHANGED
|
@@ -13,13 +13,15 @@ export declare class Call {
|
|
|
13
13
|
private readonly _callId;
|
|
14
14
|
private readonly _destination;
|
|
15
15
|
private readonly _isIncoming;
|
|
16
|
+
private readonly _originalCallerName?;
|
|
17
|
+
private readonly _originalCallerNumber?;
|
|
16
18
|
private readonly _callState;
|
|
17
19
|
private readonly _isMuted;
|
|
18
20
|
private readonly _isHeld;
|
|
19
21
|
private readonly _duration;
|
|
20
22
|
private _durationTimer?;
|
|
21
23
|
private _startTime?;
|
|
22
|
-
constructor(_telnyxCall: TelnyxCall, _callId: string, _destination: string, _isIncoming: boolean);
|
|
24
|
+
constructor(_telnyxCall: TelnyxCall, _callId: string, _destination: string, _isIncoming: boolean, isReattached?: boolean, _originalCallerName?: string, _originalCallerNumber?: string);
|
|
23
25
|
/**
|
|
24
26
|
* Unique identifier for this call
|
|
25
27
|
*/
|
|
@@ -52,6 +54,24 @@ export declare class Call {
|
|
|
52
54
|
* Current call duration in seconds (synchronous access)
|
|
53
55
|
*/
|
|
54
56
|
get currentDuration(): number;
|
|
57
|
+
/**
|
|
58
|
+
* Custom headers received from the WebRTC INVITE message.
|
|
59
|
+
* These headers are passed during call initiation and can contain application-specific information.
|
|
60
|
+
* Format should be [{"name": "X-Header-Name", "value": "Value"}] where header names must start with "X-".
|
|
61
|
+
*/
|
|
62
|
+
get inviteCustomHeaders(): {
|
|
63
|
+
name: string;
|
|
64
|
+
value: string;
|
|
65
|
+
}[] | null;
|
|
66
|
+
/**
|
|
67
|
+
* Custom headers received from the WebRTC ANSWER message.
|
|
68
|
+
* These headers are passed during call acceptance and can contain application-specific information.
|
|
69
|
+
* Format should be [{"name": "X-Header-Name", "value": "Value"}] where header names must start with "X-".
|
|
70
|
+
*/
|
|
71
|
+
get answerCustomHeaders(): {
|
|
72
|
+
name: string;
|
|
73
|
+
value: string;
|
|
74
|
+
}[] | null;
|
|
55
75
|
/**
|
|
56
76
|
* Get the underlying Telnyx Call object (for internal use)
|
|
57
77
|
* @internal
|
|
@@ -91,12 +111,20 @@ export declare class Call {
|
|
|
91
111
|
get canResume$(): Observable<boolean>;
|
|
92
112
|
/**
|
|
93
113
|
* Answer the incoming call
|
|
114
|
+
* @param customHeaders Optional custom headers to include with the answer
|
|
94
115
|
*/
|
|
95
|
-
answer(
|
|
116
|
+
answer(customHeaders?: {
|
|
117
|
+
name: string;
|
|
118
|
+
value: string;
|
|
119
|
+
}[]): Promise<void>;
|
|
96
120
|
/**
|
|
97
121
|
* Hang up the call
|
|
122
|
+
* @param customHeaders Optional custom headers to include with the hangup request
|
|
98
123
|
*/
|
|
99
|
-
hangup(
|
|
124
|
+
hangup(customHeaders?: {
|
|
125
|
+
name: string;
|
|
126
|
+
value: string;
|
|
127
|
+
}[]): Promise<void>;
|
|
100
128
|
/**
|
|
101
129
|
* Put the call on hold
|
|
102
130
|
*/
|
package/lib/models/call.js
CHANGED
|
@@ -46,15 +46,22 @@ const react_native_1 = require("react-native");
|
|
|
46
46
|
* integrate with any state management solution.
|
|
47
47
|
*/
|
|
48
48
|
class Call {
|
|
49
|
-
constructor(_telnyxCall, _callId, _destination, _isIncoming) {
|
|
49
|
+
constructor(_telnyxCall, _callId, _destination, _isIncoming, isReattached = false, _originalCallerName, _originalCallerNumber) {
|
|
50
50
|
this._telnyxCall = _telnyxCall;
|
|
51
51
|
this._callId = _callId;
|
|
52
52
|
this._destination = _destination;
|
|
53
53
|
this._isIncoming = _isIncoming;
|
|
54
|
+
this._originalCallerName = _originalCallerName;
|
|
55
|
+
this._originalCallerNumber = _originalCallerNumber;
|
|
54
56
|
this._callState = new rxjs_1.BehaviorSubject(call_state_1.TelnyxCallState.RINGING);
|
|
55
57
|
this._isMuted = new rxjs_1.BehaviorSubject(false);
|
|
56
58
|
this._isHeld = new rxjs_1.BehaviorSubject(false);
|
|
57
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
|
+
}
|
|
58
65
|
this._setupCallListeners();
|
|
59
66
|
}
|
|
60
67
|
/**
|
|
@@ -105,6 +112,22 @@ class Call {
|
|
|
105
112
|
get currentDuration() {
|
|
106
113
|
return this._duration.value;
|
|
107
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
|
+
}
|
|
108
131
|
/**
|
|
109
132
|
* Get the underlying Telnyx Call object (for internal use)
|
|
110
133
|
* @internal
|
|
@@ -162,8 +185,9 @@ class Call {
|
|
|
162
185
|
}
|
|
163
186
|
/**
|
|
164
187
|
* Answer the incoming call
|
|
188
|
+
* @param customHeaders Optional custom headers to include with the answer
|
|
165
189
|
*/
|
|
166
|
-
async answer() {
|
|
190
|
+
async answer(customHeaders) {
|
|
167
191
|
if (!call_state_1.CallStateHelpers.canAnswer(this.currentState)) {
|
|
168
192
|
throw new Error(`Cannot answer call in state: ${this.currentState}`);
|
|
169
193
|
}
|
|
@@ -180,7 +204,8 @@ class Call {
|
|
|
180
204
|
// Fallback for Android or when CallKit is not available
|
|
181
205
|
console.log('Call: Setting state to CONNECTING before answering');
|
|
182
206
|
this._callState.next(call_state_1.TelnyxCallState.CONNECTING);
|
|
183
|
-
|
|
207
|
+
// Pass custom headers to the underlying Telnyx call
|
|
208
|
+
await this._telnyxCall.answer(customHeaders);
|
|
184
209
|
}
|
|
185
210
|
catch (error) {
|
|
186
211
|
console.error('Failed to answer call:', error);
|
|
@@ -189,8 +214,9 @@ class Call {
|
|
|
189
214
|
}
|
|
190
215
|
/**
|
|
191
216
|
* Hang up the call
|
|
217
|
+
* @param customHeaders Optional custom headers to include with the hangup request
|
|
192
218
|
*/
|
|
193
|
-
async hangup() {
|
|
219
|
+
async hangup(customHeaders) {
|
|
194
220
|
if (!call_state_1.CallStateHelpers.canHangup(this.currentState)) {
|
|
195
221
|
throw new Error(`Cannot hang up call in state: ${this.currentState}`);
|
|
196
222
|
}
|
|
@@ -205,7 +231,19 @@ class Call {
|
|
|
205
231
|
}
|
|
206
232
|
}
|
|
207
233
|
// Fallback for Android or when CallKit is not available
|
|
208
|
-
await this._telnyxCall.hangup();
|
|
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
|
+
}
|
|
209
247
|
}
|
|
210
248
|
catch (error) {
|
|
211
249
|
console.error('Failed to hang up call:', error);
|
|
@@ -314,10 +352,68 @@ class Call {
|
|
|
314
352
|
// Start duration timer when call becomes active
|
|
315
353
|
if (telnyxState === call_state_1.TelnyxCallState.ACTIVE && !this._startTime) {
|
|
316
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
|
+
}
|
|
317
400
|
}
|
|
318
401
|
// Stop duration timer when call ends
|
|
319
402
|
if (call_state_1.CallStateHelpers.isTerminated(telnyxState)) {
|
|
320
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
|
+
}
|
|
321
417
|
}
|
|
322
418
|
});
|
|
323
419
|
}
|
|
@@ -331,6 +427,8 @@ class Call {
|
|
|
331
427
|
case 'ringing':
|
|
332
428
|
case 'new':
|
|
333
429
|
return call_state_1.TelnyxCallState.RINGING;
|
|
430
|
+
case 'connecting':
|
|
431
|
+
return call_state_1.TelnyxCallState.CONNECTING;
|
|
334
432
|
case 'active':
|
|
335
433
|
case 'answered':
|
|
336
434
|
return call_state_1.TelnyxCallState.ACTIVE;
|
|
@@ -342,6 +440,8 @@ class Call {
|
|
|
342
440
|
case 'failed':
|
|
343
441
|
case 'rejected':
|
|
344
442
|
return call_state_1.TelnyxCallState.FAILED;
|
|
443
|
+
case 'dropped':
|
|
444
|
+
return call_state_1.TelnyxCallState.DROPPED;
|
|
345
445
|
default:
|
|
346
446
|
console.warn(`Unknown call state: ${telnyxState}`);
|
|
347
447
|
return call_state_1.TelnyxCallState.RINGING;
|