@dora-cell/sdk-react 0.1.1-beta.9 → 1.0.1

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/README.md CHANGED
@@ -5,374 +5,169 @@ React hooks and components for the Dora Cell VoIP SDK.
5
5
  ## Features
6
6
 
7
7
  ✅ **React Hooks** - Easy-to-use hooks for call management
8
- ✅ **Context Provider** - Simple setup with DoraCellProvider
9
- ✅ **TypeScript support** - Full type definitions included
8
+ ✅ **Context Provider** - Simple setup with `DoraCellProvider`
9
+ ✅ **Pre-built UI** - Includes `Dialpad`, `CallInterface`, and `CreditBalance`
10
10
  ✅ **Auto state management** - Automatic call state and connection tracking
11
- ✅ **React 18 & 19 compatible** - Works with latest React versions
11
+ ✅ **DID Management** - Hooks to fetch and switch between caller IDs
12
12
 
13
13
  ## Installation
14
14
 
15
15
  ```bash
16
- npm install @dora-cell/sdk @dora-cell/sdk-react jssip
16
+ npm install @dora-cell/sdk @dora-cell/sdk-react
17
17
  # or
18
- yarn add @dora-cell/sdk @dora-cell/sdk-react jssip
18
+ yarn add @dora-cell/sdk @dora-cell/sdk-react
19
19
  # or
20
- pnpm add @dora-cell/sdk @dora-cell/sdk-react jssip
20
+ pnpm add @dora-cell/sdk @dora-cell/sdk-react
21
21
  ```
22
22
 
23
- ## Quick Start
24
-
25
- ### Authentication
26
-
27
- You can authenticate using either an API Token (recommended for production) or Direct SIP Credentials (useful for testing/development).
28
-
29
- #### Option 1: API Token (Production)
30
- The SDK will fetch SIP credentials from your backend using the token.
31
- ```tsx
32
- const config = {
33
- auth: {
34
- type: 'api-token',
35
- apiToken: 'YOUR_API_TOKEN',
36
- // apiBaseUrl: 'https://api.your-backend.com' (Optional)
37
- }
38
- };
39
- ```
40
-
41
- #### Option 2: Extension Authentication (Plug & Play)
42
- You can connect using just your extension number. The SDK handles the server configuration automatically.
23
+ > [!NOTE]
24
+ > `@dora-cell/sdk` is a peer dependency of this package. `jssip` will be installed automatically as a dependency of the SDK.
43
25
 
44
- ```tsx
45
- const config = {
46
- auth: {
47
- type: 'extension',
48
- extension: '1000',
49
- // password: '...' // Optional override (default: 1234)
50
- }
51
- };
52
- ```
26
+ ## Quick Start
53
27
 
54
28
  ### 1. Wrap your app with DoraCellProvider
55
29
 
56
30
  ```tsx
57
- import { DoraCellProvider } from '@dora-cell/sdk-react';
31
+ import { DoraCellProvider } from "@dora-cell/sdk-react";
58
32
 
59
33
  function App() {
60
34
  return (
61
35
  <DoraCellProvider
62
36
  config={{
63
37
  auth: {
64
- type: 'api-token',
65
- apiToken: 'your-api-token-here',
66
- apiBaseUrl: 'https://api.usedora.com'
38
+ type: "api-token",
39
+ publicKey: "pk_...",
40
+ secretKey: "sk_...",
67
41
  },
68
- debug: true
42
+ environment: "production",
69
43
  }}
44
+ autoInitialize={true}
70
45
  >
71
- <YourApp />
46
+ <MainApp />
72
47
  </DoraCellProvider>
73
48
  );
74
49
  }
75
50
  ```
76
51
 
77
- ### 2. Use hooks in your components
52
+ ### 2. Use Hooks in your components
78
53
 
