@tbisoftware/phone 1.0.10 β†’ 1.0.12

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 (4) hide show
  1. package/README.md +309 -7
  2. package/dist/index.cjs +29 -29
  3. package/dist/index.js +2306 -2113
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -4,10 +4,25 @@ A reusable SIP phone component for React applications built with Tailwind CSS an
4
4
 
5
5
  ## Installation
6
6
 
7
+ ```bash
7
8
  npm install @tbisoftware/phone
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - πŸ“ž Full SIP/WebRTC phone functionality
14
+ - 🎨 Beautiful UI built with Tailwind CSS
15
+ - πŸͺ Headless mode with `usePhoneManager` hook for custom UIs
16
+ - πŸ“± Call history with localStorage persistence
17
+ - 🌐 Internationalization support with custom labels
18
+ - ⚑ Singleton pattern for reliable WebSocket connections
8
19
 
9
20
  ## Usage
10
21
 
22
+ ### Option 1: Ready-to-use Component
23
+
24
+ The simplest way to add a phone to your app:
25
+
11
26
  ```tsx
12
27
  import { Phone } from "@tbisoftware/phone";
13
28
 
@@ -21,17 +36,304 @@ const config = {
21
36
  };
22
37
 
23
38
  function App() {
24
- return <Phone config={config} />;
39
+ return (
40
+ <Phone
41
+ config={config}
42
+ onCallStart={(number) => console.log('Calling:', number)}
43
+ onCallEnd={(number, duration, status) => {
44
+ console.log(`Call to ${number} ${status}. Duration: ${duration}s`);
45
+ }}
46
+ />
47
+ );
48
+ }
49
+ ```
50
+
51
+ ### Option 2: Headless Hook for Custom UI
52
+
53
+ Use `usePhoneManager` to build your own phone interface:
54
+
55
+ ```tsx
56
+ import { usePhoneManager, formatDuration } from "@tbisoftware/phone";
57
+
58
+ const config = {
59
+ websocketUrl: "wss://your-sip-server.com:8989",
60
+ sipUri: "sip:user@domain.com",
61
+ password: "your-password",
62
+ registrarServer: "sip:domain.com",
63
+ displayName: "User Name",
64
+ authorizationUser: "auth-user",
65
+ };
66
+
67
+ function CustomPhone() {
68
+ const {
69
+ status,
70
+ callNumber,
71
+ setCallNumber,
72
+ callHistory,
73
+ currentCallDuration,
74
+ startCall,
75
+ endCall,
76
+ isReady,
77
+ connectionStatus,
78
+ } = usePhoneManager(config, {
79
+ onCallStart: (number) => console.log('Starting call to:', number),
80
+ onCallEnd: (number, duration, status) => {
81
+ console.log(`Call ended: ${number}, ${duration}s, ${status}`);
82
+ },
83
+ onStatusChange: (status) => console.log('Status:', status),
84
+ onConnectionChange: (status) => console.log('Connection:', status),
85
+ });
86
+
87
+ return (
88
+ <div className="p-4 border rounded-lg">
89
+ {/* Connection indicator */}
90
+ <div className="flex items-center gap-2 mb-4">
91
+ <div className={`w-2 h-2 rounded-full ${
92
+ connectionStatus === 'connected' ? 'bg-green-500' :
93
+ connectionStatus === 'connecting' ? 'bg-yellow-500' :
94
+ 'bg-red-500'
95
+ }`} />
96
+ <span className="text-sm text-gray-500">
97
+ {connectionStatus === 'connected' ? 'Ready' :
98
+ connectionStatus === 'connecting' ? 'Connecting...' :
99
+ 'Disconnected'}
100
+ </span>
101
+ </div>
102
+
103
+ {/* Idle state - show input */}
104
+ {status === 'disconnected' && (
105
+ <div className="flex gap-2">
106
+ <input
107
+ type="tel"
108
+ value={callNumber}
109
+ onChange={(e) => setCallNumber(e.target.value)}
110
+ onKeyDown={(e) => e.key === 'Enter' && startCall(callNumber)}
111
+ placeholder="Enter phone number"
112
+ className="flex-1 px-3 py-2 border rounded"
113
+ />
114
+ <button
115
+ onClick={() => startCall(callNumber)}
116
+ disabled={!isReady || callNumber.length < 9}
117
+ className="px-4 py-2 bg-green-500 text-white rounded disabled:opacity-50"
118
+ >
119
+ Call
120
+ </button>
121
+ </div>
122
+ )}
123
+
124
+ {/* Calling state */}
125
+ {status === 'progress' && (
126
+ <div className="text-center">
127
+ <p className="text-lg">Calling {callNumber}...</p>
128
+ <button
129
+ onClick={endCall}
130
+ className="mt-4 px-4 py-2 bg-red-500 text-white rounded"
131
+ >
132
+ Cancel
133
+ </button>
134
+ </div>
135
+ )}
136
+
137
+ {/* In call */}
138
+ {status === 'confirmed' && (
139
+ <div className="text-center">
140
+ <p className="text-lg font-bold">{callNumber}</p>
141
+ <p className="text-2xl font-mono text-green-600">
142
+ {formatDuration(currentCallDuration)}
143
+ </p>
144
+ <button
145
+ onClick={endCall}
146
+ className="mt-4 px-4 py-2 bg-red-500 text-white rounded"
147
+ >
148
+ Hang Up
149
+ </button>
150
+ </div>
151
+ )}
152
+
153
+ {/* Call history */}
154
+ {status === 'disconnected' && callHistory.length > 0 && (
155
+ <div className="mt-4">
156
+ <h3 className="text-sm font-semibold mb-2">Recent Calls</h3>
157
+ <ul className="space-y-1">
158
+ {callHistory.slice(0, 5).map((entry) => (
159
+ <li
160
+ key={entry.id}
161
+ className="flex justify-between text-sm cursor-pointer hover:bg-gray-50 p-1 rounded"
162
+ onClick={() => {
163
+ setCallNumber(entry.number);
164
+ startCall(entry.number);
165
+ }}
166
+ >
167
+ <span>{entry.number}</span>
168
+ <span className={
169
+ entry.status === 'completed' ? 'text-green-500' :
170
+ entry.status === 'failed' ? 'text-red-500' :
171
+ 'text-yellow-500'
172
+ }>
173
+ {entry.status}
174
+ </span>
175
+ </li>
176
+ ))}
177
+ </ul>
178
+ </div>
179
+ )}
180
+ </div>
181
+ );
182
+ }
183
+ ```
184
+
185
+ ### Option 3: Using Provider and usePhone Hook
186
+
187
+ For more complex scenarios where you need to access phone state from multiple components:
188
+
189
+ ```tsx
190
+ import { PhoneProvider, usePhone } from "@tbisoftware/phone";
191
+
192
+ const config = {
193
+ websocketUrl: "wss://your-sip-server.com:8989",
194
+ sipUri: "sip:user@domain.com",
195
+ password: "your-password",
196
+ registrarServer: "sip:domain.com",
197
+ displayName: "User Name",
198
+ authorizationUser: "auth-user",
199
+ };
200
+
201
+ // Wrap your app with the provider
202
+ function App() {
203
+ return (
204
+ <PhoneProvider config={config}>
205
+ <PhoneDialer />
206
+ <CallStatus />
207
+ </PhoneProvider>
208
+ );
209
+ }
210
+
211
+ // Access phone state from any child component
212
+ function PhoneDialer() {
213
+ const { callNumber, setCallNumber, startCall, isReady } = usePhone();
214
+
215
+ return (
216
+ <div>
217
+ <input
218
+ value={callNumber}
219
+ onChange={(e) => setCallNumber(e.target.value)}
220
+ />
221
+ <button onClick={() => startCall(callNumber)} disabled={!isReady}>
222
+ Call
223
+ </button>
224
+ </div>
225
+ );
226
+ }
227
+
228
+ function CallStatus() {
229
+ const { status, currentCallDuration, endCall } = usePhone();
230
+
231
+ if (status === 'disconnected') return null;
232
+
233
+ return (
234
+ <div>
235
+ <p>Status: {status}</p>
236
+ {status === 'confirmed' && <p>Duration: {currentCallDuration}s</p>}
237
+ <button onClick={endCall}>End Call</button>
238
+ </div>
239
+ );
25
240
  }
