@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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/NativetalkCallSdk.podspec +31 -0
  3. package/README.md +494 -0
  4. package/android/build.gradle +58 -0
  5. package/android/gradle.properties +2 -0
  6. package/android/src/main/AndroidManifest.xml +84 -0
  7. package/android/src/main/java/io/nativetalk/callsdk/BackgroundService.kt +149 -0
  8. package/android/src/main/java/io/nativetalk/callsdk/CallActionReceiver.kt +24 -0
  9. package/android/src/main/java/io/nativetalk/callsdk/CallService.kt +45 -0
  10. package/android/src/main/java/io/nativetalk/callsdk/Compatibility.kt +96 -0
  11. package/android/src/main/java/io/nativetalk/callsdk/CoreManager.kt +801 -0
  12. package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallScreeningService.kt +105 -0
  13. package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallSdkModule.kt +205 -0
  14. package/android/src/main/java/io/nativetalk/callsdk/NativetalkCallSdkPackage.kt +18 -0
  15. package/android/src/main/java/io/nativetalk/callsdk/TelephonyMonitor.kt +229 -0
  16. package/android/src/main/java/io/nativetalk/callsdk/Utils.kt +42 -0
  17. package/android/src/main/res/drawable/ic_nativetalk_call.xml +9 -0
  18. package/android/src/main/res/values/strings.xml +9 -0
  19. package/app.plugin.js +1 -0
  20. package/ios/NativetalkCallSdk-Bridging-Header.h +4 -0
  21. package/ios/NativetalkCallSdk.swift +738 -0
  22. package/ios/NativetalkCallSdkBridge.m +35 -0
  23. package/lib/commonjs/CallProvider.js +602 -0
  24. package/lib/commonjs/helpers.js +173 -0
  25. package/lib/commonjs/index.js +96 -0
  26. package/lib/commonjs/native.js +146 -0
  27. package/lib/commonjs/types.js +8 -0
  28. package/lib/commonjs/ui/Avatar.js +29 -0
  29. package/lib/commonjs/ui/Dialer.js +189 -0
  30. package/lib/commonjs/ui/IncomingCallView.js +128 -0
  31. package/lib/commonjs/ui/OutgoingCallView.js +117 -0
  32. package/lib/commonjs/ui/index.js +22 -0
  33. package/lib/commonjs/ui/theme.js +21 -0
  34. package/lib/module/CallProvider.js +573 -0
  35. package/lib/module/helpers.js +161 -0
  36. package/lib/module/index.js +57 -0
  37. package/lib/module/native.js +123 -0
  38. package/lib/module/types.js +7 -0
  39. package/lib/module/ui/Avatar.js +22 -0
  40. package/lib/module/ui/Dialer.js +162 -0
  41. package/lib/module/ui/IncomingCallView.js +101 -0
  42. package/lib/module/ui/OutgoingCallView.js +110 -0
  43. package/lib/module/ui/index.js +13 -0
  44. package/lib/module/ui/theme.js +17 -0
  45. package/lib/typescript/CallProvider.d.ts +46 -0
  46. package/lib/typescript/helpers.d.ts +52 -0
  47. package/lib/typescript/index.d.ts +77 -0
  48. package/lib/typescript/native.d.ts +53 -0
  49. package/lib/typescript/types.d.ts +155 -0
  50. package/lib/typescript/ui/Avatar.d.ts +13 -0
  51. package/lib/typescript/ui/Dialer.d.ts +29 -0
  52. package/lib/typescript/ui/IncomingCallView.d.ts +39 -0
  53. package/lib/typescript/ui/OutgoingCallView.d.ts +28 -0
  54. package/lib/typescript/ui/index.d.ts +13 -0
  55. package/lib/typescript/ui/theme.d.ts +20 -0
  56. package/linphonesw-pod/Sources/LinphoneSdkInfos.swift +4 -0
  57. package/linphonesw-pod/Sources/LinphoneWrapper.swift +42949 -0
  58. package/linphonesw-pod/linphonesw.podspec +46 -0
  59. package/package.json +90 -0
  60. package/plugin/build/index.js +12 -0
  61. package/plugin/build/withAndroid.js +78 -0
  62. package/plugin/build/withIos.js +66 -0
  63. package/src/CallProvider.tsx +675 -0
  64. package/src/helpers.ts +179 -0
  65. package/src/index.ts +84 -0
  66. package/src/native.ts +185 -0
  67. package/src/types.ts +202 -0
  68. package/src/ui/Avatar.tsx +46 -0
  69. package/src/ui/Dialer.tsx +248 -0
  70. package/src/ui/IncomingCallView.tsx +161 -0
  71. package/src/ui/OutgoingCallView.tsx +203 -0
  72. package/src/ui/index.ts +13 -0
  73. package/src/ui/theme.ts +36 -0
  74. package/ui/package.json +6 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * In-call screen used for both outgoing calls and after answering an incoming