79
54
  ```tsx
80
- import { useCall, useConnectionStatus } from '@dora-cell/sdk-react';
81
-
82
- function CallInterface() {
83
- const {
84
- call,
85
- hangup,
86
- toggleMute,
87
- callStatus,
88
- callDuration,
89
- isMuted,
90
- currentCall
91
- } = useCall();
92
-
93
- const { isConnected, status } = useConnectionStatus();
94
- const [number, setNumber] = useState('');
95
-
96
- const handleCall = async () => {
97
- try {
98
- await call(number);
99
- } catch (error) {
100
- console.error('Call failed:', error);
101
- }
102
- };
55
+ import { useCall, useConnectionStatus } from "@dora-cell/sdk-react";
56
+
57
+ function CallManager() {
58
+ const { call, callStatus, callDuration } = useCall();
59
+ const { isConnected } = useConnectionStatus();
103
60
 
104
61
  return (
105
62
  <div>
106
- <p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>
107
-
108
- {callStatus === 'idle' && (
109
- <>
110
- <input
111
- value={number}
112
- onChange={(e) => setNumber(e.target.value)}
113
- placeholder="Enter phone number"
114
- />
115
- <button onClick={handleCall} disabled={!isConnected}>
116
- Call
117
- </button>
118
- </>
119
- )}
120
-
121
- {callStatus === 'ongoing' && (
122
- <>
123
- <p>Call with: {currentCall?.remoteNumber}</p>
124
- <p>Duration: {callDuration}</p>
125
- <button onClick={toggleMute}>
126
- {isMuted ? 'Unmute' : 'Mute'}
127
- </button>
128
- <button onClick={hangup}>Hang Up</button>
129
- </>
130
- )}
131
-
132
- {callStatus === 'ringing' && (
133
- <p>Ringing...</p>
134
- )}
63
+ <p>Status: {isConnected ? "Ready" : "Connecting..."}</p>
64
+ {callStatus === "ongoing" && <p>Talking: {callDuration}</p>}
65
+ <button onClick={() => call("08012345678")} disabled={!isConnected}>
66
+ Call Now
67
+ </button>
135
68
  </div>
136
69
  );
137
70
  }
138
71
  ```
139
72
 
140
- ## API Reference
141
-
142
- ### DoraCellProvider
143
-
144
- Wrap your app with this provider to enable SDK functionality.
145
-
146
- ```tsx
147
- <DoraCellProvider
148
- config={{
149
- auth: {
150
- type: 'api-token',
151
- apiToken: string,
152
- apiBaseUrl?: string
153
- },
154
- turnServers?: RTCIceServer[],
155
- debug?: boolean,
156
- autoSelectExtension?: boolean
157
- }}
158
- >
159
- {children}
160
- </DoraCellProvider>
161
- ```
162
-
163
- ### useCall()
164
-
165
- Hook for managing calls.
166
-
167
- ```typescript
168
- const {
169
- // Methods
170
- call: (phoneNumber: string) => Promise<void>,
171
- hangup: () => void,
172
- answerCall: () => void,
173
- toggleMute: () => void,
174
-
175
- // State
176
- callStatus: 'idle' | 'ringing' | 'ongoing',
177
- callDuration: string, // Formatted as "MM:SS"
178
- isMuted: boolean,
179
- currentCall: Call | null,
180
- error: Error | null
181
- } = useCall();
182
- ```
73
+ ## UI Components
183
74
 
184
- **Methods:**
185
- - `call(phoneNumber)` - Make an outbound call
186
- - `hangup()` - End the current call
187
- - `answerCall()` - Answer an incoming call
188
- - `toggleMute()` - Toggle mute/unmute
189
-
190
- **State:**
191
- - `callStatus` - Current call status
192
- - `callDuration` - Formatted call duration (e.g., "01:23")
193
- - `isMuted` - Whether the call is muted
194
- - `currentCall` - Current call object with details
195
- - `error` - Any call-related errors
196
-
197
- ### useConnectionStatus()
198
-
199
- Hook for monitoring connection status.
200
-
201
- ```typescript
202
- const {
203
- isConnected: boolean,
204
- status: 'disconnected' | 'connecting' | 'connected' | 'error',
205
- error: Error | null
206
- } = useConnectionStatus();
207
- ```
75
+ We provide high-level components that match the Dora Cell design language.
208
76
 
209
- ### useDoraCell()
77
+ ### `CallInterface`
210
78
 
211
- Access the underlying SDK instance directly.
79
+ A slide-over interface that handles the entire call lifecycle (Ringing, Connecting, Ongoing, Mute controls).
212
80
 
