@nativetalkcommunications/react-native-call-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/LICENSE +21 -0
- package/NativetalkCallSdk.podspec +31 -0
- package/README.md +494 -0
- package/android/build.gradle +58 -0
- package/android/gradle.properties +2 -0
- package/android/src/main/AndroidManifest.xml +84 -0
- package/android/src/main/java/io/nativetalk/callsdk/BackgroundService.kt +149 -0
- package/android/src/main/java/io/nativetalk/callsdk/CallActionReceiver.kt +24 -0
- package/android/src/main/java/io/nativetalk/callsdk/CallService.kt +45 -0
- package/android/src/main/java/io/nativetalk/callsdk/Compatibility.kt +96 -0
- package/android/src/main/java/io/nativetalk/callsdk/CoreManager.kt +801 -0
- package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallScreeningService.kt +105 -0
- package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallSdkModule.kt +205 -0
- package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallSdkPackage.kt +18 -0
- package/android/src/main/java/io/nativetalk/callsdk/TelephonyMonitor.kt +229 -0
- package/android/src/main/java/io/nativetalk/callsdk/Utils.kt +42 -0
- package/android/src/main/res/drawable/ic_nativetalk_call.xml +9 -0
- package/android/src/main/res/values/strings.xml +9 -0
- package/app.plugin.js +1 -0
- package/ios/NativetalkCallSdk-Bridging-Header.h +4 -0
- package/ios/NativetalkCallSdk.swift +738 -0
- package/ios/NativetalkCallSdkBridge.m +35 -0
- package/lib/commonjs/CallProvider.js +602 -0
- package/lib/commonjs/helpers.js +173 -0
- package/lib/commonjs/index.js +96 -0
- package/lib/commonjs/native.js +146 -0
- package/lib/commonjs/types.js +8 -0
- package/lib/commonjs/ui/Avatar.js +29 -0
- package/lib/commonjs/ui/Dialer.js +189 -0
- package/lib/commonjs/ui/IncomingCallView.js +128 -0
- package/lib/commonjs/ui/OutgoingCallView.js +117 -0
- package/lib/commonjs/ui/index.js +22 -0
- package/lib/commonjs/ui/theme.js +21 -0
- package/lib/module/CallProvider.js +573 -0
- package/lib/module/helpers.js +161 -0
- package/lib/module/index.js +57 -0
- package/lib/module/native.js +123 -0
- package/lib/module/types.js +7 -0
- package/lib/module/ui/Avatar.js +22 -0
- package/lib/module/ui/Dialer.js +162 -0
- package/lib/module/ui/IncomingCallView.js +101 -0
- package/lib/module/ui/OutgoingCallView.js +110 -0
- package/lib/module/ui/index.js +13 -0
- package/lib/module/ui/theme.js +17 -0
- package/lib/typescript/CallProvider.d.ts +46 -0
- package/lib/typescript/helpers.d.ts +52 -0
- package/lib/typescript/index.d.ts +77 -0
- package/lib/typescript/native.d.ts +53 -0
- package/lib/typescript/types.d.ts +155 -0
- package/lib/typescript/ui/Avatar.d.ts +13 -0
- package/lib/typescript/ui/Dialer.d.ts +29 -0
- package/lib/typescript/ui/IncomingCallView.d.ts +39 -0
- package/lib/typescript/ui/OutgoingCallView.d.ts +28 -0
- package/lib/typescript/ui/index.d.ts +13 -0
- package/lib/typescript/ui/theme.d.ts +20 -0
- package/linphonesw-pod/Sources/LinphoneSdkInfos.swift +4 -0
- package/linphonesw-pod/Sources/LinphoneWrapper.swift +42949 -0
- package/linphonesw-pod/linphonesw.podspec +46 -0
- package/package.json +90 -0
- package/plugin/build/index.js +12 -0
- package/plugin/build/withAndroid.js +78 -0
- package/plugin/build/withIos.js +66 -0
- package/src/CallProvider.tsx +675 -0
- package/src/helpers.ts +179 -0
- package/src/index.ts +84 -0
- package/src/native.ts +185 -0
- package/src/types.ts +202 -0
- package/src/ui/Avatar.tsx +46 -0
- package/src/ui/Dialer.tsx +248 -0
- package/src/ui/IncomingCallView.tsx +161 -0
- package/src/ui/OutgoingCallView.tsx +203 -0
- package/src/ui/index.ts +13 -0
- package/src/ui/theme.ts +36 -0
- package/ui/package.json +6 -0
|
@@ -0,0 +1,128 @@
|
|
|
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 (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.IncomingCallView = void 0;
|
|
27
|
+
/**
|
|
28
|
+
* Drop-in screen rendered when an incoming call is ringing.
|
|
29
|
+
*
|
|
30
|
+
* Wire-up:
|
|
31
|
+
*
|
|
32
|
+
* ```tsx
|
|
33
|
+
* <CallProvider
|
|
34
|
+
* onIncomingCall={() => navigation.navigate('IncomingCall')}
|
|
35
|
+
* config={cfg}
|
|
36
|
+
* >
|
|
37
|
+
* …
|
|
38
|
+
* </CallProvider>
|
|
39
|
+
*
|
|
40
|
+
* // your IncomingCall screen:
|
|
41
|
+
* <IncomingCallView onAnswered={() => navigation.replace('InCall')} />
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
const react_1 = __importStar(require("react"));
|
|
45
|
+
const react_native_1 = require("react-native");
|
|
46
|
+
const CallProvider_1 = require("../CallProvider");
|
|
47
|
+
const helpers_1 = require("../helpers");
|
|
48
|
+
const Avatar_1 = require("./Avatar");
|
|
49
|
+
const theme_1 = require("./theme");
|
|
50
|
+
function IncomingCallView({ onAnswered, onDeclined, onDismissed, location, title = 'Incoming call', theme, header, style, }) {
|
|
51
|
+
const { incoming, incomingInfo, answer, decline } = (0, CallProvider_1.useCall)();
|
|
52
|
+
const t = (0, theme_1.mergeTheme)(theme);
|
|
53
|
+
// Auto-dismiss if the call ends while this screen is mounted.
|
|
54
|
+
(0, react_1.useEffect)(() => {
|
|
55
|
+
if (!incoming)
|
|
56
|
+
onDismissed?.();
|
|
57
|
+
}, [incoming, onDismissed]);
|
|
58
|
+
const pretty = (s = '') => s.includes('@') ? (0, helpers_1.parseSipUser)(s) : s;
|
|
59
|
+
const name = incomingInfo?.name ?? 'Unknown';
|
|
60
|
+
const phone = incomingInfo?.phone ?? '';
|
|
61
|
+
const initials = incomingInfo?.initials ?? '??';
|
|
62
|
+
const onAnswer = async () => {
|
|
63
|
+
await answer();
|
|
64
|
+
onAnswered?.();
|
|
65
|
+
};
|
|
66
|
+
const onDecline = async () => {
|
|
67
|
+
await decline('busy');
|
|
68
|
+
onDeclined?.();
|
|
69
|
+
};
|
|
70
|
+
return (<react_native_1.View style={[styles.container, { backgroundColor: t.background }, style]}>
|
|
71
|
+
{header}
|
|
72
|
+
<react_native_1.Text style={[styles.status, { color: t.text }]}>{title}</react_native_1.Text>
|
|
73
|
+
|
|
74
|
+
<react_native_1.View style={styles.avatarWrap}>
|
|
75
|
+
<Avatar_1.Avatar initials={initials} size={80} color={t.primary} background="#EEF2FF"/>
|
|
76
|
+
</react_native_1.View>
|
|
77
|
+
|
|
78
|
+
<react_native_1.Text style={[styles.name, { color: t.text }]}>{pretty(name)}</react_native_1.Text>
|
|
79
|
+
<react_native_1.Text style={[styles.phone, { color: t.text }]}>{pretty(phone)}</react_native_1.Text>
|
|
80
|
+
{!!location && (<react_native_1.Text style={[styles.location, { color: t.subtext }]}>{location}</react_native_1.Text>)}
|
|
81
|
+
|
|
82
|
+
<react_native_1.View style={styles.bottomRow}>
|
|
83
|
+
<react_native_1.TouchableOpacity onPress={onDecline} activeOpacity={0.85} style={[styles.circleBtn, { backgroundColor: t.decline }]}>
|
|
84
|
+
<react_native_1.Text style={styles.circleIcon}>✕</react_native_1.Text>
|
|
85
|
+
</react_native_1.TouchableOpacity>
|
|
86
|
+
|
|
87
|
+
<react_native_1.TouchableOpacity onPress={onAnswer} activeOpacity={0.85} style={[styles.circleBtn, { backgroundColor: t.answer }]}>
|
|
88
|
+
<react_native_1.Text style={styles.circleIcon}>📞</react_native_1.Text>
|
|
89
|
+
</react_native_1.TouchableOpacity>
|
|
90
|
+
</react_native_1.View>
|
|
91
|
+
</react_native_1.View>);
|
|
92
|
+
}
|
|
93
|
+
exports.IncomingCallView = IncomingCallView;
|
|
94
|
+
const styles = react_native_1.StyleSheet.create({
|
|
95
|
+
container: {
|
|
96
|
+
flex: 1,
|
|
97
|
+
alignItems: 'center',
|
|
98
|
+
justifyContent: 'flex-start',
|
|
99
|
+
paddingVertical: 60,
|
|
100
|
+
},
|
|
101
|
+
status: { fontSize: 18, marginBottom: 16, marginTop: 8 },
|
|
102
|
+
avatarWrap: { marginBottom: 22, marginTop: 6 },
|
|
103
|
+
name: { fontSize: 28, fontWeight: '800', textAlign: 'center' },
|
|
104
|
+
phone: { fontSize: 18, marginTop: 8 },
|
|
105
|
+
location: { fontSize: 16, marginTop: 8 },
|
|
106
|
+
bottomRow: {
|
|
107
|
+
position: 'absolute',
|
|
108
|
+
bottom: 60,
|
|
109
|
+
left: 0,
|
|
110
|
+
right: 0,
|
|
111
|
+
flexDirection: 'row',
|
|
112
|
+
justifyContent: 'space-between',
|
|
113
|
+
paddingHorizontal: 40,
|
|
114
|
+
},
|
|
115
|
+
circleBtn: {
|
|
116
|
+
width: 80,
|
|
117
|
+
height: 80,
|
|
118
|
+
borderRadius: 40,
|
|
119
|
+
alignItems: 'center',
|
|
120
|
+
justifyContent: 'center',
|
|
121
|
+
shadowColor: '#000',
|
|
122
|
+
shadowOpacity: 0.15,
|
|
123
|
+
shadowOffset: { width: 0, height: 4 },
|
|
124
|
+
shadowRadius: 8,
|
|
125
|
+
elevation: 4,
|
|
126
|
+
},
|
|
127
|
+
circleIcon: { fontSize: 32, color: '#fff' },
|
|
128
|
+
});
|
|
@@ -0,0 +1,117 @@
|
|
|
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.OutgoingCallView = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* In-call screen used for both outgoing calls and after answering an incoming
|
|
9
|
+
* call. Shows the caller, status, timer, and mute/speaker/end controls.
|
|
10
|
+
*
|
|
11
|
+
* Pure presentation — all state comes from `useCall()`.
|
|
12
|
+
*/
|
|
13
|
+
const react_1 = __importDefault(require("react"));
|
|
14
|
+
const react_native_1 = require("react-native");
|
|
15
|
+
const CallProvider_1 = require("../CallProvider");
|
|
16
|
+
const helpers_1 = require("../helpers");
|
|
17
|
+
const Avatar_1 = require("./Avatar");
|
|
18
|
+
const theme_1 = require("./theme");
|
|
19
|
+
function OutgoingCallView({ name, phone, location, initials, onEnded, theme, header, style, }) {
|
|
20
|
+
const { callStatus, hangup, toggleMute, toggleSpeaker, formattedDuration, isMuted, isSpeaker, } = (0, CallProvider_1.useCall)();
|
|
21
|
+
const t = (0, theme_1.mergeTheme)(theme);
|
|
22
|
+
const status = (0, helpers_1.callStatusLabel)(callStatus);
|
|
23
|
+
const showTimer = ['In progress', 'On hold', 'Call ended'].includes(status);
|
|
24
|
+
const ended = status === 'Call ended';
|
|
25
|
+
const pretty = (s = '') => (s.includes('@') ? (0, helpers_1.parseSipUser)(s) : s);
|
|
26
|
+
const handleEnd = async () => {
|
|
27
|
+
if (!ended)
|
|
28
|
+
await hangup();
|
|
29
|
+
onEnded?.();
|
|
30
|
+
};
|
|
31
|
+
return (<react_native_1.View style={[styles.container, { backgroundColor: t.background }, style]}>
|
|
32
|
+
{header}
|
|
33
|
+
<react_native_1.Text style={[styles.status, ended && { color: t.decline }, !ended && { color: t.text }]}>
|
|
34
|
+
{status}
|
|
35
|
+
</react_native_1.Text>
|
|
36
|
+
{showTimer && (<react_native_1.Text style={[styles.duration, { color: t.text }]}>{formattedDuration}</react_native_1.Text>)}
|
|
37
|
+
|
|
38
|
+
<react_native_1.View style={styles.avatarWrap}>
|
|
39
|
+
<Avatar_1.Avatar initials={initials ?? (name || '??').slice(0, 2)} size={80} color={t.primary} background="#EEF2FF"/>
|
|
40
|
+
</react_native_1.View>
|
|
41
|
+
|
|
42
|
+
{!!name && <react_native_1.Text style={[styles.name, { color: t.text }]}>{pretty(name)}</react_native_1.Text>}
|
|
43
|
+
{!!phone && <react_native_1.Text style={[styles.phone, { color: t.text }]}>{pretty(phone)}</react_native_1.Text>}
|
|
44
|
+
{!!location && (<react_native_1.Text style={[styles.location, { color: t.subtext }]}>{location}</react_native_1.Text>)}
|
|
45
|
+
|
|
46
|
+
<react_native_1.View style={styles.controlsGrid}>
|
|
47
|
+
<CallControl label={isMuted ? 'Unmute' : 'Mute'} icon="🎙" active={isMuted} disabled={ended} onPress={toggleMute} theme={t}/>
|
|
48
|
+
<CallControl label={isSpeaker ? 'Earpiece' : 'Speaker'} icon="🔊" active={isSpeaker} disabled={ended} onPress={toggleSpeaker} theme={t}/>
|
|
49
|
+
<react_native_1.View style={styles.controlPlaceholder}/>
|
|
50
|
+
</react_native_1.View>
|
|
51
|
+
|
|
52
|
+
<react_native_1.TouchableOpacity style={[styles.endBtn, { backgroundColor: t.decline }]} onPress={handleEnd}>
|
|
53
|
+
<react_native_1.Text style={{ color: '#fff', fontSize: 28 }}>✕</react_native_1.Text>
|
|
54
|
+
</react_native_1.TouchableOpacity>
|
|
55
|
+
</react_native_1.View>);
|
|
56
|
+
}
|
|
57
|
+
exports.OutgoingCallView = OutgoingCallView;
|
|
58
|
+
function CallControl({ label, icon, active, disabled, onPress, theme }) {
|
|
59
|
+
const bg = active ? theme.controlOnBg : theme.controlOffBg;
|
|
60
|
+
const iconColor = active
|
|
61
|
+
? theme.controlIconOn
|
|
62
|
+
: disabled
|
|
63
|
+
? '#ccc'
|
|
64
|
+
: theme.controlIconOff;
|
|
65
|
+
return (<react_native_1.TouchableOpacity style={[styles.control, disabled && styles.controlDisabled]} onPress={onPress} disabled={disabled} activeOpacity={0.7}>
|
|
66
|
+
<react_native_1.View style={[styles.controlCircle, { backgroundColor: bg }]}>
|
|
67
|
+
<react_native_1.Text style={{ fontSize: 24, color: iconColor }}>{icon}</react_native_1.Text>
|
|
68
|
+
</react_native_1.View>
|
|
69
|
+
<react_native_1.Text style={[styles.controlLabel, disabled && { color: '#aaa' }]}>
|
|
70
|
+
{label}
|
|
71
|
+
</react_native_1.Text>
|
|
72
|
+
</react_native_1.TouchableOpacity>);
|
|
73
|
+
}
|
|
74
|
+
const styles = react_native_1.StyleSheet.create({
|
|
75
|
+
container: {
|
|
76
|
+
flex: 1,
|
|
77
|
+
alignItems: 'center',
|
|
78
|
+
justifyContent: 'flex-start',
|
|
79
|
+
paddingVertical: 60,
|
|
80
|
+
},
|
|
81
|
+
status: { fontSize: 15, marginBottom: 6, marginTop: 10 },
|
|
82
|
+
duration: { fontSize: 16, fontWeight: '600', marginBottom: 10 },
|
|
83
|
+
avatarWrap: { marginBottom: 10, marginTop: 15 },
|
|
84
|
+
name: { fontSize: 22, fontWeight: '700', textAlign: 'center' },
|
|
85
|
+
phone: { fontSize: 18, marginTop: 6, marginBottom: 6 },
|
|
86
|
+
location: { fontSize: 15, marginBottom: 16 },
|
|
87
|
+
controlsGrid: {
|
|
88
|
+
width: '88%',
|
|
89
|
+
flexDirection: 'row',
|
|
90
|
+
flexWrap: 'wrap',
|
|
91
|
+
justifyContent: 'space-between',
|
|
92
|
+
alignSelf: 'center',
|
|
93
|
+
marginTop: 30,
|
|
94
|
+
marginBottom: 18,
|
|
95
|
+
},
|
|
96
|
+
control: { width: '30%', alignItems: 'center', marginVertical: 16 },
|
|
97
|
+
controlPlaceholder: { width: '30%' },
|
|
98
|
+
controlDisabled: { opacity: 0.48 },
|
|
99
|
+
controlCircle: {
|
|
100
|
+
width: 65,
|
|
101
|
+
height: 65,
|
|
102
|
+
borderRadius: 50,
|
|
103
|
+
alignItems: 'center',
|
|
104
|
+
justifyContent: 'center',
|
|
105
|
+
marginBottom: 7,
|
|
106
|
+
},
|
|
107
|
+
controlLabel: { fontSize: 15, color: '#222', textAlign: 'center' },
|
|
108
|
+
endBtn: {
|
|
109
|
+
position: 'absolute',
|
|
110
|
+
bottom: 60,
|
|
111
|
+
width: 70,
|
|
112
|
+
height: 70,
|
|
113
|
+
borderRadius: 35,
|
|
114
|
+
alignItems: 'center',
|
|
115
|
+
justifyContent: 'center',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeTheme = exports.defaultTheme = exports.Avatar = exports.OutgoingCallView = exports.IncomingCallView = exports.Dialer = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Optional UI components for the Nativetalk Call SDK.
|
|
6
|
+
*
|
|
7
|
+
* These are entirely opt-in — the core hook (`useCall()`) gives you everything
|
|
8
|
+
* you need to roll your own UI. The components here are kept deliberately
|
|
9
|
+
* minimal (no external icon packs, no navigation library) so they work in
|
|
10
|
+
* any RN project.
|
|
11
|
+
*/
|
|
12
|
+
var Dialer_1 = require("./Dialer");
|
|
13
|
+
Object.defineProperty(exports, "Dialer", { enumerable: true, get: function () { return Dialer_1.Dialer; } });
|
|
14
|
+
var IncomingCallView_1 = require("./IncomingCallView");
|
|
15
|
+
Object.defineProperty(exports, "IncomingCallView", { enumerable: true, get: function () { return IncomingCallView_1.IncomingCallView; } });
|
|
16
|
+
var OutgoingCallView_1 = require("./OutgoingCallView");
|
|
17
|
+
Object.defineProperty(exports, "OutgoingCallView", { enumerable: true, get: function () { return OutgoingCallView_1.OutgoingCallView; } });
|
|
18
|
+
var Avatar_1 = require("./Avatar");
|
|
19
|
+
Object.defineProperty(exports, "Avatar", { enumerable: true, get: function () { return Avatar_1.Avatar; } });
|
|
20
|
+
var theme_1 = require("./theme");
|
|
21
|
+
Object.defineProperty(exports, "defaultTheme", { enumerable: true, get: function () { return theme_1.defaultTheme; } });
|
|
22
|
+
Object.defineProperty(exports, "mergeTheme", { enumerable: true, get: function () { return theme_1.mergeTheme; } });
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeTheme = exports.defaultTheme = void 0;
|
|
4
|
+
exports.defaultTheme = {
|
|
5
|
+
primary: '#2D6BFF',
|
|
6
|
+
background: '#fafafa',
|
|
7
|
+
text: '#111',
|
|
8
|
+
subtext: '#555',
|
|
9
|
+
answer: '#33c124',
|
|
10
|
+
decline: '#ff2d2d',
|
|
11
|
+
controlOnBg: '#111',
|
|
12
|
+
controlOffBg: '#fff',
|
|
13
|
+
controlIconOn: '#fff',
|
|
14
|
+
controlIconOff: '#111',
|
|
15
|
+
};
|
|
16
|
+
function mergeTheme(override) {
|
|
17
|
+
if (!override)
|
|
18
|
+
return exports.defaultTheme;
|
|
19
|
+
return { ...exports.defaultTheme, ...override };
|
|
20
|
+
}
|
|
21
|
+
exports.mergeTheme = mergeTheme;
|