26
241
  ```
27
242
 
28
- ## Props
243
+ ## API Reference
244
+
245
+ ### `<Phone />` Component Props
246
+
247
+ | Prop | Type | Description |
248
+ |------|------|-------------|
249
+ | `config` | `PhoneConfig` | Required. SIP configuration object |
250
+ | `className` | `string` | Optional. Additional CSS classes |
251
+ | `onCallStart` | `(number: string) => void` | Callback when a call starts |
252
+ | `onCallEnd` | `(number: string, duration: number, status: 'completed' \| 'failed') => void` | Callback when a call ends |
253
+ | `onStatusChange` | `(status: PhoneStatus) => void` | Callback when status changes |
254
+ | `labels` | `Partial<PhoneLabels>` | Custom labels for internationalization |
255
+
256
+ ### `usePhoneManager(config, options)` Hook
257
+
258
+ Returns an object with:
259
+
260
+ | Property | Type | Description |
261
+ |----------|------|-------------|
262
+ | `status` | `PhoneStatus` | Current call status: `'disconnected' \| 'progress' \| 'confirmed' \| 'failed' \| 'ended'` |
263
+ | `callNumber` | `string` | Current phone number |
264
+ | `setCallNumber` | `(number: string) => void` | Set the phone number |
265
+ | `callHistory` | `CallHistoryEntry[]` | Array of past calls |
266
+ | `clearCallHistory` | `() => void` | Clear the call history |
267
+ | `currentCallDuration` | `number` | Duration of current call in seconds |
268
+ | `startCall` | `(number: string) => void` | Start a call |
269
+ | `endCall` | `() => void` | End the current call |
270
+ | `isReady` | `boolean` | Whether the phone is registered and ready |
271
+ | `connectionStatus` | `ConnectionStatus` | `'connecting' \| 'connected' \| 'disconnected' \| 'failed'` |
272
+ | `ua` | `JsSIP.UA \| null` | Raw JsSIP User Agent for advanced usage |
273
+
274
+ #### Options
29
275
 
30
- - config: PhoneConfig (required)
31
- - className: string
32
- - onCallStart: (number: string) => void
33
- - onCallEnd: (number, duration, status) => void
34
- - labels: Custom labels for i18n
276
+ | Option | Type | Default | Description |
277
+ |--------|------|---------|-------------|
278
+ | `onCallStart` | `(number: string) => void` | - | Callback when call starts |
279
+ | `onCallEnd` | `(number, duration, status) => void` | - | Callback when call ends |
280
+ | `onStatusChange` | `(status: PhoneStatus) => void` | - | Callback on status change |
281
+ | `onConnectionChange` | `(status: ConnectionStatus) => void` | - | Callback on connection change |
282
+ | `persistHistory` | `boolean` | `true` | Save history to localStorage |
283
+ | `historyKey` | `string` | `'tbi-phone-call-history'` | localStorage key for history |
284
+
285
+ ### `PhoneConfig` Type
286
+
287
+ ```typescript
288
+ interface PhoneConfig {
289
+ websocketUrl: string; // WebSocket URL of SIP server
290
+ sipUri: string; // SIP URI (sip:user@domain.com)
291
+ password: string; // SIP password
292
+ registrarServer: string; // SIP registrar server
293
+ displayName: string; // Display name for caller ID
294
+ authorizationUser: string; // Authorization username
295
+ }
296
+ ```
297
+
298
+ ### `PhoneLabels` Type
299
+
300
+ ```typescript
301
+ interface PhoneLabels {
302
+ placeholder: string; // Default: "Ingresa un nΓΊmero"
303
+ calling: string; // Default: "Llamando"
304
+ inCall: string; // Default: "En llamada"
305
+ callEnded: string; // Default: "Llamada finalizada"
306
+ inactive: string; // Default: "Inactivo"
307
+ waitingResponse: string; // Default: "Esperando respuesta..."
308
+ cancel: string; // Default: "Cancelar"
309
+ hangUp: string; // Default: "Colgar"
310
+ callHistory: string; // Default: "Historial de llamadas"
311
+ noCallsRegistered: string;// Default: "No hay llamadas registradas"
312
+ callsRegistered: string; // Default: "llamadas registradas"
313
+ noCalls: string; // Default: "No hay llamadas en el historial"
314
+ }
315
+ ```
316
+
317
+ ## Triggering Calls from Outside
318
+
319
+ You can trigger a call from anywhere in your app using a custom event:
320
+
321
+ ```javascript
322
+ // Dispatch this event to start a call
323
+ window.dispatchEvent(new CustomEvent('StartCallEvent', {
324
+ detail: { number: '+1234567890' }
325
+ }));
326
+ ```
327
+
328
+ ## Styling
329
+
330
+ The component uses Tailwind CSS classes. Make sure you have Tailwind CSS configured in your project. All components have the `tbi-phone` class for custom styling:
331
+
332
+ ```css
333
+ .tbi-phone {
334
+ /* Your custom styles */
335
+ }
336
+ ```
35
337
 
36
338
  ## License
37
339