213
- ```typescript
214
- const sdk = useDoraCell();
215
-
216
- // Use SDK methods directly
217
- sdk.getExtensions();
218
- sdk.on('call:incoming', (call) => {
219
- console.log('Incoming call from:', call.remoteNumber);
220
- });
221
- ```
222
-
223
- ### UI Components
224
-
225
- The SDK includes pre-built UI components for typical call flows.
226
-
227
- #### CallInterface
228
- A complete active call modal that handles ringing, connecting, and ongoing call states.
81
+ > [!TIP]
82
+ > This component handles audio playback (remote stream and ringtones) automatically. You just need to ensure it's rendered in your tree when a call is active.
229
83
 
230
84
  ```tsx
231
- import { CallInterface } from '@dora-cell/sdk-react';
85
+ import { CallInterface } from "@dora-cell/sdk-react";
232
86
 
233
- function MyApp() {
87
+ function Dashboard() {
234
88
  const [isOpen, setIsOpen] = useState(false);
235
89
 
236
90
  return (
237
91
  <>
238
- <CallInterface
239
- isOpen={isOpen}
240
- onOpenChange={setIsOpen}
241
- onCallEnded={() => console.log('Call ended')}
242
- />
92
+ {/*
93
+ This should be rendered globally (e.g., in your root layout)
94
+ to handle incoming calls even when the dialer is closed.
95
+ */}
96
+ <CallInterface isOpen={isOpen} onOpenChange={setIsOpen} />
243
97
  </>
244
98
  );
245
99
  }
246
100
  ```
247
101
 
248
- #### Dialpad
249
- A dialpad component for entering phone numbers and placing calls.
102
+ ### `Dialpad`
103
+
104
+ A flexible keypad for entering numbers and initiating calls. It automatically fetches available Caller IDs (extensions) and allows switching between them.
105
+
106
+ ### `CreditBalance`
107
+
108
+ A standalone component that displays the user's current credit balance and currency. It automatically refreshes whenever the SDK initializes and whenever a call ends.
250
109
 
251
110
  ```tsx
252
- import { Dialpad } from '@dora-cell/sdk-react';
111
+ import { CreditBalance } from "@dora-cell/sdk-react";
253
112
 
254
- function MyApp() {
113
+ function Header() {
255
114
  return (
256
- <Dialpad
257
- onCallInitiated={(number) => console.log('Calling:', number)}
258
- initialNumber=""
259
- />
115
+ <header>
116
+ <h1>My App</h1>
117
+ <CreditBalance />
118
+ </header>
260
119
  );
261
120
  }
262
121
  ```
263
122
 
264
- ## Advanced Usage
265
-
266
- ### Handling Incoming Calls
267
-
268
- ```tsx
269
- function IncomingCallHandler() {
270
- const { currentCall, answerCall, hangup, callStatus } = useCall();
271
- const sdk = useDoraCell();
272
-
273
- useEffect(() => {
274
- const handleIncoming = (call) => {
275
- // Show notification or UI for incoming call
276
- console.log('Incoming call from:', call.remoteNumber);
277
- };
278
-
279
- sdk.on('call:incoming', handleIncoming);
280
- return () => sdk.off('call:incoming', handleIncoming);
281
- }, [sdk]);
282
-
283
- if (callStatus === 'ringing' && currentCall?.direction === 'inbound') {
284
- return (
285
- <div>
286
- <p>Incoming call from {currentCall.remoteNumber}</p>
287
- <button onClick={answerCall}>Answer</button>
288
- <button onClick={hangup}>Decline</button>
289
- </div>
290
- );
291
- }
292
-
293
- return null;
294
- }
295
- ```
123
+ ## API Reference
296
124
 
297
- ### Custom Extension Selection
125
+ ### `useCall()`
298
126
 