3
+ * call. Shows the caller, status, timer, and mute/speaker/end controls.
4
+ *
5
+ * Pure presentation — all state comes from `useCall()`.
6
+ */
7
+ import React from 'react';
8
+ import { StyleSheet, Text, TouchableOpacity, View, } from 'react-native';
9
+ import { useCall } from '../CallProvider';
10
+ import { callStatusLabel, parseSipUser } from '../helpers';
11
+ import { Avatar } from './Avatar';
12
+ import { mergeTheme } from './theme';
13
+ export function OutgoingCallView({ name, phone, location, initials, onEnded, theme, header, style, }) {
14
+ const { callStatus, hangup, toggleMute, toggleSpeaker, formattedDuration, isMuted, isSpeaker, } = useCall();
15
+ const t = mergeTheme(theme);
16
+ const status = callStatusLabel(callStatus);
17
+ const showTimer = ['In progress', 'On hold', 'Call ended'].includes(status);
18
+ const ended = status === 'Call ended';
19
+ const pretty = (s = '') => (s.includes('@') ? parseSipUser(s) : s);
20
+ const handleEnd = async () => {
21
+ if (!ended)
22
+ await hangup();
23
+ onEnded?.();
24
+ };
25
+ return (<View style={[styles.container, { backgroundColor: t.background }, style]}>
26
+ {header}
27
+ <Text style={[styles.status, ended && { color: t.decline }, !ended && { color: t.text }]}>
28
+ {status}
29
+ </Text>
30
+ {showTimer && (<Text style={[styles.duration, { color: t.text }]}>{formattedDuration}</Text>)}
31
+
32
+ <View style={styles.avatarWrap}>
33
+ <Avatar initials={initials ?? (name || '??').slice(0, 2)} size={80} color={t.primary} background="#EEF2FF"/>
34
+ </View>
35
+
36
+ {!!name && <Text style={[styles.name, { color: t.text }]}>{pretty(name)}</Text>}
37
+ {!!phone && <Text style={[styles.phone, { color: t.text }]}>{pretty(phone)}</Text>}
38
+ {!!location && (<Text style={[styles.location, { color: t.subtext }]}>{location}</Text>)}
39
+
40
+ <View style={styles.controlsGrid}>
41
+ <CallControl label={isMuted ? 'Unmute' : 'Mute'} icon="🎙" active={isMuted} disabled={ended} onPress={toggleMute} theme={t}/>
42
+ <CallControl label={isSpeaker ? 'Earpiece' : 'Speaker'} icon="🔊" active={isSpeaker} disabled={ended} onPress={toggleSpeaker} theme={t}/>
43
+ <View style={styles.controlPlaceholder}/>
44
+ </View>
45
+
46
+ <TouchableOpacity style={[styles.endBtn, { backgroundColor: t.decline }]} onPress={handleEnd}>
47
+ <Text style={{ color: '#fff', fontSize: 28 }}>✕</Text>
48
+ </TouchableOpacity>
49
+ </View>);
50
+ }
51
+ function CallControl({ label, icon, active, disabled, onPress, theme }) {
52
+ const bg = active ? theme.controlOnBg : theme.controlOffBg;
53
+ const iconColor = active
54
+ ? theme.controlIconOn
55
+ : disabled
56
+ ? '#ccc'
57
+ : theme.controlIconOff;
58
+ return (<TouchableOpacity style={[styles.control, disabled && styles.controlDisabled]} onPress={onPress} disabled={disabled} activeOpacity={0.7}>
59
+ <View style={[styles.controlCircle, { backgroundColor: bg }]}>
60
+ <Text style={{ fontSize: 24, color: iconColor }}>{icon}</Text>
61
+ </View>
62
+ <Text style={[styles.controlLabel, disabled && { color: '#aaa' }]}>
63
+ {label}
64
+ </Text>
65
+ </TouchableOpacity>);
66
+ }
67
+ const styles = StyleSheet.create({
68
+ container: {
69
+ flex: 1,
70
+ alignItems: 'center',
71
+ justifyContent: 'flex-start',
72
+ paddingVertical: 60,
73
+ },
74
+ status: { fontSize: 15, marginBottom: 6, marginTop: 10 },
75
+ duration: { fontSize: 16, fontWeight: '600', marginBottom: 10 },
76
+ avatarWrap: { marginBottom: 10, marginTop: 15 },
77
+ name: { fontSize: 22, fontWeight: '700', textAlign: 'center' },
78
+ phone: { fontSize: 18, marginTop: 6, marginBottom: 6 },
79
+ location: { fontSize: 15, marginBottom: 16 },
80
+ controlsGrid: {
81
+ width: '88%',
82
+ flexDirection: 'row',
83
+ flexWrap: 'wrap',
84
+ justifyContent: 'space-between',
85
+ alignSelf: 'center',
86
+ marginTop: 30,
87
+ marginBottom: 18,
88
+ },
89
+ control: { width: '30%', alignItems: 'center', marginVertical: 16 },
90
+ controlPlaceholder: { width: '30%' },
91
+ controlDisabled: { opacity: 0.48 },
92
+ controlCircle: {
93
+ width: 65,
94
+ height: 65,
95
+ borderRadius: 50,
96
+ alignItems: 'center',
97
+ justifyContent: 'center',
98
+ marginBottom: 7,
99
+ },
100
+ controlLabel: { fontSize: 15, color: '#222', textAlign: 'center' },
101
+ endBtn: {
102
+ position: 'absolute',
103
+ bottom: 60,
104
+ width: 70,
105
+ height: 70,
106
+ borderRadius: 35,
107
+ alignItems: 'center',
108
+ justifyContent: 'center',
109
+ },
110
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Optional UI components for the Nativetalk Call SDK.
3
+ *
4
+ * These are entirely opt-in — the core hook (`useCall()`) gives you everything
5
+ * you need to roll your own UI. The components here are kept deliberately
6
+ * minimal (no external icon packs, no navigation library) so they work in
7
+ * any RN project.
8
+ */
9
+ export { Dialer } from './Dialer';
10
+ export { IncomingCallView } from './IncomingCallView';
11
+ export { OutgoingCallView } from './OutgoingCallView';
12
+ export { Avatar } from './Avatar';
13
+ export { defaultTheme, mergeTheme } from './theme';
@@ -0,0 +1,17 @@
1
+ export const defaultTheme = {
2
+ primary: '#2D6BFF',
3
+ background: '#fafafa',
4
+ text: '#111',
5
+ subtext: '#555',
6
+ answer: '#33c124',
7
+ decline: '#ff2d2d',
8
+ controlOnBg: '#111',
9
+ controlOffBg: '#fff',
10
+ controlIconOn: '#fff',
11
+ controlIconOff: '#111',
12
+ };
13
+ export function mergeTheme(override) {
14
+ if (!override)
15
+ return defaultTheme;
16
+ return { ...defaultTheme, ...override };
17
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * <CallProvider> — the single React component the host app mounts to enable
3
+ * calling. It wires the native event stream into React state and exposes
4
+ * everything through `useCall()`.
5
+ *
6
+ * ──────────────────────────────────────────────────────────────────────────
7
+ * Mental model
8
+ * ──────────────────────────────────────────────────────────────────────────
9
+ *
10
+ * Linphone Core ──events──► Native module ──RN bridge──► CallProvider
11
+ * ▲ │
12
+ * │ ▼
13
+ * └────── method calls (dial/answer/end) ──────────── useCall() hook
14
+ *
15
+ * - The Linphone `Core` is the single source of truth for "what state is the
16
+ * call in". CallProvider just mirrors that state into React.
17
+ * - Provider → native flows via plain method calls. Native → provider flows
18
+ * via NativeEventEmitter events (RegistrationChanged, CallIncoming, etc.).
19
+ *
20
+ * ──────────────────────────────────────────────────────────────────────────
21
+ * Design rules
22
+ * ──────────────────────────────────────────────────────────────────────────
23
+ * - No coupling to auth, navigation, or any specific HTTP client. SIP config
24
+ * is passed in as a prop; lifecycle events (incoming, ended) are exposed as
25
+ * callbacks so the host app decides how to navigate / display screens.
26
+ * - Safe to mount with `config={null}` while you fetch credentials — the SDK
27
+ * simply idles until a real config arrives, then auto-registers (unless
28
+ * `autoRegister={false}`).
29
+ * - All event callbacks are read through refs (see `*Ref` block) so the host
30
+ * app can pass fresh closures on every render WITHOUT re-subscribing the
31
+ * native events — that would drop in-flight calls during a re-render.
32
+ */
33
+ import React from 'react';
34
+ import { callStatusLabel, formatDuration } from './helpers';
35
+ import type { CallApi, CallProviderProps } from './types';
36
+ export declare function CallProvider(props: CallProviderProps): React.JSX.Element;
37
+ /**
38
+ * Access the call API from any descendant of `<CallProvider>`.
39
+ *
40
+ * Throws if used outside the provider.
41
+ */
42
+ export declare function useCall(): CallApi;
43
+ /** Internal — exported for power users who want to read the context without throwing. */
44
+ export declare function useCallSafe(): CallApi | null;
45
+ /** Re-export to allow `formatDuration`, `callStatusLabel`, etc. to be imported from the main entry too. */
46
+ export { callStatusLabel, formatDuration };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Pure helpers used by the provider and UI components.
3
+ *
4
+ * Kept here (not in `native.ts` or `CallProvider.tsx`) so they can be
5
+ * unit-tested without touching React or native modules.
6
+ */
7
+ import type { CallState, RegistrationState } from './types';
8
+ /**
9
+ * Strips `http(s)://` and trailing `/` from a tenant domain.
10
+ *
11
+ * Multi-tenant backends often hand the SDK an HTTP URL ("https://t1.example.com/")
12
+ * when what we need is a bare SIP host ("t1.example.com"). Doing this once
13
+ * here is friendlier than failing the registration with a cryptic error.
14
+ */
15
+ export declare function formatTenantDomain(domain: string | undefined | null): string;
16
+ /**
17
+ * Extract the user-part of `sip:user@domain` (or returns the input unchanged).
18
+ *
19
+ * The regex stops at `@`, `;` (URI params like `;transport=tcp`), and `>`
20
+ * (display-name angle brackets) so it handles all three common SIP URI
21
+ * formats without separate parsers.
22
+ */
23
+ export declare function parseSipUser(sipUri?: string): string;
24
+ export declare function sanitizeDial(input?: string): string;
25
+ /** Format seconds as `M:SS` or `H:MM:SS`. */
26
+ export declare function formatDuration(totalSeconds: number): string;
27
+ /** Convert two-character initials for avatar placeholders. */
28
+ export declare function initialsFrom(name: string | undefined | null): string;
29
+ /** Normalise a raw call-state value (string or int) to its canonical string form. */
30
+ export declare function callStateName(raw: unknown): CallState;
31
+ /** Normalise a raw registration-state value (string or int) to canonical lowercase string. */
32
+ export declare function regStateName(raw: unknown): RegistrationState;
33
+ /**
34
+ * Map a raw call status to a user-friendly UI label.
35
+ *
36
+ * You typically don't need to call this directly — `useCall().callStatus`
37
+ * already returns the canonical state name, and the bundled
38
+ * `<OutgoingCallView>` does its own mapping. Use this when building a custom UI.
39
+ */
40
+ export declare function callStatusLabel(status: CallState): string;
41
+ /**
42
+ * Resolve a destination string to a SIP URI.
43
+ *
44
+ * Three-case dispatch in order of "most explicit wins":
45
+ * 1. `sip:100@gw.com` → already a URI, pass through unchanged.
46
+ * 2. `100@gw.com` → caller specified the gateway, just prefix `sip:`.
47
+ * 3. `100` (bare) → fill in the configured domain.
48
+ *
49
+ * This lets apps mix internal-extension dialling with PSTN gateway URIs
50
+ * without needing a separate API.
51
+ */
52
+ export declare function destinationToSipUri(destination: string, domain: string): string;
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Public entry point for `@nativetalk/react-native-call-sdk`.
3
+ *
4
+ * Most apps only need:
5
+ *
6
+ * ```tsx
7
+ * import { CallProvider, useCall } from '@nativetalk/react-native-call-sdk';
8
+ * ```
9
+ *
10
+ * Optional UI components are exported from the `/ui` sub-path:
11
+ *
12
+ * ```tsx
13
+ * import { Dialer, IncomingCallView, OutgoingCallView } from '@nativetalk/react-native-call-sdk/ui';
14
+ * ```
15
+ *
16
+ * Power-users can reach the raw native bridge:
17
+ *
18
+ * ```ts
19
+ * import { CallEngine } from '@nativetalk/react-native-call-sdk';
20
+ * CallEngine.call('sip:100@sip.example.com');
21
+ * ```
22
+ */
23
+ export { CallProvider, useCall, useCallSafe } from './CallProvider';
24
+ export type { CallApi, CallLogEntry, CallProviderEvents, CallProviderProps, CallState, DeclineReason, IncomingCallInfo, RegistrationEvent, RegistrationState, SipConfig, SipTransport, } from './types';
25
+ export { callStateName, callStatusLabel, destinationToSipUri, formatDuration, formatTenantDomain, initialsFrom, parseSipUser, regStateName, sanitizeDial, } from './helpers';
26
+ import * as Native from './native';
27
+ /**
28
+ * Direct access to the native bridge.
29
+ *
30
+ * This is escape-hatch territory — prefer `useCall()` whenever possible. Use
31
+ * `CallEngine` only when you need to drive the SDK from outside React (e.g.
32
+ * a headless task) or to wire iOS VoIP push tokens.
33
+ */
34
+ export declare const CallEngine: {
35
+ init: typeof Native.init;
36
+ register: typeof Native.register;
37
+ call: typeof Native.call;
38
+ answer: typeof Native.answer;
39
+ end: typeof Native.end;
40
+ hangup: typeof Native.end;
41
+ decline: typeof Native.decline;
42
+ mute: typeof Native.mute;
43
+ speaker: typeof Native.speaker;
44
+ hold: typeof Native.hold;
45
+ resume: typeof Native.resume;
46
+ sendDtmf: typeof Native.sendDtmf;
47
+ playKeyTone: typeof Native.playKeyTone;
48
+ refreshRegisters: typeof Native.refreshRegisters;
49
+ setRegisterEnabled: typeof Native.setRegisterEnabled;
50
+ getRegistrationStatus: typeof Native.getRegistrationStatus;
51
+ getCallLogs: typeof Native.getCallLogs;
52
+ startNativeServices: typeof Native.startNativeServices;
53
+ stopNativeServices: typeof Native.stopNativeServices;
54
+ registerVoipToken: typeof Native.registerVoipToken;
55
+ ensureMicPermission: typeof Native.ensureMicPermission;
56
+ on: {
57
+ RegistrationChanged: (cb: (event: any) => void) => {
58
+ remove: () => void;
59
+ };
60
+ CallIncoming: (cb: (event: any) => void) => {
61
+ remove: () => void;
62
+ };
63
+ CallState: (cb: (event: any) => void) => {
64
+ remove: () => void;
65
+ };
66
+ CallEnded: (cb: (event: any) => void) => {
67
+ remove: () => void;
68
+ };
69
+ TMPhoneCallState: (cb: (event: any) => void) => {
70
+ remove: () => void;
71
+ };
72
+ TMPhoneCallInfo: (cb: (event: any) => void) => {
73
+ remove: () => void;
74
+ };
75
+ };
76
+ };
77
+ export { callEvents } from './native';
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Thin bridge to the native `NativetalkCallSdk` module.
3
+ *
4
+ * This file is the only place that talks to `NativeModules` and the event
5
+ * emitter. Everything else in the SDK uses these exports so the JS layer
6
+ * stays testable.
7
+ *
8
+ * If you see "TurboModuleRegistry … was not found" at runtime, the native
9
+ * library isn't linked. See `docs/installation.md`.
10
+ */
11
+ import { NativeEventEmitter } from 'react-native';
12
+ import type { CallLogEntry, RegistrationEvent, SipTransport } from './types';
13
+ export declare const callEvents: NativeEventEmitter;
14
+ /** Request microphone permission on Android. Returns true if granted (or platform is iOS). */
15
+ export declare function ensureMicPermission(): Promise<boolean>;
16
+ export declare function init(cfg?: Record<string, unknown>): Promise<void>;
17
+ export declare function startNativeServices(): void;
18
+ export declare function stopNativeServices(logout?: boolean): void;
19
+ export interface RegisterArgs {
20
+ username: string;
21
+ password: string;
22
+ domain: string;
23
+ transport?: SipTransport;
24
+ }
25
+ export declare function register(account: RegisterArgs): void;
26
+ export declare function refreshRegisters(): void;
27
+ export declare function setRegisterEnabled(enabled: boolean): void;
28
+ export declare function getRegistrationStatus(): Promise<RegistrationEvent>;
29
+ export declare function call(sipUri: string): void;
30
+ export declare function answer(): void;
31
+ export declare function end(): void;
32
+ export declare function decline(reason?: string): void;
33
+ export declare function mute(on: boolean): void;
34
+ export declare function speaker(on: boolean): void;
35
+ export declare function hold(): void;
36
+ export declare function resume(): void;
37
+ export declare function sendDtmf(digit: string): void;
38
+ export declare function playKeyTone(digit: string): void;
39
+ export declare function getCallLogs(): Promise<CallLogEntry[]>;
40
+ export declare function registerVoipToken(hex: string): void;
41
+ type Listener<T> = (event: T) => void;
42
+ type Sub = {
43
+ remove: () => void;
44
+ };
45
+ export declare const on: {
46
+ RegistrationChanged: (cb: Listener<any>) => Sub;
47
+ CallIncoming: (cb: Listener<any>) => Sub;
48
+ CallState: (cb: Listener<any>) => Sub;
49
+ CallEnded: (cb: Listener<any>) => Sub;
50
+ TMPhoneCallState: (cb: Listener<any>) => Sub;
51
+ TMPhoneCallInfo: (cb: Listener<any>) => Sub;
52
+ };
53
+ export {};
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Public types for the Nativetalk Call SDK.
3
+ *
4
+ * These describe the SIP account configuration, registration state, call
5
+ * lifecycle events, and the shape of `useCall()` and `<CallProvider>`.
6
+ */
7
+ /// <reference types="react" />
8
+ /** Transport used for SIP signalling. Most providers support `tcp`; use `tls` for encrypted signalling. */
9
+ export type SipTransport = 'udp' | 'tcp' | 'tls';
10
+ /**
11
+ * SIP account credentials.
12
+ *
13
+ * `domain` should be the SIP server hostname (and optionally `:port`). Do NOT
14
+ * include `sip:` or `http(s)://` — the SDK strips those automatically, but it's
15
+ * cleaner not to send them.
16
+ */
17
+ export interface SipConfig {
18
+ username: string;
19
+ password: string;
20
+ domain: string;
21
+ transport?: SipTransport;
22
+ }
23
+ /** Registration lifecycle states reported by the underlying SIP stack. */
24
+ export type RegistrationState = 'none' | 'progress' | 'ok' | 'cleared' | 'failed' | 'unknown';
25
+ /** Registration state event payload. */
26
+ export interface RegistrationEvent {
27
+ state: RegistrationState;
28
+ message?: string;
29
+ username?: string;
30
+ domain?: string;
31
+ displayName?: string;
32
+ /** Human-readable form of `state` (e.g. `"Ok"`, `"Failed"`). */
33
+ pretty?: string;
34
+ }
35
+ /**
36
+ * Linphone-derived call lifecycle states. Use the string forms in your UI.
37
+ *
38
+ * The numeric mapping is preserved internally for backwards compatibility with
39
+ * raw Linphone state values, but `useCall().callStatus` always returns the
40
+ * string form.
41
+ */
42
+ export type CallState = 'Idle' | 'IncomingReceived' | 'PushIncomingReceived' | 'OutgoingInit' | 'OutgoingProgress' | 'OutgoingRinging' | 'OutgoingEarlyMedia' | 'Connected' | 'StreamsRunning' | 'Pausing' | 'Paused' | 'Resuming' | 'Referred' | 'Error' | 'End' | 'PausedByRemote' | 'UpdatedByRemote' | 'IncomingEarlyMedia' | 'Updating' | 'Released' | 'EarlyUpdatedByRemote' | 'EarlyUpdating' | string;
43
+ /** Reason passed to `decline()`. Maps to standard SIP response codes. */
44
+ export type DeclineReason = 'declined' | 'busy' | 'notacceptable' | 'temporarilyunavailable';
45
+ /** Information about an incoming call, surfaced via `useCall().incomingInfo`. */
46
+ export interface IncomingCallInfo {
47
+ /** Best-effort display name: display-name → username → SIP user-part → `"Unknown"`. */
48
+ name: string;
49
+ /** Phone number or SIP identifier the call came from. */
50
+ phone: string;
51
+ /** Two-character initials for avatar placeholders. */
52
+ initials: string;
53
+ /** SIP call ID. May be missing on push-initiated calls before media flows. */
54
+ callId?: string;
55
+ /** Raw SIP URI of the remote party (`sip:user@domain`). */
56
+ uri?: string;
57
+ }
58
+ /** A single call history entry, normalised across platforms. */
59
+ export interface CallLogEntry {
60
+ id: number;
61
+ /** ISO-8601 timestamp (UTC). */
62
+ call_start: string;
63
+ call_type: 'LOCAL' | 'DID' | 'STANDARD' | string;
64
+ caller_id: string;
65
+ call_direction: 'inbound' | 'outbound' | string;
66
+ called_number: string;
67
+ /** Platform-specific shape — Android returns `{ text, code }`, iOS returns a string. */
68
+ disposition: {
69
+ text: string;
70
+ code: number;
71
+ } | string;
72
+ debit: string;
73
+ /** `MM:SS` or `HH:MM:SS`. */
74
+ duration: string;
75
+ destination: string;
76
+ sip_user: string;
77
+ created_at: string;
78
+ updated_at: string;
79
+ }
80
+ /** Optional callbacks the host app can supply to `<CallProvider>`. */
81
+ export interface CallProviderEvents {
82
+ /** Fired when a new incoming call arrives. Use this to navigate to your incoming-call screen. */
83
+ onIncomingCall?: (info: IncomingCallInfo) => void;
84
+ /** Fired when an outgoing call has been initiated via `dial()`. */
85
+ onOutgoingCall?: (info: {
86
+ phone: string;
87
+ initials: string;
88
+ }) => void;
89
+ /** Fired when the active call ends (any reason). */
90
+ onCallEnded?: () => void;
91
+ /** Fired on every call state transition. */
92
+ onCallStateChanged?: (state: CallState) => void;
93
+ /** Fired on every registration state transition. */
94
+ onRegistrationStateChanged?: (event: RegistrationEvent) => void;
95
+ /** Fired when an error occurs (e.g. registration failure). */
96
+ onError?: (error: {
97
+ code: string;
98
+ message: string;
99
+ }) => void;
100
+ }
101
+ /** Props for `<CallProvider>`. */
102
+ export interface CallProviderProps extends CallProviderEvents {
103
+ children: React.ReactNode;
104
+ /**
105
+ * SIP credentials. Pass `null` or omit while you load them from your backend —
106
+ * the SDK will idle until a valid config arrives.
107
+ */
108
+ config?: SipConfig | null;
109
+ /**
110
+ * If true (default), the SDK calls `register()` automatically whenever
111
+ * `config` changes. Set to false if you want manual control via the
112
+ * `register()` action.
113
+ */
114
+ autoRegister?: boolean;
115
+ /**
116
+ * If true (default on Android), the SDK starts the background/foreground
117
+ * service so calls can still arrive when the app is backgrounded. Has no
118
+ * effect on iOS — there CallKit + VoIP push handles backgrounding.
119
+ */
120
+ enableNativeServices?: boolean;
121
+ /**
122
+ * If true, the SDK requests `RECORD_AUDIO` permission on Android during
123
+ * provider mount. Default true. Disable if you handle permissions yourself.
124
+ */
125
+ requestMicPermission?: boolean;
126
+ }
127
+ /** Public API returned by `useCall()`. */
128
+ export interface CallApi {
129
+ config: SipConfig | null;
130
+ registration: RegistrationEvent | null;
131
+ callStatus: CallState;
132
+ durationSec: number;
133
+ formattedDuration: string;
134
+ incoming: boolean;
135
+ incomingInfo: IncomingCallInfo | null;
136
+ isMuted: boolean;
137
+ isSpeaker: boolean;
138
+ isHeld: boolean;
139
+ dial: (destination: string) => Promise<void>;
140
+ answer: () => Promise<void>;
141
+ hangup: () => Promise<void>;
142
+ decline: (reason?: DeclineReason) => Promise<void>;
143
+ toggleMute: () => void;
144
+ toggleSpeaker: () => void;
145
+ toggleHold: () => void;
146
+ sendDtmf: (digit: string) => void;
147
+ playKeyTone: (digit: string) => void;
148
+ register: (config?: SipConfig) => Promise<void>;
149
+ refreshRegistration: () => Promise<void>;
150
+ unregister: () => Promise<void>;
151
+ getCallLogs: () => Promise<CallLogEntry[]>;
152
+ getRegistrationStatus: () => Promise<RegistrationEvent>;
153
+ startNativeServices: () => void;
154
+ stopNativeServices: (logout?: boolean) => void;
155
+ }
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { ViewStyle, TextStyle } from 'react-native';
3
+ interface AvatarProps {
4
+ initials: string;
5
+ size?: number;
6
+ background?: string;
7
+ color?: string;
8
+ style?: ViewStyle;
9
+ textStyle?: TextStyle;
10
+ }
11
+ /** Tiny initials-circle used by the bundled call screens. */
12
+ export declare function Avatar({ initials, size, background, color, style, textStyle, }: AvatarProps): React.JSX.Element;
13
+ export {};
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Bundled dial-pad component.
3
+ *
4
+ * Drop it anywhere inside a `<CallProvider>` and it'll dial through the SDK.
5
+ * Override almost everything via props if you want to keep the layout but
6
+ * change the look.
7
+ */
8
+ import React from 'react';
9
+ import { type CallTheme } from './theme';
10
+ interface DialerProps {
11
+ /** Initial value of the input. */
12
+ initialValue?: string;
13
+ /** Called whenever the user submits a number (presses the call button or hits return). */
14
+ onDialed?: (number: string) => void;
15
+ /** Header rendered above the input. Pass `null` to hide. */
16
+ header?: React.ReactNode | null;
17
+ /** Custom call button. Receives an `onPress` and the current number. */
18
+ renderCallButton?: (props: {
19
+ onPress: () => void;
20
+ disabled: boolean;
21
+ number: string;
22
+ }) => React.ReactNode;
23
+ /** Theme overrides. */
24
+ theme?: Partial<CallTheme>;
25
+ /** If true (default) the dial-pad plays a DTMF UI tone on each key press. */
26
+ playKeyTones?: boolean;
27
+ }
28
+ export declare function Dialer({ initialValue, onDialed, header, renderCallButton, theme, playKeyTones, }: DialerProps): React.JSX.Element;
29
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Drop-in screen rendered when an incoming call is ringing.
3
+ *
4
+ * Wire-up:
5
+ *
6
+ * ```tsx
7
+ * <CallProvider
8
+ * onIncomingCall={() => navigation.navigate('IncomingCall')}
9
+ * config={cfg}
10
+ * >
11
+ * …
12
+ * </CallProvider>
13
+ *
14
+ * // your IncomingCall screen:
15
+ * <IncomingCallView onAnswered={() => navigation.replace('InCall')} />
16
+ * ```
17
+ */
18
+ import React from 'react';
19
+ import { type StyleProp, type ViewStyle } from 'react-native';
20
+ import { type CallTheme } from './theme';
21
+ interface IncomingCallViewProps {
22
+ /** Called once `answer()` resolves — typically you navigate to the in-call screen here. */
23
+ onAnswered?: () => void;
24
+ /** Called after `decline()` completes. */
25
+ onDeclined?: () => void;
26
+ /** Called when there's no live incoming call (e.g. caller hung up first). */
27
+ onDismissed?: () => void;
28
+ /** Override the location string under the caller name. */
29
+ location?: string;
30
+ /** Custom title — defaults to "Incoming call". */
31
+ title?: string;
32
+ /** Theme overrides. */
33
+ theme?: Partial<CallTheme>;
34
+ /** Optional top-of-screen element (e.g. a logo). */
35
+ header?: React.ReactNode;
36
+ style?: StyleProp<ViewStyle>;
37
+ }
38
+ export declare function IncomingCallView({ onAnswered, onDeclined, onDismissed, location, title, theme, header, style, }: IncomingCallViewProps): React.JSX.Element;
39
+ export {};
@@ -0,0 +1,28 @@
1
+ /**
2
+ * In-call screen used for both outgoing calls and after answering an incoming
3
+ * call. Shows the caller, status, timer, and mute/speaker/end controls.
4
+ *
5
+ * Pure presentation — all state comes from `useCall()`.
6
+ */
7
+ import React from 'react';
8
+ import { type StyleProp, type ViewStyle } from 'react-native';
9
+ import { type CallTheme } from './theme';
10
+ interface OutgoingCallViewProps {
11
+ /** Caller name to display. Falls back to the SIP user-part. */
12
+ name?: string;
13
+ /** Phone number to display under the name. */
14
+ phone?: string;
15
+ /** Free-form location string (e.g. "Lagos, Nigeria"). Optional. */
16
+ location?: string;
17
+ /** Two-character avatar initials. */
18
+ initials?: string;
19
+ /** Called after hangup completes. Usually you `navigation.goBack()`. */
20
+ onEnded?: () => void;
21
+ /** Theme overrides. */
22
+ theme?: Partial<CallTheme>;
23
+ /** Optional top-of-screen element (e.g. a logo). */
24
+ header?: React.ReactNode;
25
+ style?: StyleProp<ViewStyle>;
26
+ }
27
+ export declare function OutgoingCallView({ name, phone, location, initials, onEnded, theme, header, style, }: OutgoingCallViewProps): React.JSX.Element;
28
+ export {};
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Optional UI components for the Nativetalk Call SDK.
3
+ *
4
+ * These are entirely opt-in — the core hook (`useCall()`) gives you everything
5
+ * you need to roll your own UI. The components here are kept deliberately
6
+ * minimal (no external icon packs, no navigation library) so they work in
7
+ * any RN project.
8
+ */
9
+ export { Dialer } from './Dialer';
10
+ export { IncomingCallView } from './IncomingCallView';
11
+ export { OutgoingCallView } from './OutgoingCallView';
12
+ export { Avatar } from './Avatar';
13
+ export { defaultTheme, mergeTheme, type CallTheme } from './theme';