@telnyx/react-voice-commons-sdk 0.2.0 → 0.3.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/CHANGELOG.md +24 -0
- package/README.md +24 -0
- package/lib/hooks/useAppStateHandler.js +0 -12
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/internal/CallKitHandler.js +0 -8
- package/package.json +2 -6
- package/src/callkit/callkit-coordinator.ts +0 -1
- package/src/hooks/useAppStateHandler.ts +0 -13
- package/src/index.ts +2 -0
- package/src/internal/CallKitHandler.tsx +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# CHANGELOG.md
|
|
2
2
|
|
|
3
|
+
## [0.3.0] (2026-04-15)
|
|
4
|
+
|
|
5
|
+
### ⚠️ Breaking changes
|
|
6
|
+
|
|
7
|
+
- **`expo-router` is no longer a dependency of the SDK.** The SDK previously navigated the host app in a few places (`useAppStateHandler` on background disconnect, `CallKitHandler` after CallKit answer/end). Those calls have been removed — navigation is now exclusively the host app's responsibility.
|
|
8
|
+
- **Migration for Expo consumers:** subscribe to `voipClient.connectionState$` and `voipClient.activeCall$` in your app and navigate there. Example:
|
|
9
|
+
```tsx
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const sub = voipClient.connectionState$.subscribe((state) => {
|
|
12
|
+
if (state === TelnyxConnectionState.DISCONNECTED) {
|
|
13
|
+
router.replace('/');
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
return () => sub.unsubscribe();
|
|
17
|
+
}, []);
|
|
18
|
+
```
|
|
19
|
+
- `CallKitHandler` already exposed `onNavigateToDialer` / `onNavigateBack` callback props; those are now the only way to wire navigation.
|
|
20
|
+
- The `navigateToLoginOnDisconnect` option on `useAppStateHandler` is retained in the type signature for source compatibility but no longer has any effect.
|
|
21
|
+
- **Bare React Native (non-Expo) projects are now supported.** Metro bundling no longer fails on missing `expo-router`; SDK-level behavior is identical for Expo and bare RN consumers.
|
|
22
|
+
|
|
23
|
+
### Enhancement
|
|
24
|
+
|
|
25
|
+
- **`react-native-url-polyfill` is now a direct dependency and auto-loaded** from the SDK entry point. Previously, apps running Hermes crashed on the second login attempt with `URLSearchParams.set is not implemented` because the SDK constructs the WebSocket URL via `URLSearchParams`, which is incomplete in Hermes. No action required from consumers.
|
|
26
|
+
|
|
3
27
|
## [0.2.0](https://github.com/team-telnyx/react-native-voice-commons/releases/tag/commons-sdk-v0.2.0) (2026-04-01)
|
|
4
28
|
|
|
5
29
|
### Enhancement
|
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ A high-level, state-agnostic, drop-in module for the Telnyx React Native SDK tha
|
|
|
11
11
|
- **Reactive State Management**: RxJS-based state streams for real-time UI updates
|
|
12
12
|
- **TypeScript Support**: Full TypeScript definitions for better developer experience
|
|
13
13
|
- **Cross-Platform**: Built for both iOS and Android with React Native
|
|
14
|
+
- **Framework-agnostic**: Works in both Expo and bare React Native projects. See the [bare RN reference demo](https://github.com/team-telnyx/telnyx-react-native-bare-demo) for non-Expo integration.
|
|
14
15
|
|
|
15
16
|
## About @telnyx/react-voice-commons-sdk
|
|
16
17
|
|
|
@@ -115,6 +116,29 @@ call.callState$.subscribe((state) => {
|
|
|
115
116
|
});
|
|
116
117
|
```
|
|
117
118
|
|
|
119
|
+
### Navigation
|
|
120
|
+
|
|
121
|
+
As of **v0.3.0**, the SDK no longer navigates the host app. Routing on state transitions (e.g. redirecting to a login screen on disconnect, surfacing a dialer screen after answering a call via CallKit) is entirely the host app's responsibility. Subscribe to `connectionState$` and `activeCall$` and invoke your own navigator.
|
|
122
|
+
|
|
123
|
+
Example using `expo-router`:
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
import { router } from 'expo-router';
|
|
127
|
+
import { useEffect } from 'react';
|
|
128
|
+
import { TelnyxConnectionState } from '@telnyx/react-voice-commons-sdk';
|
|
129
|
+
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
const sub = voipClient.connectionState$.subscribe((state) => {
|
|
132
|
+
if (state === TelnyxConnectionState.DISCONNECTED) {
|
|
133
|
+
router.replace('/');
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
return () => sub.unsubscribe();
|
|
137
|
+
}, []);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The same pattern works with `react-navigation`, React Router, or any other navigator — the SDK is agnostic.
|
|
141
|
+
|
|
118
142
|
### 4. Call Management
|
|
119
143
|
|
|
120
144
|
```tsx
|
|
@@ -8,7 +8,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
8
8
|
exports.useAppStateHandler = void 0;
|
|
9
9
|
const react_1 = require('react');
|
|
10
10
|
const react_native_1 = require('react-native');
|
|
11
|
-
const expo_router_1 = require('expo-router');
|
|
12
11
|
const async_storage_1 = __importDefault(require('@react-native-async-storage/async-storage'));
|
|
13
12
|
const connection_state_1 = require('../models/connection-state');
|
|
14
13
|
const call_state_1 = require('../models/call-state');
|
|
@@ -70,9 +69,6 @@ const useAppStateHandler = ({
|
|
|
70
69
|
if (!stillInProgress) {
|
|
71
70
|
log('AppStateHandler: Push notification call completed, now disconnecting socket');
|
|
72
71
|
await voipClient.logout();
|
|
73
|
-
if (navigateToLoginOnDisconnect) {
|
|
74
|
-
expo_router_1.router.replace('/');
|
|
75
|
-
}
|
|
76
72
|
}
|
|
77
73
|
}, 5000); // Wait 5 seconds
|
|
78
74
|
appState.current = nextAppState;
|
|
@@ -83,14 +79,6 @@ const useAppStateHandler = ({
|
|
|
83
79
|
// Disconnect the socket with background reason
|
|
84
80
|
await voipClient.logout();
|
|
85
81
|
log('AppStateHandler: Socket disconnected successfully');
|
|
86
|
-
// Navigate to login screen
|
|
87
|
-
if (navigateToLoginOnDisconnect) {
|
|
88
|
-
// Use a small delay to ensure the disconnect completes
|
|
89
|
-
setTimeout(() => {
|
|
90
|
-
log('AppStateHandler: Navigating to login screen');
|
|
91
|
-
expo_router_1.router.replace('/');
|
|
92
|
-
}, 100);
|
|
93
|
-
}
|
|
94
82
|
} catch (error) {
|
|
95
83
|
console.error('AppStateHandler: Error during background disconnect:', error);
|
|
96
84
|
}
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -62,6 +62,7 @@ exports.useAppReadyNotifier =
|
|
|
62
62
|
exports.createTelnyxVoipClient =
|
|
63
63
|
exports.TelnyxVoipClient =
|
|
64
64
|
void 0;
|
|
65
|
+
require('react-native-url-polyfill/auto');
|
|
65
66
|
// Main client
|
|
66
67
|
var telnyx_voip_client_1 = require('./telnyx-voip-client');
|
|
67
68
|
Object.defineProperty(exports, 'TelnyxVoipClient', {
|
|
@@ -8,7 +8,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
8
8
|
exports.CallKitHandler = void 0;
|
|
9
9
|
const react_1 = require('react');
|
|
10
10
|
const react_native_1 = require('react-native');
|
|
11
|
-
const expo_router_1 = require('expo-router');
|
|
12
11
|
const async_storage_1 = __importDefault(require('@react-native-async-storage/async-storage'));
|
|
13
12
|
const TelnyxVoiceContext_1 = require('../context/TelnyxVoiceContext');
|
|
14
13
|
// Global flag to ensure only one CallKitHandler is active
|
|
@@ -20,7 +19,6 @@ let isCallKitHandlerActive = false;
|
|
|
20
19
|
* @internal - Users should not use this component directly
|
|
21
20
|
*/
|
|
22
21
|
const CallKitHandler = ({ onLoginRequired, onNavigateToDialer, onNavigateBack }) => {
|
|
23
|
-
const router = (0, expo_router_1.useRouter)();
|
|
24
22
|
const { voipClient } = (0, TelnyxVoiceContext_1.useTelnyxVoice)();
|
|
25
23
|
// Store active calls by CallKit UUID for coordination
|
|
26
24
|
const activeCallsRef = (0, react_1.useRef)(new Map());
|
|
@@ -90,11 +88,8 @@ const CallKitHandler = ({ onLoginRequired, onNavigateToDialer, onNavigateBack })
|
|
|
90
88
|
callUUID: eventData.callUUID,
|
|
91
89
|
isTrackedCall: activeCallsRef.current.has(eventData.callUUID),
|
|
92
90
|
});
|
|
93
|
-
// Navigate to dialer after answering
|
|
94
91
|
if (onNavigateToDialer) {
|
|
95
92
|
onNavigateToDialer();
|
|
96
|
-
} else {
|
|
97
|
-
router.replace('/dialer');
|
|
98
93
|
}
|
|
99
94
|
};
|
|
100
95
|
const handleEndCall = async (eventData) => {
|
|
@@ -105,11 +100,8 @@ const CallKitHandler = ({ onLoginRequired, onNavigateToDialer, onNavigateBack })
|
|
|
105
100
|
// Clean up our local tracking info
|
|
106
101
|
activeCallsRef.current.delete(eventData.callUUID);
|
|
107
102
|
await async_storage_1.default.removeItem('@push_notification_payload');
|
|
108
|
-
// Navigate back after call ends
|
|
109
103
|
if (onNavigateBack) {
|
|
110
104
|
onNavigateBack();
|
|
111
|
-
} else {
|
|
112
|
-
router.replace('/dialer');
|
|
113
105
|
}
|
|
114
106
|
};
|
|
115
107
|
// This component doesn't render anything, it just handles events
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telnyx/react-voice-commons-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A high-level, state-agnostic, drop-in module for the Telnyx React Native SDK that simplifies WebRTC voice calling integration",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "lib/index.js",
|
|
@@ -66,7 +66,6 @@
|
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"@react-native-async-storage/async-storage": "^2.1.0",
|
|
69
|
-
"expo-router": "^5.1.0",
|
|
70
69
|
"react": ">=19.0.0 <20.0.0",
|
|
71
70
|
"react-native": ">=0.79.0 <1.0.0",
|
|
72
71
|
"react-native-webrtc": "^124.0.5"
|
|
@@ -75,9 +74,6 @@
|
|
|
75
74
|
"@react-native-async-storage/async-storage": {
|
|
76
75
|
"optional": false
|
|
77
76
|
},
|
|
78
|
-
"expo-router": {
|
|
79
|
-
"optional": false
|
|
80
|
-
},
|
|
81
77
|
"react": {
|
|
82
78
|
"optional": false
|
|
83
79
|
},
|
|
@@ -93,6 +89,7 @@
|
|
|
93
89
|
"@telnyx/react-native-voice-sdk": ">=0.4.1",
|
|
94
90
|
"eventemitter3": "^5.0.1",
|
|
95
91
|
"expo": "~53.0.22",
|
|
92
|
+
"react-native-url-polyfill": "^3.0.0",
|
|
96
93
|
"react-native-voip-push-notification": "^3.3.3",
|
|
97
94
|
"rxjs": "^7.8.2"
|
|
98
95
|
},
|
|
@@ -104,7 +101,6 @@
|
|
|
104
101
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
105
102
|
"@typescript-eslint/parser": "^6.0.0",
|
|
106
103
|
"eslint": "^8.0.0",
|
|
107
|
-
"expo-router": "^5.1.0",
|
|
108
104
|
"jest": "^29.5.0",
|
|
109
105
|
"prettier": "^3.0.0",
|
|
110
106
|
"ts-jest": "^29.1.0",
|
|
@@ -2,7 +2,6 @@ import { Platform, AppState } from 'react-native';
|
|
|
2
2
|
import CallKit, { CallEndReason } from './callkit';
|
|
3
3
|
import { Call } from '@telnyx/react-native-voice-sdk';
|
|
4
4
|
import { VoicePnBridge } from '../internal/voice-pn-bridge';
|
|
5
|
-
import { router } from 'expo-router';
|
|
6
5
|
import { TelnyxVoipClient } from '../telnyx-voip-client';
|
|
7
6
|
import { TelnyxConnectionState } from '../models/connection-state';
|
|
8
7
|
import { act } from 'react';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
2
|
import { AppState, AppStateStatus } from 'react-native';
|
|
3
|
-
import { router } from 'expo-router';
|
|
4
3
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
5
4
|
import { TelnyxVoipClient } from '../telnyx-voip-client';
|
|
6
5
|
import { TelnyxConnectionState } from '../models/connection-state';
|
|
@@ -76,9 +75,6 @@ export const useAppStateHandler = ({
|
|
|
76
75
|
if (!stillInProgress) {
|
|
77
76
|
log('AppStateHandler: Push notification call completed, now disconnecting socket');
|
|
78
77
|
await voipClient.logout();
|
|
79
|
-
if (navigateToLoginOnDisconnect) {
|
|
80
|
-
router.replace('/');
|
|
81
|
-
}
|
|
82
78
|
}
|
|
83
79
|
}, 5000); // Wait 5 seconds
|
|
84
80
|
appState.current = nextAppState;
|
|
@@ -92,15 +88,6 @@ export const useAppStateHandler = ({
|
|
|
92
88
|
await voipClient.logout();
|
|
93
89
|
|
|
94
90
|
log('AppStateHandler: Socket disconnected successfully');
|
|
95
|
-
|
|
96
|
-
// Navigate to login screen
|
|
97
|
-
if (navigateToLoginOnDisconnect) {
|
|
98
|
-
// Use a small delay to ensure the disconnect completes
|
|
99
|
-
setTimeout(() => {
|
|
100
|
-
log('AppStateHandler: Navigating to login screen');
|
|
101
|
-
router.replace('/');
|
|
102
|
-
}, 100);
|
|
103
|
-
}
|
|
104
91
|
} catch (error) {
|
|
105
92
|
console.error('AppStateHandler: Error during background disconnect:', error);
|
|
106
93
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import { Platform, DeviceEventEmitter } from 'react-native';
|
|
3
|
-
import { useRouter } from 'expo-router';
|
|
4
3
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
5
4
|
import { useTelnyxVoice } from '../context/TelnyxVoiceContext';
|
|
6
5
|
import { callKitCoordinator } from '../callkit';
|
|
@@ -36,7 +35,6 @@ export const CallKitHandler: React.FC<CallKitHandlerProps> = ({
|
|
|
36
35
|
onNavigateToDialer,
|
|
37
36
|
onNavigateBack,
|
|
38
37
|
}) => {
|
|
39
|
-
const router = useRouter();
|
|
40
38
|
const { voipClient } = useTelnyxVoice();
|
|
41
39
|
|
|
42
40
|
// Store active calls by CallKit UUID for coordination
|
|
@@ -118,11 +116,8 @@ export const CallKitHandler: React.FC<CallKitHandlerProps> = ({
|
|
|
118
116
|
isTrackedCall: activeCallsRef.current.has(eventData.callUUID),
|
|
119
117
|
});
|
|
120
118
|
|
|
121
|
-
// Navigate to dialer after answering
|
|
122
119
|
if (onNavigateToDialer) {
|
|
123
120
|
onNavigateToDialer();
|
|
124
|
-
} else {
|
|
125
|
-
router.replace('/dialer');
|
|
126
121
|
}
|
|
127
122
|
};
|
|
128
123
|
|
|
@@ -136,11 +131,8 @@ export const CallKitHandler: React.FC<CallKitHandlerProps> = ({
|
|
|
136
131
|
activeCallsRef.current.delete(eventData.callUUID);
|
|
137
132
|
await AsyncStorage.removeItem('@push_notification_payload');
|
|
138
133
|
|
|
139
|
-
// Navigate back after call ends
|
|
140
134
|
if (onNavigateBack) {
|
|
141
135
|
onNavigateBack();
|
|
142
|
-
} else {
|
|
143
|
-
router.replace('/dialer');
|
|
144
136
|
}
|
|
145
137
|
};
|
|
146
138
|
|