299
- ```tsx
300
- function CallWithExtension() {
301
- const sdk = useDoraCell();
302
- const [selectedExtension, setSelectedExtension] = useState('');
127
+ Returns methods and state for the active call.
303
128
 
304
- const extensions = sdk.getExtensions();
129
+ - `call(number)`: Initiate a call.
130
+ - `hangup()`: End current call.
131
+ - `answerCall()`: Answer incoming call.
132
+ - `toggleMute()`: Toggle microphone.
133
+ - `callStatus`: `'idle' | 'connecting' | 'ringing' | 'ongoing' | 'ended'`.
134
+ - `callDuration`: Formatted string `"00:00"`.
135
+ - `isMuted`: Boolean state.
305
136
 
306
- const makeCall = async (number: string) => {
307
- await sdk.call(number, { extension: selectedExtension });
308
- };
137
+ ### `useConnectionStatus()`
309
138
 
310
- return (
311
- <div>
312
- <select
313
- value={selectedExtension}
314
- onChange={(e) => setSelectedExtension(e.target.value)}
315
- >
316
- {extensions.map(ext => (
317
- <option key={ext} value={ext}>{ext}</option>
318
- ))}
319
- </select>
320
- {/* Rest of your call UI */}
321
- </div>
322
- );
323
- }
324
- ```
139
+ Returns the SIP registration state.
325
140
 
326
- ### Error Handling
141
+ - `isConnected`: `true` when registered and ready.
142
+ - `connectionStatus`: `'disconnected' | 'connecting' | 'connected' | 'registered' | 'registrationFailed'`.
143
+ - `extension`: The currently registered extension number.
144
+ - `error`: Any connection error object.
327
145
 
328
- ```tsx
329
- function CallWithErrorHandling() {
330
- const { call, error, callStatus } = useCall();
331
- const [number, setNumber] = useState('');
332
-
333
- const handleCall = async () => {
334
- try {
335
- await call(number);
336
- } catch (err) {
337
- console.error('Call failed:', err);
338
- // Show error to user
339
- }
340
- };
146
+ ### `useWallet()`
341
147
 
342
- return (
343
- <div>
344
- {error && <div className="error">{error.message}</div>}
345
- {/* Rest of your UI */}
346
- </div>
347
- );
348
- }
349
- ```
148
+ Returns the user's credit balance.
350
149
 
351
- ## TypeScript Support
150
+ - `balance`: Numerical balance.
151
+ - `currency`: Currency code (e.g., `"NGN"`).
152
+ - `isLoading`: Boolean loading state.
153
+ - `refresh()`: Fetch the latest balance.
352
154
 
353
- All hooks and components are fully typed. Import types as needed:
155
+ ### `useExtensions()`
354
156
 
355
- ```typescript
356
- import type { Call, CallStatus, ConnectionStatus } from '@dora-cell/sdk';
357
- import type { DoraCellConfig } from '@dora-cell/sdk-react';
358
- ```
157
+ Returns available Caller IDs.
359
158
 
360
- ## Examples
159
+ - `extensions`: Array of available DID numbers/extensions.
160
+ - `setExtension(ext)`: Switch to a different Caller ID.
161
+ - `isLoading`: Boolean loading state.
361
162
 
362
- See the `/examples` directory in the main repository for complete working examples:
363
- - `react-app/` - React application with hooks
364
- - `nextjs-app/` - Next.js application
163
+ ## Styling
365
164
 
366
- ## Requirements
165
+ The components use Tailwind CSS internally. To avoid conflicts, all styles are prefixed with `dora-`. You must import the CSS file in your main entry file (e.g., `layout.tsx` or `App.tsx`):
367
166
 
368
- - React 18.0.0 or higher (including React 19)
369
- - @dora-cell/sdk (peer dependency)
370
- - jssip (peer dependency)
167
+ ```javascript
168
+ import "@dora-cell/sdk-react/styles.css";
169
+ ```
371
170
 
372
171
  ## License
373
172
 
374
173
  MIT
