@tbisoftware/phone 1.0.13 → 2.0.4
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 +266 -108
- package/dist/core/PhoneManager.d.ts +94 -0
- package/dist/core/PhoneManager.d.ts.map +1 -0
- package/dist/core/index.cjs +1 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +7 -0
- package/dist/index-Br8w8pI3.cjs +68 -0
- package/dist/index-TymkBND5.js +8237 -0
- package/dist/index.cjs +1 -68
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -8616
- package/dist/react/Phone.d.ts +3 -0
- package/dist/react/Phone.d.ts.map +1 -0
- package/dist/react/PhoneContext.d.ts +23 -0
- package/dist/react/PhoneContext.d.ts.map +1 -0
- package/dist/react/index.cjs +1 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +9 -0
- package/dist/react/usePhoneManager.d.ts +67 -0
- package/dist/react/usePhoneManager.d.ts.map +1 -0
- package/dist/usePhoneManager-OZM1GaNS.js +536 -0
- package/dist/usePhoneManager-uj2opBKT.cjs +1 -0
- package/dist/vue/index.cjs +1 -0
- package/dist/vue/index.d.ts +8 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +706 -0
- package/dist/vue/usePhone.d.ts +59 -0
- package/dist/vue/usePhone.d.ts.map +1 -0
- package/dist/vue/usePhoneManager.d.ts +72 -0
- package/dist/vue/usePhoneManager.d.ts.map +1 -0
- package/package.json +33 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @tbisoftware/phone
|
|
2
2
|
|
|
3
|
-
A reusable SIP phone component for React applications built with Tailwind CSS and JsSIP.
|
|
3
|
+
A reusable SIP phone component for **React** and **Vue** applications built with Tailwind CSS and JsSIP.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,21 +10,35 @@ npm install @tbisoftware/phone
|
|
|
10
10
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
13
|
+
- Multi-framework support: **React** and **Vue 3**
|
|
14
|
+
- Full SIP/WebRTC phone functionality
|
|
15
|
+
- Beautiful UI built with Tailwind CSS
|
|
16
|
+
- Headless mode with `usePhoneManager` hook/composable for custom UIs
|
|
17
|
+
- Call history with localStorage persistence
|
|
18
|
+
- Internationalization support with custom labels
|
|
19
|
+
- Singleton pattern for reliable WebSocket connections
|
|
20
|
+
- Framework-agnostic core for custom integrations
|
|
19
21
|
|
|
20
|
-
##
|
|
22
|
+
## Entry Points
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
| Import Path | Description |
|
|
25
|
+
|-------------|-------------|
|
|
26
|
+
| `@tbisoftware/phone` | Default React exports (backward compatible) |
|
|
27
|
+
| `@tbisoftware/phone/react` | Explicit React exports |
|
|
28
|
+
| `@tbisoftware/phone/vue` | Vue 3 exports |
|
|
29
|
+
| `@tbisoftware/phone/core` | Framework-agnostic core (PhoneManager class) |
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
# React Usage
|
|
34
|
+
|
|
35
|
+
## Option 1: Ready-to-use Component
|
|
36
|
+
|
|
37
|
+
The simplest way to add a phone to your React app:
|
|
25
38
|
|
|
26
39
|
```tsx
|
|
27
40
|
import { Phone } from "@tbisoftware/phone";
|
|
41
|
+
// or explicitly: import { Phone } from "@tbisoftware/phone/react";
|
|
28
42
|
|
|
29
43
|
const config = {
|
|
30
44
|
websocketUrl: "wss://your-sip-server.com:8989",
|
|
@@ -37,7 +51,7 @@ const config = {
|
|
|
37
51
|
|
|
38
52
|
function App() {
|
|
39
53
|
return (
|
|
40
|
-
<Phone
|
|
54
|
+
<Phone
|
|
41
55
|
config={config}
|
|
42
56
|
onCallStart={(number) => console.log('Calling:', number)}
|
|
43
57
|
onCallEnd={(number, duration, status) => {
|
|
@@ -48,7 +62,7 @@ function App() {
|
|
|
48
62
|
}
|
|
49
63
|
```
|
|
50
64
|
|
|
51
|
-
|
|
65
|
+
## Option 2: Headless Hook for Custom UI
|
|
52
66
|
|
|
53
67
|
Use `usePhoneManager` to build your own phone interface:
|
|
54
68
|
|
|
@@ -80,101 +94,32 @@ function CustomPhone() {
|
|
|
80
94
|
onCallEnd: (number, duration, status) => {
|
|
81
95
|
console.log(`Call ended: ${number}, ${duration}s, ${status}`);
|
|
82
96
|
},
|
|
83
|
-
onStatusChange: (status) => console.log('Status:', status),
|
|
84
|
-
onConnectionChange: (status) => console.log('Connection:', status),
|
|
85
97
|
});
|
|
86
98
|
|
|
87
99
|
return (
|
|
88
100
|
<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
101
|
{status === 'disconnected' && (
|
|
105
102
|
<div className="flex gap-2">
|
|
106
103
|
<input
|
|
107
104
|
type="tel"
|
|
108
105
|
value={callNumber}
|
|
109
106
|
onChange={(e) => setCallNumber(e.target.value)}
|
|
110
|
-
onKeyDown={(e) => e.key === 'Enter' && startCall(callNumber)}
|
|
111
107
|
placeholder="Enter phone number"
|
|
112
|
-
className="flex-1 px-3 py-2 border rounded"
|
|
113
108
|
/>
|
|
114
109
|
<button
|
|
115
110
|
onClick={() => startCall(callNumber)}
|
|
116
111
|
disabled={!isReady || callNumber.length < 9}
|
|
117
|
-
className="px-4 py-2 bg-green-500 text-white rounded disabled:opacity-50"
|
|
118
112
|
>
|
|
119
113
|
Call
|
|
120
114
|
</button>
|
|
121
115
|
</div>
|
|
122
116
|
)}
|
|
123
117
|
|
|
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
118
|
{status === 'confirmed' && (
|
|
139
119
|
<div className="text-center">
|
|
140
|
-
<p
|
|
141
|
-
<p
|
|
142
|
-
|
|
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>
|
|
120
|
+
<p>{callNumber}</p>
|
|
121
|
+
<p>{formatDuration(currentCallDuration)}</p>
|
|
122
|
+
<button onClick={endCall}>Hang Up</button>
|
|
178
123
|
</div>
|
|
179
124
|
)}
|
|
180
125
|
</div>
|
|
@@ -182,23 +127,15 @@ function CustomPhone() {
|
|
|
182
127
|
}
|
|
183
128
|
```
|
|
184
129
|
|
|
185
|
-
|
|
130
|
+
## Option 3: Using Provider and usePhone Hook
|
|
186
131
|
|
|
187
|
-
For
|
|
132
|
+
For complex scenarios where you need to access phone state from multiple components:
|
|
188
133
|
|
|
189
134
|
```tsx
|
|
190
135
|
import { PhoneProvider, usePhone } from "@tbisoftware/phone";
|
|
191
136
|
|
|
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
|
-
};
|
|
137
|
+
const config = { /* ... */ };
|
|
200
138
|
|
|
201
|
-
// Wrap your app with the provider
|
|
202
139
|
function App() {
|
|
203
140
|
return (
|
|
204
141
|
<PhoneProvider config={config}>
|
|
@@ -208,10 +145,9 @@ function App() {
|
|
|
208
145
|
);
|
|
209
146
|
}
|
|
210
147
|
|
|
211
|
-
// Access phone state from any child component
|
|
212
148
|
function PhoneDialer() {
|
|
213
149
|
const { callNumber, setCallNumber, startCall, isReady } = usePhone();
|
|
214
|
-
|
|
150
|
+
|
|
215
151
|
return (
|
|
216
152
|
<div>
|
|
217
153
|
<input
|
|
@@ -227,9 +163,9 @@ function PhoneDialer() {
|
|
|
227
163
|
|
|
228
164
|
function CallStatus() {
|
|
229
165
|
const { status, currentCallDuration, endCall } = usePhone();
|
|
230
|
-
|
|
166
|
+
|
|
231
167
|
if (status === 'disconnected') return null;
|
|
232
|
-
|
|
168
|
+
|
|
233
169
|
return (
|
|
234
170
|
<div>
|
|
235
171
|
<p>Status: {status}</p>
|
|
@@ -240,9 +176,222 @@ function CallStatus() {
|
|
|
240
176
|
}
|
|
241
177
|
```
|
|
242
178
|
|
|
243
|
-
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
# Vue 3 Usage
|
|
182
|
+
|
|
183
|
+
## Option 1: Ready-to-use Component
|
|
184
|
+
|
|
185
|
+
```vue
|
|
186
|
+
<script setup lang="ts">
|
|
187
|
+
import { Phone } from "@tbisoftware/phone/vue";
|
|
188
|
+
|
|
189
|
+
const config = {
|
|
190
|
+
websocketUrl: "wss://your-sip-server.com:8989",
|
|
191
|
+
sipUri: "sip:user@domain.com",
|
|
192
|
+
password: "your-password",
|
|
193
|
+
registrarServer: "sip:domain.com",
|
|
194
|
+
displayName: "User Name",
|
|
195
|
+
authorizationUser: "auth-user",
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
function handleCallStart(number: string) {
|
|
199
|
+
console.log('Calling:', number);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function handleCallEnd(number: string, duration: number, status: string) {
|
|
203
|
+
console.log(`Call to ${number} ${status}. Duration: ${duration}s`);
|
|
204
|
+
}
|
|
205
|
+
</script>
|
|
206
|
+
|
|
207
|
+
<template>
|
|
208
|
+
<Phone
|
|
209
|
+
:config="config"
|
|
210
|
+
@call-start="handleCallStart"
|
|
211
|
+
@call-end="handleCallEnd"
|
|
212
|
+
/>
|
|
213
|
+
</template>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Option 2: Headless Composable for Custom UI
|
|
217
|
+
|
|
218
|
+
Use `usePhoneManager` to build your own phone interface:
|
|
219
|
+
|
|
220
|
+
```vue
|
|
221
|
+
<script setup lang="ts">
|
|
222
|
+
import { usePhoneManager } from "@tbisoftware/phone/vue";
|
|
223
|
+
import { formatDuration } from "@tbisoftware/phone/core";
|
|
224
|
+
|
|
225
|
+
const config = {
|
|
226
|
+
websocketUrl: "wss://your-sip-server.com:8989",
|
|
227
|
+
sipUri: "sip:user@domain.com",
|
|
228
|
+
password: "your-password",
|
|
229
|
+
registrarServer: "sip:domain.com",
|
|
230
|
+
displayName: "User Name",
|
|
231
|
+
authorizationUser: "auth-user",
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const {
|
|
235
|
+
status,
|
|
236
|
+
callNumber,
|
|
237
|
+
setCallNumber,
|
|
238
|
+
callHistory,
|
|
239
|
+
currentCallDuration,
|
|
240
|
+
startCall,
|
|
241
|
+
endCall,
|
|
242
|
+
isReady,
|
|
243
|
+
connectionStatus,
|
|
244
|
+
} = usePhoneManager(config, {
|
|
245
|
+
onCallStart: (number) => console.log('Starting call to:', number),
|
|
246
|
+
onCallEnd: (number, duration, status) => {
|
|
247
|
+
console.log(`Call ended: ${number}, ${duration}s, ${status}`);
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
</script>
|
|
251
|
+
|
|
252
|
+
<template>
|
|
253
|
+
<div class="p-4 border rounded-lg">
|
|
254
|
+
<div v-if="status === 'disconnected'" class="flex gap-2">
|
|
255
|
+
<input
|
|
256
|
+
type="tel"
|
|
257
|
+
:value="callNumber"
|
|
258
|
+
@input="setCallNumber(($event.target as HTMLInputElement).value)"
|
|
259
|
+
placeholder="Enter phone number"
|
|
260
|
+
/>
|
|
261
|
+
<button
|
|
262
|
+
@click="startCall(callNumber)"
|
|
263
|
+
:disabled="!isReady || callNumber.length < 9"
|
|
264
|
+
>
|
|
265
|
+
Call
|
|
266
|
+
</button>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<div v-if="status === 'confirmed'" class="text-center">
|
|
270
|
+
<p>{{ callNumber }}</p>
|
|
271
|
+
<p>{{ formatDuration(currentCallDuration) }}</p>
|
|
272
|
+
<button @click="endCall">Hang Up</button>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
</template>
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Option 3: Using Provider and usePhone Composable
|
|
279
|
+
|
|
280
|
+
For complex scenarios where you need to access phone state from multiple components:
|
|
281
|
+
|
|
282
|
+
```vue
|
|
283
|
+
<!-- App.vue -->
|
|
284
|
+
<script setup lang="ts">
|
|
285
|
+
import { usePhoneProvider } from "@tbisoftware/phone/vue";
|
|
286
|
+
import PhoneDialer from './PhoneDialer.vue';
|
|
287
|
+
import CallStatus from './CallStatus.vue';
|
|
288
|
+
|
|
289
|
+
const config = { /* ... */ };
|
|
244
290
|
|
|
245
|
-
|
|
291
|
+
usePhoneProvider({ config });
|
|
292
|
+
</script>
|
|
293
|
+
|
|
294
|
+
<template>
|
|
295
|
+
<PhoneDialer />
|
|
296
|
+
<CallStatus />
|
|
297
|
+
</template>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
```vue
|
|
301
|
+
<!-- PhoneDialer.vue -->
|
|
302
|
+
<script setup lang="ts">
|
|
303
|
+
import { usePhone } from "@tbisoftware/phone/vue";
|
|
304
|
+
|
|
305
|
+
const { callNumber, setCallNumber, startCall, isReady } = usePhone();
|
|
306
|
+
</script>
|
|
307
|
+
|
|
308
|
+
<template>
|
|
309
|
+
<div>
|
|
310
|
+
<input
|
|
311
|
+
:value="callNumber"
|
|
312
|
+
@input="setCallNumber(($event.target as HTMLInputElement).value)"
|
|
313
|
+
/>
|
|
314
|
+
<button @click="startCall(callNumber)" :disabled="!isReady">
|
|
315
|
+
Call
|
|
316
|
+
</button>
|
|
317
|
+
</div>
|
|
318
|
+
</template>
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
```vue
|
|
322
|
+
<!-- CallStatus.vue -->
|
|
323
|
+
<script setup lang="ts">
|
|
324
|
+
import { usePhone } from "@tbisoftware/phone/vue";
|
|
325
|
+
|
|
326
|
+
const { status, currentCallDuration, endCall } = usePhone();
|
|
327
|
+
</script>
|
|
328
|
+
|
|
329
|
+
<template>
|
|
330
|
+
<div v-if="status !== 'disconnected'">
|
|
331
|
+
<p>Status: {{ status }}</p>
|
|
332
|
+
<p v-if="status === 'confirmed'">Duration: {{ currentCallDuration }}s</p>
|
|
333
|
+
<button @click="endCall">End Call</button>
|
|
334
|
+
</div>
|
|
335
|
+
</template>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
# Core Usage (Framework-Agnostic)
|
|
341
|
+
|
|
342
|
+
For custom integrations or other frameworks, you can use the `PhoneManager` class directly:
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
import { PhoneManager } from "@tbisoftware/phone/core";
|
|
346
|
+
|
|
347
|
+
const config = {
|
|
348
|
+
websocketUrl: "wss://your-sip-server.com:8989",
|
|
349
|
+
sipUri: "sip:user@domain.com",
|
|
350
|
+
password: "your-password",
|
|
351
|
+
registrarServer: "sip:domain.com",
|
|
352
|
+
displayName: "User Name",
|
|
353
|
+
authorizationUser: "auth-user",
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const manager = new PhoneManager(
|
|
357
|
+
config,
|
|
358
|
+
{
|
|
359
|
+
onStatusChange: (status) => console.log('Status:', status),
|
|
360
|
+
onCallStart: (number) => console.log('Calling:', number),
|
|
361
|
+
onCallEnd: (number, duration, status) => {
|
|
362
|
+
console.log(`Call ended: ${number}, ${duration}s, ${status}`);
|
|
363
|
+
},
|
|
364
|
+
onConnectionChange: (status) => console.log('Connection:', status),
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
persistHistory: true,
|
|
368
|
+
historyKey: 'my-phone-history',
|
|
369
|
+
}
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
// Initialize the phone
|
|
373
|
+
manager.initialize();
|
|
374
|
+
|
|
375
|
+
// Make a call
|
|
376
|
+
manager.startCall('+1234567890');
|
|
377
|
+
|
|
378
|
+
// End the call
|
|
379
|
+
manager.endCall();
|
|
380
|
+
|
|
381
|
+
// Access state
|
|
382
|
+
console.log(manager.state.status);
|
|
383
|
+
console.log(manager.state.isReady);
|
|
384
|
+
console.log(manager.state.callHistory);
|
|
385
|
+
|
|
386
|
+
// Clean up when done
|
|
387
|
+
manager.destroy();
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
# API Reference
|
|
393
|
+
|
|
394
|
+
## `<Phone />` Component Props
|
|
246
395
|
|
|
247
396
|
| Prop | Type | Description |
|
|
248
397
|
|------|------|-------------|
|
|
@@ -253,7 +402,7 @@ function CallStatus() {
|
|
|
253
402
|
| `onStatusChange` | `(status: PhoneStatus) => void` | Callback when status changes |
|
|
254
403
|
| `labels` | `Partial<PhoneLabels>` | Custom labels for internationalization |
|
|
255
404
|
|
|
256
|
-
|
|
405
|
+
## `usePhoneManager(config, options)` Hook/Composable
|
|
257
406
|
|
|
258
407
|
Returns an object with:
|
|
259
408
|
|
|
@@ -271,7 +420,7 @@ Returns an object with:
|
|
|
271
420
|
| `connectionStatus` | `ConnectionStatus` | `'connecting' \| 'connected' \| 'disconnected' \| 'failed'` |
|
|
272
421
|
| `ua` | `JsSIP.UA \| null` | Raw JsSIP User Agent for advanced usage |
|
|
273
422
|
|
|
274
|
-
|
|
423
|
+
### Options
|
|
275
424
|
|
|
276
425
|
| Option | Type | Default | Description |
|
|
277
426
|
|--------|------|---------|-------------|
|
|
@@ -282,7 +431,7 @@ Returns an object with:
|
|
|
282
431
|
| `persistHistory` | `boolean` | `true` | Save history to localStorage |
|
|
283
432
|
| `historyKey` | `string` | `'tbi-phone-call-history'` | localStorage key for history |
|
|
284
433
|
|
|
285
|
-
|
|
434
|
+
## `PhoneConfig` Type
|
|
286
435
|
|
|
287
436
|
```typescript
|
|
288
437
|
interface PhoneConfig {
|
|
@@ -295,7 +444,7 @@ interface PhoneConfig {
|
|
|
295
444
|
}
|
|
296
445
|
```
|
|
297
446
|
|
|
298
|
-
|
|
447
|
+
## `PhoneLabels` Type
|
|
299
448
|
|
|
300
449
|
```typescript
|
|
301
450
|
interface PhoneLabels {
|
|
@@ -319,9 +468,8 @@ interface PhoneLabels {
|
|
|
319
468
|
You can trigger a call from anywhere in your app using a custom event:
|
|
320
469
|
|
|
321
470
|
```javascript
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
detail: { number: '+1234567890' }
|
|
471
|
+
window.dispatchEvent(new CustomEvent('StartCallEvent', {
|
|
472
|
+
detail: { number: '+1234567890' }
|
|
325
473
|
}));
|
|
326
474
|
```
|
|
327
475
|
|
|
@@ -335,6 +483,16 @@ The component uses Tailwind CSS classes. Make sure you have Tailwind CSS configu
|
|
|
335
483
|
}
|
|
336
484
|
```
|
|
337
485
|
|
|
486
|
+
## Migration from v1 to v2
|
|
487
|
+
|
|
488
|
+
If you're upgrading from v1 (React-only), your code should continue to work without changes. The default export still provides React components.
|
|
489
|
+
|
|
490
|
+
For explicit React imports (recommended):
|
|
491
|
+
```diff
|
|
492
|
+
- import { Phone } from "@tbisoftware/phone";
|
|
493
|
+
+ import { Phone } from "@tbisoftware/phone/react";
|
|
494
|
+
```
|
|
495
|
+
|
|
338
496
|
## License
|
|
339
497
|
|
|
340
498
|
MIT
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import JsSIP from 'jssip';
|
|
2
|
+
import type { PhoneConfig, PhoneStatus, CallHistoryEntry, ConnectionStatus } from '../types';
|
|
3
|
+
export interface PhoneManagerEvents {
|
|
4
|
+
onConnecting?: () => void;
|
|
5
|
+
onConnected?: () => void;
|
|
6
|
+
onDisconnected?: () => void;
|
|
7
|
+
onRegistered?: () => void;
|
|
8
|
+
onUnregistered?: () => void;
|
|
9
|
+
onRegistrationFailed?: (cause?: string) => void;
|
|
10
|
+
onStatusChange?: (status: PhoneStatus) => void;
|
|
11
|
+
onConnectionChange?: (status: ConnectionStatus) => void;
|
|
12
|
+
onCallStart?: (number: string) => void;
|
|
13
|
+
onCallEnd?: (number: string, duration: number, status: 'completed' | 'failed') => void;
|
|
14
|
+
onDurationUpdate?: (duration: number) => void;
|
|
15
|
+
onHistoryUpdate?: (history: CallHistoryEntry[]) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface PhoneManagerState {
|
|
18
|
+
status: PhoneStatus;
|
|
19
|
+
callNumber: string;
|
|
20
|
+
callHistory: CallHistoryEntry[];
|
|
21
|
+
currentCallDuration: number;
|
|
22
|
+
isReady: boolean;
|
|
23
|
+
connectionStatus: ConnectionStatus;
|
|
24
|
+
}
|
|
25
|
+
export interface PhoneManagerOptions {
|
|
26
|
+
persistHistory?: boolean;
|
|
27
|
+
historyKey?: string;
|
|
28
|
+
maxHistoryItems?: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Framework-agnostic Phone Manager class.
|
|
32
|
+
* Handles all SIP communication logic and can be used with any UI framework.
|
|
33
|
+
*/
|
|
34
|
+
export declare class PhoneManager {
|
|
35
|
+
private config;
|
|
36
|
+
private uaInstance;
|
|
37
|
+
private currentSession;
|
|
38
|
+
private callStartedTS;
|
|
39
|
+
private durationInterval;
|
|
40
|
+
private startCallEventListener;
|
|
41
|
+
private _state;
|
|
42
|
+
private events;
|
|
43
|
+
private options;
|
|
44
|
+
private listener;
|
|
45
|
+
constructor(config: PhoneConfig, events?: PhoneManagerEvents, options?: PhoneManagerOptions);
|
|
46
|
+
/**
|
|
47
|
+
* Get the current state
|
|
48
|
+
*/
|
|
49
|
+
get state(): Readonly<PhoneManagerState>;
|
|
50
|
+
/**
|
|
51
|
+
* Get the raw JsSIP UA instance (for advanced usage)
|
|
52
|
+
*/
|
|
53
|
+
get ua(): JsSIP.UA | null;
|
|
54
|
+
/**
|
|
55
|
+
* Initialize and start the phone manager
|
|
56
|
+
*/
|
|
57
|
+
initialize(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Cleanup and destroy the phone manager
|
|
60
|
+
*/
|
|
61
|
+
destroy(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Set the call number
|
|
64
|
+
*/
|
|
65
|
+
setCallNumber(number: string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Start a call to the given number
|
|
68
|
+
*/
|
|
69
|
+
startCall(number: string): void;
|
|
70
|
+
/**
|
|
71
|
+
* End the current call
|
|
72
|
+
*/
|
|
73
|
+
endCall(): void;
|
|
74
|
+
/**
|
|
75
|
+
* Initialize the phone and start a call once registered
|
|
76
|
+
*/
|
|
77
|
+
private initializeAndCall;
|
|
78
|
+
/**
|
|
79
|
+
* Clear the call history
|
|
80
|
+
*/
|
|
81
|
+
clearHistory(): void;
|
|
82
|
+
/**
|
|
83
|
+
* Update events handlers
|
|
84
|
+
*/
|
|
85
|
+
setEvents(events: Partial<PhoneManagerEvents>): void;
|
|
86
|
+
private updateState;
|
|
87
|
+
private loadHistory;
|
|
88
|
+
private saveHistory;
|
|
89
|
+
private addToHistory;
|
|
90
|
+
private startDurationTimer;
|
|
91
|
+
private stopDurationTimer;
|
|
92
|
+
}
|
|
93
|
+
export default PhoneManager;
|
|
94
|
+
//# sourceMappingURL=PhoneManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PhoneManager.d.ts","sourceRoot":"","sources":["../../src/core/PhoneManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("../index-Br8w8pI3.cjs");exports.PhoneManager=e.PhoneManager;exports.cn=e.cn;exports.defaultLabels=e.defaultLabels;exports.formatDuration=e.formatDuration;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { PhoneManager } from './PhoneManager';
|
|
2
|
+
export type { PhoneManagerEvents, PhoneManagerState, PhoneManagerOptions, } from './PhoneManager';
|
|
3
|
+
export type { PhoneConfig, PhoneStatus, ConnectionStatus, CallHistoryEntry, PhoneLabels, } from '../types';
|
|
4
|
+
export { defaultLabels } from '../types';
|
|
5
|
+
export { formatDuration } from '../utils/formatDuration';
|
|
6
|
+
export { cn } from '../utils/cn';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EACR,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,GACtB,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EACR,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,GACd,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC"}
|