@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,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.destinationToSipUri = exports.callStatusLabel = exports.regStateName = exports.callStateName = exports.initialsFrom = exports.formatDuration = exports.sanitizeDial = exports.parseSipUser = exports.formatTenantDomain = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Strips `http(s)://` and trailing `/` from a tenant domain.
|
|
6
|
+
*
|
|
7
|
+
* Multi-tenant backends often hand the SDK an HTTP URL ("https://t1.example.com/")
|
|
8
|
+
* when what we need is a bare SIP host ("t1.example.com"). Doing this once
|
|
9
|
+
* here is friendlier than failing the registration with a cryptic error.
|
|
10
|
+
*/
|
|
11
|
+
function formatTenantDomain(domain) {
|
|
12
|
+
if (!domain)
|
|
13
|
+
return '';
|
|
14
|
+
return domain.replace(/^https?:\/\//, '').replace(/\/$/, '');
|
|
15
|
+
}
|
|
16
|
+
exports.formatTenantDomain = formatTenantDomain;
|
|
17
|
+
/**
|
|
18
|
+
* Extract the user-part of `sip:user@domain` (or returns the input unchanged).
|
|
19
|
+
*
|
|
20
|
+
* The regex stops at `@`, `;` (URI params like `;transport=tcp`), and `>`
|
|
21
|
+
* (display-name angle brackets) so it handles all three common SIP URI
|
|
22
|
+
* formats without separate parsers.
|
|
23
|
+
*/
|
|
24
|
+
function parseSipUser(sipUri = '') {
|
|
25
|
+
const m = /sip:([^@;>]+)/i.exec(sipUri);
|
|
26
|
+
return m?.[1] ?? sipUri.replace(/^sip:/i, '');
|
|
27
|
+
}
|
|
28
|
+
exports.parseSipUser = parseSipUser;
|
|
29
|
+
// Allowlist anything dialable: digits plus the three special SIP keys.
|
|
30
|
+
// Notably DROPS letters — PSTN gateways reject them, and the dial-pad
|
|
31
|
+
// converts letters via the standard ABC/DEF mapping before getting here.
|
|
32
|
+
function sanitizeDial(input = '') {
|
|
33
|
+
return input.replace(/[^\d+#*]/g, '');
|
|
34
|
+
}
|
|
35
|
+
exports.sanitizeDial = sanitizeDial;
|
|
36
|
+
/** Format seconds as `M:SS` or `H:MM:SS`. */
|
|
37
|
+
function formatDuration(totalSeconds) {
|
|
38
|
+
const s = Math.max(0, Math.floor(totalSeconds));
|
|
39
|
+
const h = Math.floor(s / 3600);
|
|
40
|
+
const m = Math.floor((s % 3600) / 60);
|
|
41
|
+
const sec = s % 60;
|
|
42
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
43
|
+
return h ? `${h}:${pad(m)}:${pad(sec)}` : `${m}:${pad(sec)}`;
|
|
44
|
+
}
|
|
45
|
+
exports.formatDuration = formatDuration;
|
|
46
|
+
/** Convert two-character initials for avatar placeholders. */
|
|
47
|
+
function initialsFrom(name) {
|
|
48
|
+
return (name || '??').slice(0, 2).toUpperCase();
|
|
49
|
+
}
|
|
50
|
+
exports.initialsFrom = initialsFrom;
|
|
51
|
+
/**
|
|
52
|
+
* Linphone numeric-state → string-state mapping.
|
|
53
|
+
*
|
|
54
|
+
* The SDK's current native modules emit states as strings ("Connected"),
|
|
55
|
+
* but older Linphone bindings — and any third party that wires up the
|
|
56
|
+
* native module directly — may still send the raw enum ordinals. Keeping
|
|
57
|
+
* this map means a Linphone upgrade or a different binding doesn't break
|
|
58
|
+
* existing apps.
|
|
59
|
+
*/
|
|
60
|
+
const CALL_STATE_BY_INT = {
|
|
61
|
+
0: 'Idle',
|
|
62
|
+
1: 'IncomingReceived',
|
|
63
|
+
2: 'PushIncomingReceived',
|
|
64
|
+
3: 'OutgoingInit',
|
|
65
|
+
4: 'OutgoingProgress',
|
|
66
|
+
5: 'OutgoingRinging',
|
|
67
|
+
6: 'OutgoingEarlyMedia',
|
|
68
|
+
7: 'Connected',
|
|
69
|
+
8: 'StreamsRunning',
|
|
70
|
+
9: 'Pausing',
|
|
71
|
+
10: 'Paused',
|
|
72
|
+
11: 'Resuming',
|
|
73
|
+
12: 'Referred',
|
|
74
|
+
13: 'Error',
|
|
75
|
+
14: 'End',
|
|
76
|
+
15: 'PausedByRemote',
|
|
77
|
+
16: 'UpdatedByRemote',
|
|
78
|
+
17: 'IncomingEarlyMedia',
|
|
79
|
+
18: 'Updating',
|
|
80
|
+
19: 'Released',
|
|
81
|
+
20: 'EarlyUpdatedByRemote',
|
|
82
|
+
21: 'EarlyUpdating',
|
|
83
|
+
};
|
|
84
|
+
const REG_STATE_BY_INT = {
|
|
85
|
+
0: 'none',
|
|
86
|
+
1: 'progress',
|
|
87
|
+
2: 'ok',
|
|
88
|
+
3: 'cleared',
|
|
89
|
+
4: 'failed',
|
|
90
|
+
};
|
|
91
|
+
/** Normalise a raw call-state value (string or int) to its canonical string form. */
|
|
92
|
+
function callStateName(raw) {
|
|
93
|
+
if (typeof raw === 'number')
|
|
94
|
+
return CALL_STATE_BY_INT[raw] ?? String(raw);
|
|
95
|
+
return String(raw ?? '');
|
|
96
|
+
}
|
|
97
|
+
exports.callStateName = callStateName;
|
|
98
|
+
/** Normalise a raw registration-state value (string or int) to canonical lowercase string. */
|
|
99
|
+
function regStateName(raw) {
|
|
100
|
+
if (typeof raw === 'number') {
|
|
101
|
+
return REG_STATE_BY_INT[raw] ?? 'unknown';
|
|
102
|
+
}
|
|
103
|
+
const s = String(raw ?? '').toLowerCase();
|
|
104
|
+
if (s === 'none' ||
|
|
105
|
+
s === 'progress' ||
|
|
106
|
+
s === 'ok' ||
|
|
107
|
+
s === 'cleared' ||
|
|
108
|
+
s === 'failed') {
|
|
109
|
+
return s;
|
|
110
|
+
}
|
|
111
|
+
return 'unknown';
|
|
112
|
+
}
|
|
113
|
+
exports.regStateName = regStateName;
|
|
114
|
+
/**
|
|
115
|
+
* Map a raw call status to a user-friendly UI label.
|
|
116
|
+
*
|
|
117
|
+
* You typically don't need to call this directly — `useCall().callStatus`
|
|
118
|
+
* already returns the canonical state name, and the bundled
|
|
119
|
+
* `<OutgoingCallView>` does its own mapping. Use this when building a custom UI.
|
|
120
|
+
*/
|
|
121
|
+
function callStatusLabel(status) {
|
|
122
|
+
switch (status) {
|
|
123
|
+
case 'OutgoingInit':
|
|
124
|
+
case 'OutgoingProgress':
|
|
125
|
+
case 'OutgoingEarlyMedia':
|
|
126
|
+
return 'Calling…';
|
|
127
|
+
case 'OutgoingRinging':
|
|
128
|
+
return 'Ringing…';
|
|
129
|
+
case 'Connected':
|
|
130
|
+
case 'StreamsRunning':
|
|
131
|
+
return 'In progress';
|
|
132
|
+
case 'Pausing':
|
|
133
|
+
return 'Pausing…';
|
|
134
|
+
case 'Paused':
|
|
135
|
+
case 'PausedByRemote':
|
|
136
|
+
return 'On hold';
|
|
137
|
+
case 'Resuming':
|
|
138
|
+
return 'Resuming…';
|
|
139
|
+
case 'End':
|
|
140
|
+
case 'Released':
|
|
141
|
+
return 'Call ended';
|
|
142
|
+
case 'Error':
|
|
143
|
+
return 'Call failed';
|
|
144
|
+
case 'IncomingReceived':
|
|
145
|
+
case 'PushIncomingReceived':
|
|
146
|
+
case 'IncomingEarlyMedia':
|
|
147
|
+
return 'Incoming call';
|
|
148
|
+
default:
|
|
149
|
+
return status || 'Idle';
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.callStatusLabel = callStatusLabel;
|
|
153
|
+
/**
|
|
154
|
+
* Resolve a destination string to a SIP URI.
|
|
155
|
+
*
|
|
156
|
+
* Three-case dispatch in order of "most explicit wins":
|
|
157
|
+
* 1. `sip:100@gw.com` → already a URI, pass through unchanged.
|
|
158
|
+
* 2. `100@gw.com` → caller specified the gateway, just prefix `sip:`.
|
|
159
|
+
* 3. `100` (bare) → fill in the configured domain.
|
|
160
|
+
*
|
|
161
|
+
* This lets apps mix internal-extension dialling with PSTN gateway URIs
|
|
162
|
+
* without needing a separate API.
|
|
163
|
+
*/
|
|
164
|
+
function destinationToSipUri(destination, domain) {
|
|
165
|
+
if (!destination)
|
|
166
|
+
return '';
|
|
167
|
+
if (destination.startsWith('sip:'))
|
|
168
|
+
return destination;
|
|
169
|
+
if (destination.includes('@'))
|
|
170
|
+
return `sip:${destination}`;
|
|
171
|
+
return `sip:${destination}@${formatTenantDomain(domain)}`;
|
|
172
|
+
}
|
|
173
|
+
exports.destinationToSipUri = destinationToSipUri;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Public entry point for `@nativetalk/react-native-call-sdk`.
|
|
4
|
+
*
|
|
5
|
+
* Most apps only need:
|
|
6
|
+
*
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { CallProvider, useCall } from '@nativetalk/react-native-call-sdk';
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* Optional UI components are exported from the `/ui` sub-path:
|
|
12
|
+
*
|
|
13
|
+
* ```tsx
|
|
14
|
+
* import { Dialer, IncomingCallView, OutgoingCallView } from '@nativetalk/react-native-call-sdk/ui';
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* Power-users can reach the raw native bridge:
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { CallEngine } from '@nativetalk/react-native-call-sdk';
|
|
21
|
+
* CallEngine.call('sip:100@sip.example.com');
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
27
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
28
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
29
|
+
}
|
|
30
|
+
Object.defineProperty(o, k2, desc);
|
|
31
|
+
}) : (function(o, m, k, k2) {
|
|
32
|
+
if (k2 === undefined) k2 = k;
|
|
33
|
+
o[k2] = m[k];
|
|
34
|
+
}));
|
|
35
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
36
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
37
|
+
}) : function(o, v) {
|
|
38
|
+
o["default"] = v;
|
|
39
|
+
});
|
|
40
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.callEvents = exports.CallEngine = exports.sanitizeDial = exports.regStateName = exports.parseSipUser = exports.initialsFrom = exports.formatTenantDomain = exports.formatDuration = exports.destinationToSipUri = exports.callStatusLabel = exports.callStateName = exports.useCallSafe = exports.useCall = exports.CallProvider = void 0;
|
|
49
|
+
var CallProvider_1 = require("./CallProvider");
|
|
50
|
+
Object.defineProperty(exports, "CallProvider", { enumerable: true, get: function () { return CallProvider_1.CallProvider; } });
|
|
51
|
+
Object.defineProperty(exports, "useCall", { enumerable: true, get: function () { return CallProvider_1.useCall; } });
|
|
52
|
+
Object.defineProperty(exports, "useCallSafe", { enumerable: true, get: function () { return CallProvider_1.useCallSafe; } });
|
|
53
|
+
var helpers_1 = require("./helpers");
|
|
54
|
+
Object.defineProperty(exports, "callStateName", { enumerable: true, get: function () { return helpers_1.callStateName; } });
|
|
55
|
+
Object.defineProperty(exports, "callStatusLabel", { enumerable: true, get: function () { return helpers_1.callStatusLabel; } });
|
|
56
|
+
Object.defineProperty(exports, "destinationToSipUri", { enumerable: true, get: function () { return helpers_1.destinationToSipUri; } });
|
|
57
|
+
Object.defineProperty(exports, "formatDuration", { enumerable: true, get: function () { return helpers_1.formatDuration; } });
|
|
58
|
+
Object.defineProperty(exports, "formatTenantDomain", { enumerable: true, get: function () { return helpers_1.formatTenantDomain; } });
|
|
59
|
+
Object.defineProperty(exports, "initialsFrom", { enumerable: true, get: function () { return helpers_1.initialsFrom; } });
|
|
60
|
+
Object.defineProperty(exports, "parseSipUser", { enumerable: true, get: function () { return helpers_1.parseSipUser; } });
|
|
61
|
+
Object.defineProperty(exports, "regStateName", { enumerable: true, get: function () { return helpers_1.regStateName; } });
|
|
62
|
+
Object.defineProperty(exports, "sanitizeDial", { enumerable: true, get: function () { return helpers_1.sanitizeDial; } });
|
|
63
|
+
const Native = __importStar(require("./native"));
|
|
64
|
+
/**
|
|
65
|
+
* Direct access to the native bridge.
|
|
66
|
+
*
|
|
67
|
+
* This is escape-hatch territory — prefer `useCall()` whenever possible. Use
|
|
68
|
+
* `CallEngine` only when you need to drive the SDK from outside React (e.g.
|
|
69
|
+
* a headless task) or to wire iOS VoIP push tokens.
|
|
70
|
+
*/
|
|
71
|
+
exports.CallEngine = {
|
|
72
|
+
init: Native.init,
|
|
73
|
+
register: Native.register,
|
|
74
|
+
call: Native.call,
|
|
75
|
+
answer: Native.answer,
|
|
76
|
+
end: Native.end,
|
|
77
|
+
hangup: Native.end,
|
|
78
|
+
decline: Native.decline,
|
|
79
|
+
mute: Native.mute,
|
|
80
|
+
speaker: Native.speaker,
|
|
81
|
+
hold: Native.hold,
|
|
82
|
+
resume: Native.resume,
|
|
83
|
+
sendDtmf: Native.sendDtmf,
|
|
84
|
+
playKeyTone: Native.playKeyTone,
|
|
85
|
+
refreshRegisters: Native.refreshRegisters,
|
|
86
|
+
setRegisterEnabled: Native.setRegisterEnabled,
|
|
87
|
+
getRegistrationStatus: Native.getRegistrationStatus,
|
|
88
|
+
getCallLogs: Native.getCallLogs,
|
|
89
|
+
startNativeServices: Native.startNativeServices,
|
|
90
|
+
stopNativeServices: Native.stopNativeServices,
|
|
91
|
+
registerVoipToken: Native.registerVoipToken,
|
|
92
|
+
ensureMicPermission: Native.ensureMicPermission,
|
|
93
|
+
on: Native.on,
|
|
94
|
+
};
|
|
95
|
+
var native_1 = require("./native");
|
|
96
|
+
Object.defineProperty(exports, "callEvents", { enumerable: true, get: function () { return native_1.callEvents; } });
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.on = exports.registerVoipToken = exports.getCallLogs = exports.playKeyTone = exports.sendDtmf = exports.resume = exports.hold = exports.speaker = exports.mute = exports.decline = exports.end = exports.answer = exports.call = exports.getRegistrationStatus = exports.setRegisterEnabled = exports.refreshRegisters = exports.register = exports.stopNativeServices = exports.startNativeServices = exports.init = exports.ensureMicPermission = exports.callEvents = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Thin bridge to the native `NativetalkCallSdk` module.
|
|
6
|
+
*
|
|
7
|
+
* This file is the only place that talks to `NativeModules` and the event
|
|
8
|
+
* emitter. Everything else in the SDK uses these exports so the JS layer
|
|
9
|
+
* stays testable.
|
|
10
|
+
*
|
|
11
|
+
* If you see "TurboModuleRegistry … was not found" at runtime, the native
|
|
12
|
+
* library isn't linked. See `docs/installation.md`.
|
|
13
|
+
*/
|
|
14
|
+
const react_native_1 = require("react-native");
|
|
15
|
+
const LINKING_ERROR = `The package '@nativetalk/react-native-call-sdk' doesn't seem to be linked. Make sure:\n\n` +
|
|
16
|
+
`- you rebuilt the app after installing the package\n` +
|
|
17
|
+
`- you are not using Expo Go (use a dev client or bare workflow)\n` +
|
|
18
|
+
`- (iOS) you ran 'pod install' inside the ios/ directory\n`;
|
|
19
|
+
// Proxy fallback: if autolinking failed, every method access throws a helpful
|
|
20
|
+
// error instead of a cryptic "undefined is not a function". This means a
|
|
21
|
+
// misconfigured app crashes EARLY with a clear message, not deep inside JS.
|
|
22
|
+
const NativetalkCallSdk = react_native_1.NativeModules.NativetalkCallSdk ??
|
|
23
|
+
new Proxy({}, {
|
|
24
|
+
get() {
|
|
25
|
+
throw new Error(LINKING_ERROR);
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
// Single shared emitter — re-creating it per subscription wastes native
|
|
29
|
+
// handlers and can drop events during the swap.
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
exports.callEvents = new react_native_1.NativeEventEmitter(NativetalkCallSdk);
|
|
32
|
+
/** Request microphone permission on Android. Returns true if granted (or platform is iOS). */
|
|
33
|
+
async function ensureMicPermission() {
|
|
34
|
+
if (react_native_1.Platform.OS !== 'android')
|
|
35
|
+
return true;
|
|
36
|
+
const granted = await react_native_1.PermissionsAndroid.request(react_native_1.PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, {
|
|
37
|
+
title: 'Microphone Permission',
|
|
38
|
+
message: 'Microphone access is required to make and receive calls.',
|
|
39
|
+
buttonPositive: 'OK',
|
|
40
|
+
});
|
|
41
|
+
return granted === react_native_1.PermissionsAndroid.RESULTS.GRANTED;
|
|
42
|
+
}
|
|
43
|
+
exports.ensureMicPermission = ensureMicPermission;
|
|
44
|
+
// --- Core lifecycle ---
|
|
45
|
+
// Idempotent on the native side, safe to call multiple times.
|
|
46
|
+
function init(cfg) {
|
|
47
|
+
return NativetalkCallSdk.init(cfg ?? {});
|
|
48
|
+
}
|
|
49
|
+
exports.init = init;
|
|
50
|
+
// Android-only: iOS uses CallKit + VoIP push instead of a background
|
|
51
|
+
// service. Guarding here keeps the JS API symmetric across platforms.
|
|
52
|
+
function startNativeServices() {
|
|
53
|
+
if (react_native_1.Platform.OS !== 'android')
|
|
54
|
+
return;
|
|
55
|
+
NativetalkCallSdk.startNativeServices();
|
|
56
|
+
}
|
|
57
|
+
exports.startNativeServices = startNativeServices;
|
|
58
|
+
function stopNativeServices(logout = false) {
|
|
59
|
+
if (react_native_1.Platform.OS !== 'android')
|
|
60
|
+
return;
|
|
61
|
+
NativetalkCallSdk.stopNativeServices(logout);
|
|
62
|
+
}
|
|
63
|
+
exports.stopNativeServices = stopNativeServices;
|
|
64
|
+
function register(account) {
|
|
65
|
+
NativetalkCallSdk.register(account);
|
|
66
|
+
}
|
|
67
|
+
exports.register = register;
|
|
68
|
+
// Optional-chained: older native builds may not export this method, and
|
|
69
|
+
// we'd rather no-op than crash on a non-critical refresh.
|
|
70
|
+
function refreshRegisters() {
|
|
71
|
+
NativetalkCallSdk.refreshRegisters?.();
|
|
72
|
+
}
|
|
73
|
+
exports.refreshRegisters = refreshRegisters;
|
|
74
|
+
function setRegisterEnabled(enabled) {
|
|
75
|
+
NativetalkCallSdk.setRegisterEnabled(enabled);
|
|
76
|
+
}
|
|
77
|
+
exports.setRegisterEnabled = setRegisterEnabled;
|
|
78
|
+
function getRegistrationStatus() {
|
|
79
|
+
return NativetalkCallSdk.getRegistrationStatus();
|
|
80
|
+
}
|
|
81
|
+
exports.getRegistrationStatus = getRegistrationStatus;
|
|
82
|
+
// --- Call control ---
|
|
83
|
+
function call(sipUri) {
|
|
84
|
+
NativetalkCallSdk.call(sipUri);
|
|
85
|
+
}
|
|
86
|
+
exports.call = call;
|
|
87
|
+
function answer() {
|
|
88
|
+
NativetalkCallSdk.answer();
|
|
89
|
+
}
|
|
90
|
+
exports.answer = answer;
|
|
91
|
+
function end() {
|
|
92
|
+
NativetalkCallSdk.end();
|
|
93
|
+
}
|
|
94
|
+
exports.end = end;
|
|
95
|
+
function decline(reason = 'declined') {
|
|
96
|
+
NativetalkCallSdk.decline?.(reason);
|
|
97
|
+
}
|
|
98
|
+
exports.decline = decline;
|
|
99
|
+
function mute(on) {
|
|
100
|
+
NativetalkCallSdk.mute(on);
|
|
101
|
+
}
|
|
102
|
+
exports.mute = mute;
|
|
103
|
+
function speaker(on) {
|
|
104
|
+
NativetalkCallSdk.speaker(on);
|
|
105
|
+
}
|
|
106
|
+
exports.speaker = speaker;
|
|
107
|
+
function hold() {
|
|
108
|
+
NativetalkCallSdk.hold();
|
|
109
|
+
}
|
|
110
|
+
exports.hold = hold;
|
|
111
|
+
function resume() {
|
|
112
|
+
NativetalkCallSdk.resume();
|
|
113
|
+
}
|
|
114
|
+
exports.resume = resume;
|
|
115
|
+
function sendDtmf(digit) {
|
|
116
|
+
NativetalkCallSdk.sendDtmf(digit);
|
|
117
|
+
}
|
|
118
|
+
exports.sendDtmf = sendDtmf;
|
|
119
|
+
function playKeyTone(digit) {
|
|
120
|
+
NativetalkCallSdk.playKeyTone(digit);
|
|
121
|
+
}
|
|
122
|
+
exports.playKeyTone = playKeyTone;
|
|
123
|
+
// --- Call logs ---
|
|
124
|
+
function getCallLogs() {
|
|
125
|
+
return NativetalkCallSdk.getCallLogs();
|
|
126
|
+
}
|
|
127
|
+
exports.getCallLogs = getCallLogs;
|
|
128
|
+
// --- iOS push token (advanced; only used if you wire VoIP push manually) ---
|
|
129
|
+
// Call from the host AppDelegate's PushKit `didUpdate` handler so the token
|
|
130
|
+
// gets attached to the SIP account params. Android FCM uses a different flow.
|
|
131
|
+
function registerVoipToken(hex) {
|
|
132
|
+
if (react_native_1.Platform.OS !== 'ios')
|
|
133
|
+
return;
|
|
134
|
+
NativetalkCallSdk.registerVoipToken?.(hex);
|
|
135
|
+
}
|
|
136
|
+
exports.registerVoipToken = registerVoipToken;
|
|
137
|
+
exports.on = {
|
|
138
|
+
RegistrationChanged: (cb) => exports.callEvents.addListener('RegistrationChanged', cb),
|
|
139
|
+
CallIncoming: (cb) => exports.callEvents.addListener('CallIncoming', cb),
|
|
140
|
+
CallState: (cb) => exports.callEvents.addListener('CallState', cb),
|
|
141
|
+
CallEnded: (cb) => exports.callEvents.addListener('CallEnded', cb),
|
|
142
|
+
// The TM* events are Android-only (telephony observer for native GSM
|
|
143
|
+
// calls). They never fire on iOS but the subscription is harmless.
|
|
144
|
+
TMPhoneCallState: (cb) => exports.callEvents.addListener('TMPhoneCallState', cb),
|
|
145
|
+
TMPhoneCallInfo: (cb) => exports.callEvents.addListener('TMPhoneCallInfo', cb),
|
|
146
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Public types for the Nativetalk Call SDK.
|
|
4
|
+
*
|
|
5
|
+
* These describe the SIP account configuration, registration state, call
|
|
6
|
+
* lifecycle events, and the shape of `useCall()` and `<CallProvider>`.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,29 @@
|
|
|
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.Avatar = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const react_native_1 = require("react-native");
|
|
9
|
+
/** Tiny initials-circle used by the bundled call screens. */
|
|
10
|
+
function Avatar({ initials, size = 80, background = '#EEF2FF', color = '#2D6BFF', style, textStyle, }) {
|
|
11
|
+
return (<react_native_1.View style={[
|
|
12
|
+
styles.base,
|
|
13
|
+
{ width: size, height: size, borderRadius: size / 2, backgroundColor: background },
|
|
14
|
+
style,
|
|
15
|
+
]}>
|
|
16
|
+
<react_native_1.Text style={[
|
|
17
|
+
styles.text,
|
|
18
|
+
{ color, fontSize: Math.floor(size * 0.4) },
|
|
19
|
+
textStyle,
|
|
20
|
+
]}>
|
|
21
|
+
{(initials || '??').slice(0, 2).toUpperCase()}
|
|
22
|
+
</react_native_1.Text>
|
|
23
|
+
</react_native_1.View>);
|
|
24
|
+
}
|
|
25
|
+
exports.Avatar = Avatar;
|
|
26
|
+
const styles = react_native_1.StyleSheet.create({
|
|
27
|
+
base: { alignItems: 'center', justifyContent: 'center' },
|
|
28
|
+
text: { fontWeight: '700' },
|
|
29
|
+
});
|
|
@@ -0,0 +1,189 @@
|
|
|
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.Dialer = void 0;
|
|
27
|
+
/**
|
|
28
|
+
* Bundled dial-pad component.
|
|
29
|
+
*
|
|
30
|
+
* Drop it anywhere inside a `<CallProvider>` and it'll dial through the SDK.
|
|
31
|
+
* Override almost everything via props if you want to keep the layout but
|
|
32
|
+
* change the look.
|
|
33
|
+
*/
|
|
34
|
+
const react_1 = __importStar(require("react"));
|
|
35
|
+
const react_native_1 = require("react-native");
|
|
36
|
+
const CallProvider_1 = require("../CallProvider");
|
|
37
|
+
const helpers_1 = require("../helpers");
|
|
38
|
+
const theme_1 = require("./theme");
|
|
39
|
+
const dialPad = [
|
|
40
|
+
[
|
|
41
|
+
{ number: '1', letters: '' },
|
|
42
|
+
{ number: '2', letters: 'ABC' },
|
|
43
|
+
{ number: '3', letters: 'DEF' },
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
{ number: '4', letters: 'GHI' },
|
|
47
|
+
{ number: '5', letters: 'JKL' },
|
|
48
|
+
{ number: '6', letters: 'MNO' },
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
{ number: '7', letters: 'PQRS' },
|
|
52
|
+
{ number: '8', letters: 'TUV' },
|
|
53
|
+
{ number: '9', letters: 'WXYZ' },
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
{ number: '*', letters: '' },
|
|
57
|
+
{ number: '0', letters: '+' },
|
|
58
|
+
{ number: '#', letters: '' },
|
|
59
|
+
],
|
|
60
|
+
];
|
|
61
|
+
function Dialer({ initialValue = '', onDialed, header, renderCallButton, theme, playKeyTones = true, }) {
|
|
62
|
+
const [input, setInput] = (0, react_1.useState)(initialValue);
|
|
63
|
+
const { dial, playKeyTone } = (0, CallProvider_1.useCall)();
|
|
64
|
+
const { width, height } = (0, react_native_1.useWindowDimensions)();
|
|
65
|
+
const isCompact = height < 760;
|
|
66
|
+
const buttonSize = Math.min(width / 4.3, isCompact ? 68 : 84);
|
|
67
|
+
const t = (0, theme_1.mergeTheme)(theme);
|
|
68
|
+
const handlePress = (value) => {
|
|
69
|
+
if (playKeyTones)
|
|
70
|
+
playKeyTone(value);
|
|
71
|
+
setInput((prev) => prev + value);
|
|
72
|
+
};
|
|
73
|
+
const handleBackspace = () => setInput((prev) => prev.slice(0, -1));
|
|
74
|
+
const handleCall = async () => {
|
|
75
|
+
if (!input)
|
|
76
|
+
return;
|
|
77
|
+
try {
|
|
78
|
+
await dial(input);
|
|
79
|
+
onDialed?.(input);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
// Errors surface via <CallProvider onError>. Swallow here so the dialer
|
|
83
|
+
// doesn't crash the host app.
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const callDisabled = input.length === 0;
|
|
87
|
+
return (<react_native_1.View style={[styles.container, { backgroundColor: t.background }]}>
|
|
88
|
+
{header}
|
|
89
|
+
<react_native_1.View style={styles.inputContainer}>
|
|
90
|
+
<react_native_1.TextInput value={input} placeholder="Enter Number" placeholderTextColor="#ccc" onChangeText={(s) => setInput((0, helpers_1.sanitizeDial)(s))} keyboardType="phone-pad" inputMode="tel" autoCorrect={false} autoCapitalize="none" maxLength={64} style={[styles.inputText, { color: t.text }]} returnKeyType="done" onSubmitEditing={handleCall}/>
|
|
91
|
+
{input.length > 0 && (<react_native_1.TouchableOpacity style={styles.clearButton} onPress={handleBackspace}>
|
|
92
|
+
<react_native_1.Text style={{ fontSize: 22, color: '#999' }}>⌫</react_native_1.Text>
|
|
93
|
+
</react_native_1.TouchableOpacity>)}
|
|
94
|
+
</react_native_1.View>
|
|
95
|
+
|
|
96
|
+
<react_native_1.ScrollView style={styles.padContainer} contentContainerStyle={[
|
|
97
|
+
styles.padContent,
|
|
98
|
+
{ paddingTop: isCompact ? 16 : 30, paddingBottom: 24 },
|
|
99
|
+
]} bounces={false} showsVerticalScrollIndicator={false}>
|
|
100
|
+
{dialPad.map((row, i) => (<react_native_1.View style={[styles.padRow, { marginBottom: isCompact ? 8 : 10 }]} key={i}>
|
|
101
|
+
{row.map((item) => (<react_native_1.TouchableOpacity key={item.number} style={[
|
|
102
|
+
styles.padButton,
|
|
103
|
+
{
|
|
104
|
+
width: buttonSize,
|
|
105
|
+
height: buttonSize,
|
|
106
|
+
borderRadius: buttonSize / 2,
|
|
107
|
+
marginHorizontal: isCompact ? 6 : 8,
|
|
108
|
+
},
|
|
109
|
+
]} onPress={() => handlePress(item.number)} activeOpacity={0.8}>
|
|
110
|
+
<react_native_1.Text style={[
|
|
111
|
+
styles.padNumber,
|
|
112
|
+
{ fontSize: isCompact ? 28 : 30, color: t.text },
|
|
113
|
+
]}>
|
|
114
|
+
{item.number}
|
|
115
|
+
</react_native_1.Text>
|
|
116
|
+
{!!item.letters && (<react_native_1.Text style={[styles.padLetters, { color: t.subtext }]}>
|
|
117
|
+
{item.letters}
|
|
118
|
+
</react_native_1.Text>)}
|
|
119
|
+
</react_native_1.TouchableOpacity>))}
|
|
120
|
+
</react_native_1.View>))}
|
|
121
|
+
|
|
122
|
+
<react_native_1.View style={{ marginTop: isCompact ? 10 : 15 }}>
|
|
123
|
+
{renderCallButton ? (renderCallButton({
|
|
124
|
+
onPress: handleCall,
|
|
125
|
+
disabled: callDisabled,
|
|
126
|
+
number: input,
|
|
127
|
+
})) : (<react_native_1.TouchableOpacity onPress={handleCall} disabled={callDisabled} style={[
|
|
128
|
+
styles.callButton,
|
|
129
|
+
{
|
|
130
|
+
backgroundColor: callDisabled ? '#ccc' : t.answer,
|
|
131
|
+
width: isCompact ? 62 : 70,
|
|
132
|
+
height: isCompact ? 62 : 70,
|
|
133
|
+
borderRadius: (isCompact ? 62 : 70) / 2,
|
|
134
|
+
},
|
|
135
|
+
]}>
|
|
136
|
+
<react_native_1.Text style={{ color: '#fff', fontSize: 28 }}>📞</react_native_1.Text>
|
|
137
|
+
</react_native_1.TouchableOpacity>)}
|
|
138
|
+
</react_native_1.View>
|
|
139
|
+
</react_native_1.ScrollView>
|
|
140
|
+
</react_native_1.View>);
|
|
141
|
+
}
|
|
142
|
+
exports.Dialer = Dialer;
|
|
143
|
+
const styles = react_native_1.StyleSheet.create({
|
|
144
|
+
container: { flex: 1 },
|
|
145
|
+
inputContainer: {
|
|
146
|
+
flexDirection: 'row',
|
|
147
|
+
alignItems: 'center',
|
|
148
|
+
justifyContent: 'center',
|
|
149
|
+
minHeight: 100,
|
|
150
|
+
marginTop: 10,
|
|
151
|
+
marginBottom: 10,
|
|
152
|
+
paddingHorizontal: 30,
|
|
153
|
+
position: 'relative',
|
|
154
|
+
},
|
|
155
|
+
inputText: {
|
|
156
|
+
flex: 1,
|
|
157
|
+
fontSize: 26,
|
|
158
|
+
textAlign: 'center',
|
|
159
|
+
fontWeight: '600',
|
|
160
|
+
letterSpacing: 2,
|
|
161
|
+
paddingVertical: 12,
|
|
162
|
+
},
|
|
163
|
+
clearButton: {
|
|
164
|
+
position: 'absolute',
|
|
165
|
+
right: 35,
|
|
166
|
+
padding: 6,
|
|
167
|
+
zIndex: 10,
|
|
168
|
+
},
|
|
169
|
+
padContainer: {
|
|
170
|
+
flex: 1,
|
|
171
|
+
borderTopLeftRadius: 22,
|
|
172
|
+
borderTopRightRadius: 22,
|
|
173
|
+
},
|
|
174
|
+
padContent: { alignItems: 'center' },
|
|
175
|
+
padRow: { flexDirection: 'row', justifyContent: 'center' },
|
|
176
|
+
padButton: {
|
|
177
|
+
backgroundColor: '#fff',
|
|
178
|
+
justifyContent: 'center',
|
|
179
|
+
alignItems: 'center',
|
|
180
|
+
elevation: 1,
|
|
181
|
+
shadowColor: '#000',
|
|
182
|
+
shadowOffset: { width: 0, height: 1 },
|
|
183
|
+
shadowOpacity: 0.07,
|
|
184
|
+
shadowRadius: 1.5,
|
|
185
|
+
},
|
|
186
|
+
padNumber: { fontWeight: '600', textAlign: 'center' },
|
|
187
|
+
padLetters: { fontSize: 11, letterSpacing: 2, textAlign: 'center', marginTop: 2 },
|
|
188
|
+
callButton: { alignItems: 'center', justifyContent: 'center' },
|
|
189
|
+
});
|