375
-
376
- ## Related Packages
377
-
378
- - [@dora-cell/sdk](https://www.npmjs.com/package/@dora-cell/sdk) - Core VoIP SDK
package/dist/index.d.mts CHANGED
@@ -3,45 +3,18 @@ import React from 'react';
3
3
  import { DoraCellConfig, CallStatus, Call, ConnectionStatus, DoraCell } from '@dora-cell/sdk';
4
4
 
5
5
  interface CallInterfaceProps {
6
- /**
7
- * Whether the interface is open/visible
8
- */
9
6
  isOpen?: boolean;
10
- /**
11
- * Callback when visibility changes
12
- */
13
7
  onOpenChange?: (open: boolean) => void;
14
- /**
15
- * Callback when call ends (useful for invalidating queries)
16
- */
17
8
  onCallEnded?: () => void;
18
- /**
19
- * Custom maximize icon component
20
- */
21
9
  maximizeIcon?: React.ReactNode;
22
- /**
23
- * Custom minimize icon component
24
- */
25
10
  minimizeIcon?: React.ReactNode;
26
11
  }
27
12
  declare function CallInterface({ isOpen, onOpenChange, onCallEnded, maximizeIcon, minimizeIcon, }: CallInterfaceProps): react_jsx_runtime.JSX.Element | null;
28
13
 
29
14
  interface DialpadProps {
30
- /**
31
- * Callback when call is initiated
32
- */
33
15
  onCallInitiated?: (number: string) => void;
34
- /**
35
- * Initial number to populate
36
- */
37
16
  initialNumber?: string;
38
- /**
39
- * Whether to show the dialpad keys (0-9)
40
- */
41
17
  showKeys?: boolean;
42
- /**
43
- * Custom class name for the container
44
- */
45
18
  className?: string;
46
19
  availableExtensions?: Array<{
47
20
  label: string;
@@ -50,7 +23,7 @@ interface DialpadProps {
50
23
  selectedExtension?: string;
51
24
  onExtensionChange?: (ext: string) => void;
52
25
  }
53
- declare function Dialpad({ onCallInitiated, initialNumber, showKeys, className, availableExtensions, selectedExtension, onExtensionChange, }: DialpadProps): react_jsx_runtime.JSX.Element;
26
+ declare function Dialpad({ onCallInitiated, initialNumber, showKeys, className, availableExtensions: providedExtensions, selectedExtension: providedSelectedExtension, onExtensionChange, }: DialpadProps): react_jsx_runtime.JSX.Element;
54
27
 
55
28
  interface DoraCellContextValue {
56
29
  sdk: DoraCell | null;
@@ -61,6 +34,7 @@ interface DoraCellContextValue {
61
34
  isMuted: boolean;
62
35
  isInitialized: boolean;
63
36
  error: Error | null;
37
+ extension: string | null;
64
38
  call: (phoneNumber: string, extension?: string) => Promise<void>;
65
39
  hangup: () => void;
66
40
  toggleMute: () => void;
@@ -88,6 +62,7 @@ declare function useCall(): {
88
62
  callDuration: string;
89
63
  isMuted: boolean;
90
64
  currentCall: Call | null;
65
+ callError: string | undefined;
91
66
  };
92
67
  /**
93
68
  * Hook for connection status
@@ -97,6 +72,31 @@ declare function useConnectionStatus(): {
97
72
  isInitialized: boolean;
98
73
  isConnected: boolean;
99
74
  error: Error | null;
75
+ extension: string | null;
100
76
  };
77
+ /**
78
+ * Hook to fetch wallet/credit balance
79
+ */
80
+ declare function useWallet(): {
81
+ balance: number;
82
+ currency: string;
83
+ isLoading: boolean;
84
+ error: Error | null;
85
+ refresh: () => Promise<void>;
86
+ };
87
+ /**
88
+ * Hook for managing extensions (DID numbers)
89
+ */
90
+ declare function useExtensions(): {
91
+ extensions: any[];
92
+ isLoading: boolean;
93
+ error: Error | null;
94
+ setExtension: (extension: string) => Promise<void>;
95
+ refresh: () => Promise<void>;
96
+ };
97
+ /**
98
+ * Credit Balance component - mirrors main app style
99
+ */
100
+ declare function CreditBalance(): react_jsx_runtime.JSX.Element;
101
101
 
102
- export { CallInterface, Dialpad, DoraCellProvider, type DoraCellProviderProps, useCall, useConnectionStatus, useDoraCell };
102
+ export { CallInterface, CreditBalance, Dialpad, DoraCellProvider, type DoraCellProviderProps, useCall, useConnectionStatus, useDoraCell, useExtensions, useWallet };
package/dist/index.d.ts CHANGED
@@ -3,45 +3,18 @@ import React from 'react';
3
3
  import { DoraCellConfig, CallStatus, Call, ConnectionStatus, DoraCell } from '@dora-cell/sdk';
4
4
 
5
5
  interface CallInterfaceProps {
6
- /**
7
- * Whether the interface is open/visible
8
- */
9
6
  isOpen?: boolean;
10
- /**
11
- * Callback when visibility changes
12
- */
13
7
  onOpenChange?: (open: boolean) => void;
14
- /**
15
- * Callback when call ends (useful for invalidating queries)
16
- */
17
8
  onCallEnded?: () => void;
18
- /**
19
- * Custom maximize icon component
20
- */
21
9
  maximizeIcon?: React.ReactNode;
22
- /**
23
- * Custom minimize icon component
24
- */
25
10
  minimizeIcon?: React.ReactNode;
26
11
  }
27
12
  declare function CallInterface({ isOpen, onOpenChange, onCallEnded, maximizeIcon, minimizeIcon, }: CallInterfaceProps): react_jsx_runtime.JSX.Element | null;
28
13
 
29
14
  interface DialpadProps {
30
- /**
31
- * Callback when call is initiated
32
- */
33
15
  onCallInitiated?: (number: string) => void;
34
- /**
35
- * Initial number to populate
36
- */
37
16
  initialNumber?: string;
38
- /**
39
- * Whether to show the dialpad keys (0-9)
40
- */
41
17
  showKeys?: boolean;
42
- /**
43
- * Custom class name for the container
44
- */
45
18
  className?: string;
46
19
  availableExtensions?: Array<{
47
20
  label: string;
@@ -50,7 +23,7 @@ interface DialpadProps {
50
23
  selectedExtension?: string;
51
24
  onExtensionChange?: (ext: string) => void;
52
25
  }
53
- declare function Dialpad({ onCallInitiated, initialNumber, showKeys, className, availableExtensions, selectedExtension, onExtensionChange, }: DialpadProps): react_jsx_runtime.JSX.Element;
26
+ declare function Dialpad({ onCallInitiated, initialNumber, showKeys, className, availableExtensions: providedExtensions, selectedExtension: providedSelectedExtension, onExtensionChange, }: DialpadProps): react_jsx_runtime.JSX.Element;
54
27
 
55
28
  interface DoraCellContextValue {
56
29
  sdk: DoraCell | null;
@@ -61,6 +34,7 @@ interface DoraCellContextValue {
61
34
  isMuted: boolean;
62
35
  isInitialized: boolean;
63
36
  error: Error | null;
37
+ extension: string | null;
64
38
  call: (phoneNumber: string, extension?: string) => Promise<void>;
65
39
  hangup: () => void;
66
40
  toggleMute: () => void;
@@ -88,6 +62,7 @@ declare function useCall(): {
88
62
  callDuration: string;
89
63
  isMuted: boolean;
90
64
  currentCall: Call | null;
65
+ callError: string | undefined;
91
66
  };
92
67
  /**
93
68
  * Hook for connection status
@@ -97,6 +72,31 @@ declare function useConnectionStatus(): {
97
72
  isInitialized: boolean;
98
73
  isConnected: boolean;
99
74
  error: Error | null;
75
+ extension: string | null;
100
76
  };
77
+ /**
78
+ * Hook to fetch wallet/credit balance
79
+ */
80
+ declare function useWallet(): {
81
+ balance: number;
82
+ currency: string;
83
+ isLoading: boolean;
84
+ error: Error | null;
85
+ refresh: () => Promise<void>;
86
+ };
87
+ /**
88
+ * Hook for managing extensions (DID numbers)
89
+ */
90
+ declare function useExtensions(): {
91
+ extensions: any[];
92
+ isLoading: boolean;
93
+ error: Error | null;
94
+ setExtension: (extension: string) => Promise<void>;
95
+ refresh: () => Promise<void>;
96
+ };
97
+ /**
98
+ * Credit Balance component - mirrors main app style
99
+ */
100
+ declare function CreditBalance(): react_jsx_runtime.JSX.Element;
101
101
 
102
- export { CallInterface, Dialpad, DoraCellProvider, type DoraCellProviderProps, useCall, useConnectionStatus, useDoraCell };
102
+ export { CallInterface, CreditBalance, Dialpad, DoraCellProvider, type DoraCellProviderProps, useCall, useConnectionStatus, useDoraCell, useExtensions, useWallet };