vg-x07df 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/.azure-pipelines/publish-public.yml +37 -0
- package/.azure-pipelines/publish.yml +39 -0
- package/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/AUTO_JOIN_GUIDE.md +411 -0
- package/README.md +215 -0
- package/Screenshot 2025-09-24 at 14.34.48.png +0 -0
- package/Screenshot 2025-10-04 at 12.58.54.png +0 -0
- package/biome.json +48 -0
- package/examples/demo/.env.example +19 -0
- package/examples/demo/CHANGELOG.md +22 -0
- package/examples/demo/README.md +72 -0
- package/examples/demo/eslint.config.js +23 -0
- package/examples/demo/index.html +13 -0
- package/examples/demo/package.json +34 -0
- package/examples/demo/pnpm-lock.yaml +2098 -0
- package/examples/demo/pnpm-workspace.yaml +1 -0
- package/examples/demo/public/vite.svg +1 -0
- package/examples/demo/src/App.css +52 -0
- package/examples/demo/src/App.tsx +176 -0
- package/examples/demo/src/assets/react.svg +1 -0
- package/examples/demo/src/components/auth/LoginForm.css +144 -0
- package/examples/demo/src/components/auth/LoginForm.tsx +80 -0
- package/examples/demo/src/components/calling/AutoJoinSettings.tsx +213 -0
- package/examples/demo/src/components/calling/AutoJoinStatus.tsx +72 -0
- package/examples/demo/src/components/calling/CallInitiator.css +258 -0
- package/examples/demo/src/components/calling/CallInitiator.tsx +142 -0
- package/examples/demo/src/components/calling/CallNotifications.css +119 -0
- package/examples/demo/src/components/calling/CallNotifications.tsx +108 -0
- package/examples/demo/src/components/calling/IncomingCallModal.css +192 -0
- package/examples/demo/src/components/calling/IncomingCallModal.tsx +78 -0
- package/examples/demo/src/components/calling/MinimizedCall.css +156 -0
- package/examples/demo/src/components/calling/MinimizedCall.tsx +78 -0
- package/examples/demo/src/components/conference/ConferenceHeader.css +265 -0
- package/examples/demo/src/components/conference/ConferenceHeader.tsx +78 -0
- package/examples/demo/src/components/conference/EnhancedControlBar.css +356 -0
- package/examples/demo/src/components/conference/EnhancedControlBar.tsx +262 -0
- package/examples/demo/src/components/conference/PaginationControls.css +67 -0
- package/examples/demo/src/components/conference/PaginationControls.tsx +64 -0
- package/examples/demo/src/components/conference/ParticipantGrid.css +153 -0
- package/examples/demo/src/components/conference/ParticipantGrid.tsx +87 -0
- package/examples/demo/src/components/conference/ParticipantTile.css +210 -0
- package/examples/demo/src/components/conference/ParticipantTile.tsx +114 -0
- package/examples/demo/src/components/conference/VideoConference.css +214 -0
- package/examples/demo/src/components/conference/VideoConference.tsx +93 -0
- package/examples/demo/src/contexts/AuthContext.tsx +105 -0
- package/examples/demo/src/hooks/useAuth.ts +5 -0
- package/examples/demo/src/hooks/useCallTimer.ts +42 -0
- package/examples/demo/src/index.css +68 -0
- package/examples/demo/src/main.tsx +10 -0
- package/examples/demo/src/services/auth.service.ts +153 -0
- package/examples/demo/src/types/auth.types.ts +31 -0
- package/examples/demo/tsconfig.app.json +28 -0
- package/examples/demo/tsconfig.json +7 -0
- package/examples/demo/tsconfig.node.json +26 -0
- package/examples/demo/vite.config.ts +15 -0
- package/images/callpad-without-ai.png +0 -0
- package/package.json +28 -0
- package/packages/sdk/CHANGELOG.md +33 -0
- package/packages/sdk/LICENSE +21 -0
- package/packages/sdk/README.md +97 -0
- package/packages/sdk/documentation.md +1132 -0
- package/packages/sdk/openapi-ts.config.ts +7 -0
- package/packages/sdk/package.json +88 -0
- package/packages/sdk/src/core/auth.manager.ts +52 -0
- package/packages/sdk/src/core/events/event-bus.ts +301 -0
- package/packages/sdk/src/core/events/index.ts +8 -0
- package/packages/sdk/src/core/events/types.ts +165 -0
- package/packages/sdk/src/core/index.ts +3 -0
- package/packages/sdk/src/core/signal/api.config.ts +49 -0
- package/packages/sdk/src/core/signal/index.ts +16 -0
- package/packages/sdk/src/core/signal/signal.client.ts +101 -0
- package/packages/sdk/src/core/signal/types.ts +110 -0
- package/packages/sdk/src/core/socketio/handlers/base.handler.ts +212 -0
- package/packages/sdk/src/core/socketio/handlers/call-accepted.handler.ts +34 -0
- package/packages/sdk/src/core/socketio/handlers/call-canceled.handler.ts +34 -0
- package/packages/sdk/src/core/socketio/handlers/call-declined.handler.ts +29 -0
- package/packages/sdk/src/core/socketio/handlers/call-ended.handler.ts +40 -0
- package/packages/sdk/src/core/socketio/handlers/call-incoming.handler.ts +72 -0
- package/packages/sdk/src/core/socketio/handlers/call-join-info.handler.ts +181 -0
- package/packages/sdk/src/core/socketio/handlers/call-participant-joined.handler.ts +42 -0
- package/packages/sdk/src/core/socketio/handlers/call-participant-joining.handler.ts +42 -0
- package/packages/sdk/src/core/socketio/handlers/call-timeout.handler.ts +31 -0
- package/packages/sdk/src/core/socketio/handlers/handler.registry.ts +62 -0
- package/packages/sdk/src/core/socketio/handlers/index.ts +21 -0
- package/packages/sdk/src/core/socketio/handlers/participant-left.handler.ts +37 -0
- package/packages/sdk/src/core/socketio/handlers/schema.ts +130 -0
- package/packages/sdk/src/core/socketio/index.ts +5 -0
- package/packages/sdk/src/core/socketio/socket.manager.ts +187 -0
- package/packages/sdk/src/core/socketio/types.ts +14 -0
- package/packages/sdk/src/core/types.ts +23 -0
- package/packages/sdk/src/generated/api/core/ApiError.ts +21 -0
- package/packages/sdk/src/generated/api/core/ApiRequestOptions.ts +13 -0
- package/packages/sdk/src/generated/api/core/ApiResult.ts +7 -0
- package/packages/sdk/src/generated/api/core/CancelablePromise.ts +126 -0
- package/packages/sdk/src/generated/api/core/OpenAPI.ts +55 -0
- package/packages/sdk/src/generated/api/core/request.ts +339 -0
- package/packages/sdk/src/generated/api/index.ts +5 -0
- package/packages/sdk/src/generated/api/models.ts +219 -0
- package/packages/sdk/src/generated/api/services.ts +225 -0
- package/packages/sdk/src/hooks/index.ts +21 -0
- package/packages/sdk/src/hooks/useAutoJoin.ts +66 -0
- package/packages/sdk/src/hooks/useCallActions.ts +28 -0
- package/packages/sdk/src/hooks/useCallQuality.ts +416 -0
- package/packages/sdk/src/hooks/useCallState.ts +23 -0
- package/packages/sdk/src/hooks/useConnection.ts +15 -0
- package/packages/sdk/src/hooks/useDevices.ts +296 -0
- package/packages/sdk/src/hooks/useErrorRecovery.ts +299 -0
- package/packages/sdk/src/hooks/useErrors.ts +84 -0
- package/packages/sdk/src/hooks/useEvent.ts +188 -0
- package/packages/sdk/src/hooks/useMediaControls.ts +215 -0
- package/packages/sdk/src/hooks/useParticipantStatus.ts +318 -0
- package/packages/sdk/src/hooks/useParticipants.ts +111 -0
- package/packages/sdk/src/index.ts +66 -0
- package/packages/sdk/src/livekit/constants.ts +76 -0
- package/packages/sdk/src/livekit/device.manager.ts +172 -0
- package/packages/sdk/src/livekit/error-classifier.ts +155 -0
- package/packages/sdk/src/livekit/events/eventBridge.ts +371 -0
- package/packages/sdk/src/livekit/events/trackRegistry.ts +114 -0
- package/packages/sdk/src/livekit/index.ts +49 -0
- package/packages/sdk/src/livekit/livekit.service.ts +110 -0
- package/packages/sdk/src/livekit/media.controls.ts +315 -0
- package/packages/sdk/src/livekit/room.manager.ts +79 -0
- package/packages/sdk/src/livekit/track.utils.ts +230 -0
- package/packages/sdk/src/livekit/types.ts +135 -0
- package/packages/sdk/src/provider/RtcProvider.tsx +78 -0
- package/packages/sdk/src/services/call-actions.ts +260 -0
- package/packages/sdk/src/services/error-recovery.ts +461 -0
- package/packages/sdk/src/services/index.ts +2 -0
- package/packages/sdk/src/services/sdk-builder.ts +104 -0
- package/packages/sdk/src/state/errors.ts +163 -0
- package/packages/sdk/src/state/selectors.ts +28 -0
- package/packages/sdk/src/state/store.ts +36 -0
- package/packages/sdk/src/state/types.ts +151 -0
- package/packages/sdk/src/utils/logger.ts +183 -0
- package/packages/sdk/tsconfig.json +49 -0
- package/packages/sdk/tsup.config.ts +51 -0
- package/pnpm-workspace.yaml +4 -0
- package/tsconfig.base.json +19 -0
- package/turbo.json +34 -0
|
@@ -0,0 +1,1132 @@
|
|
|
1
|
+
# CallPad SDK Documentation
|
|
2
|
+
|
|
3
|
+
A production-ready headless SDK for CallPad audio/video calls built on React, LiveKit, and Socket.IO.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [RTC Provider Configuration](#rtc-provider-configuration)
|
|
10
|
+
- [Core Concepts](#core-concepts)
|
|
11
|
+
- [Call Management](#call-management)
|
|
12
|
+
- [Intercepting Incoming Calls](#intercepting-incoming-calls)
|
|
13
|
+
- [Initiating Calls](#initiating-calls)
|
|
14
|
+
- [Accepting and Declining Calls](#accepting-and-declining-calls)
|
|
15
|
+
- [Managing Call State](#managing-call-state)
|
|
16
|
+
- [Hooks Reference](#hooks-reference)
|
|
17
|
+
- [Event System](#event-system)
|
|
18
|
+
- [Media Controls](#media-controls)
|
|
19
|
+
- [Participant Management](#participant-management)
|
|
20
|
+
- [Auto-Join Configuration](#auto-join-configuration)
|
|
21
|
+
- [Error Handling](#error-handling)
|
|
22
|
+
- [TypeScript Support](#typescript-support)
|
|
23
|
+
- [Advanced Usage](#advanced-usage)
|
|
24
|
+
- [Examples](#examples)
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
Install the SDK using pnpm (recommended):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add vg-x07df
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Or using npm:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install vg-x07df
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Peer Dependencies
|
|
41
|
+
|
|
42
|
+
The SDK requires the following peer dependencies:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pnpm add react@>=18 react-dom@>=18 livekit-client@^2.8.0 socket.io-client@^4.7.0
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
### 1. Setup the RTC Provider
|
|
51
|
+
|
|
52
|
+
Wrap your application with the `RtcProvider` to enable CallPad functionality:
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
import React from 'react';
|
|
56
|
+
import { RtcProvider } from 'vg-x07df';
|
|
57
|
+
|
|
58
|
+
const rtcOptions = {
|
|
59
|
+
appId: 'your-app-id',
|
|
60
|
+
signalHost: 'https://your-signal-server.com',
|
|
61
|
+
authProvider: () => {
|
|
62
|
+
// Return your authentication token
|
|
63
|
+
return localStorage.getItem('auth-token');
|
|
64
|
+
},
|
|
65
|
+
logLevel: 'info',
|
|
66
|
+
enableDebug: false,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
function App() {
|
|
70
|
+
return (
|
|
71
|
+
<RtcProvider options={rtcOptions}>
|
|
72
|
+
<YourAppComponents />
|
|
73
|
+
</RtcProvider>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### 2. Use Basic Hooks
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { useCallState, useCallActions } from 'vg-x07df';
|
|
82
|
+
|
|
83
|
+
function CallComponent() {
|
|
84
|
+
const callState = useCallState();
|
|
85
|
+
const { initiate, accept, decline } = useCallActions();
|
|
86
|
+
|
|
87
|
+
const handleInitiateCall = () => {
|
|
88
|
+
initiate(['user-id-1', 'user-id-2'], 'VIDEO');
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<div>
|
|
93
|
+
<p>Call Status: {callState.status}</p>
|
|
94
|
+
<button onClick={handleInitiateCall}>Start Video Call</button>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## RTC Provider Configuration
|
|
101
|
+
|
|
102
|
+
The `RtcProvider` accepts an `options` prop with the following configuration:
|
|
103
|
+
|
|
104
|
+
### Required Options
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
interface RtcOptions {
|
|
108
|
+
appId: string; // Your application identifier
|
|
109
|
+
signalHost: string; // Signal server URL
|
|
110
|
+
authProvider: () => string | null; // Token provider function
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Optional Configuration
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
interface RtcOptions {
|
|
118
|
+
// Logging configuration
|
|
119
|
+
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
120
|
+
enableDebug?: boolean;
|
|
121
|
+
log?: (level: string, message: string, meta?: any) => void;
|
|
122
|
+
|
|
123
|
+
// Auto-join configuration
|
|
124
|
+
autoJoin?: Partial<AutoJoinConfig>;
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Auto-Join Configuration
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
interface AutoJoinConfig {
|
|
132
|
+
enabled: boolean; // Enable auto-join (default: true)
|
|
133
|
+
retryOnFailure: boolean; // Retry on connection failure (default: true)
|
|
134
|
+
maxRetries: number; // Maximum retry attempts (default: 2)
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Complete Configuration Example
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
const rtcOptions = {
|
|
142
|
+
appId: 'callpad-app',
|
|
143
|
+
signalHost: 'https://signal.example.com',
|
|
144
|
+
authProvider: () => authService.getToken(),
|
|
145
|
+
|
|
146
|
+
// Logging
|
|
147
|
+
logLevel: 'debug',
|
|
148
|
+
enableDebug: true,
|
|
149
|
+
log: (level, message, meta) => {
|
|
150
|
+
console.log(`[${level.toUpperCase()}] ${message}`, meta);
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
// Auto-join
|
|
154
|
+
autoJoin: {
|
|
155
|
+
enabled: true,
|
|
156
|
+
retryOnFailure: true,
|
|
157
|
+
maxRetries: 3,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Core Concepts
|
|
163
|
+
|
|
164
|
+
### Session Status
|
|
165
|
+
|
|
166
|
+
The SDK tracks call sessions through various states:
|
|
167
|
+
|
|
168
|
+
- `IDLE` - No active call
|
|
169
|
+
- `CALLING` - Outgoing call initiated, waiting for response
|
|
170
|
+
- `RINGING` - Incoming call received
|
|
171
|
+
- `ACCEPTED` - Call accepted but not yet connected to media
|
|
172
|
+
- `AWAITING_JOIN_INFO` - Waiting for LiveKit connection details
|
|
173
|
+
- `READY_TO_JOIN` - Ready to connect to media session
|
|
174
|
+
- `CONNECTING` - Joining LiveKit room
|
|
175
|
+
- `ACTIVE` - Successfully connected to media session
|
|
176
|
+
- `ENDED` - Call completed
|
|
177
|
+
|
|
178
|
+
### Participants
|
|
179
|
+
|
|
180
|
+
Participants represent users in a call with the following properties:
|
|
181
|
+
|
|
182
|
+
```tsx
|
|
183
|
+
interface Participant {
|
|
184
|
+
id: string;
|
|
185
|
+
firstName?: string;
|
|
186
|
+
lastName?: string;
|
|
187
|
+
avatarUrl?: string;
|
|
188
|
+
role: "CALLER" | "CALLEE" | "HOST" | "MEMBER";
|
|
189
|
+
callState: "INVITED" | "RINGING" | "JOINED" | "LEFT";
|
|
190
|
+
audioEnabled: boolean;
|
|
191
|
+
videoEnabled: boolean;
|
|
192
|
+
isSpeaking: boolean;
|
|
193
|
+
connectionQuality?: "excellent" | "good" | "poor" | "lost" | "unknown";
|
|
194
|
+
invitedAt?: number;
|
|
195
|
+
joinedAt?: number;
|
|
196
|
+
leftAt?: number;
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Call Management
|
|
201
|
+
|
|
202
|
+
### Intercepting Incoming Calls
|
|
203
|
+
|
|
204
|
+
Use the `useCallState` hook to detect incoming calls:
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
import { useCallState, useCallActions } from 'vg-x07df';
|
|
208
|
+
|
|
209
|
+
function IncomingCallHandler() {
|
|
210
|
+
const { incomingCall, status } = useCallState();
|
|
211
|
+
const { accept, decline } = useCallActions();
|
|
212
|
+
|
|
213
|
+
if (status === 'RINGING' && incomingCall) {
|
|
214
|
+
return (
|
|
215
|
+
<div className="incoming-call-modal">
|
|
216
|
+
<h3>Incoming {incomingCall.type} call</h3>
|
|
217
|
+
<p>From: {incomingCall.caller.name}</p>
|
|
218
|
+
|
|
219
|
+
<button onClick={() => accept(incomingCall.callId)}>
|
|
220
|
+
Accept
|
|
221
|
+
</button>
|
|
222
|
+
<button onClick={() => decline(incomingCall.callId)}>
|
|
223
|
+
Decline
|
|
224
|
+
</button>
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Advanced Incoming Call Handling
|
|
234
|
+
|
|
235
|
+
Use event hooks for more sophisticated incoming call management:
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
import { useEvent, SdkEventType } from 'vg-x07df';
|
|
239
|
+
|
|
240
|
+
function AdvancedIncomingCallHandler() {
|
|
241
|
+
useEvent(SdkEventType.CALL_INCOMING, (event) => {
|
|
242
|
+
const { callId, caller, type } = event.payload;
|
|
243
|
+
|
|
244
|
+
// Show custom notification
|
|
245
|
+
showNotification({
|
|
246
|
+
title: `Incoming ${type} call`,
|
|
247
|
+
body: `${caller.firstName} ${caller.lastName} is calling`,
|
|
248
|
+
actions: [
|
|
249
|
+
{ action: 'accept', title: 'Accept' },
|
|
250
|
+
{ action: 'decline', title: 'Decline' }
|
|
251
|
+
]
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
return <div>Listening for incoming calls...</div>;
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Initiating Calls
|
|
260
|
+
|
|
261
|
+
Use the `useCallActions` hook to start calls:
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
function CallInitiator() {
|
|
265
|
+
const { initiate } = useCallActions();
|
|
266
|
+
const [participants, setParticipants] = useState(['']);
|
|
267
|
+
|
|
268
|
+
const handleVideoCall = async () => {
|
|
269
|
+
try {
|
|
270
|
+
const response = await initiate(participants, 'VIDEO');
|
|
271
|
+
console.log('Call initiated:', response);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.error('Failed to initiate call:', error);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const handleAudioCall = async () => {
|
|
278
|
+
try {
|
|
279
|
+
const response = await initiate(participants, 'AUDIO');
|
|
280
|
+
console.log('Call initiated:', response);
|
|
281
|
+
} catch (error) {
|
|
282
|
+
console.error('Failed to initiate call:', error);
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
<div>
|
|
288
|
+
<input
|
|
289
|
+
value={participants[0]}
|
|
290
|
+
onChange={(e) => setParticipants([e.target.value])}
|
|
291
|
+
placeholder="User ID to call"
|
|
292
|
+
/>
|
|
293
|
+
<button onClick={handleVideoCall}>Video Call</button>
|
|
294
|
+
<button onClick={handleAudioCall}>Audio Call</button>
|
|
295
|
+
</div>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Accepting and Declining Calls
|
|
301
|
+
|
|
302
|
+
```tsx
|
|
303
|
+
function CallResponseHandler() {
|
|
304
|
+
const { accept, decline } = useCallActions();
|
|
305
|
+
const { incomingCall } = useCallState();
|
|
306
|
+
|
|
307
|
+
const handleAccept = async () => {
|
|
308
|
+
if (incomingCall) {
|
|
309
|
+
try {
|
|
310
|
+
await accept(incomingCall.callId);
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.error('Failed to accept call:', error);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const handleDecline = async () => {
|
|
318
|
+
if (incomingCall) {
|
|
319
|
+
try {
|
|
320
|
+
await decline(incomingCall.callId);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
console.error('Failed to decline call:', error);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
return (
|
|
328
|
+
<div>
|
|
329
|
+
<button onClick={handleAccept}>Accept Call</button>
|
|
330
|
+
<button onClick={handleDecline}>Decline Call</button>
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Managing Call State
|
|
337
|
+
|
|
338
|
+
Monitor and respond to call state changes:
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
function CallStateManager() {
|
|
342
|
+
const callState = useCallState();
|
|
343
|
+
|
|
344
|
+
useEffect(() => {
|
|
345
|
+
switch (callState.status) {
|
|
346
|
+
case 'CALLING':
|
|
347
|
+
console.log('Outgoing call initiated');
|
|
348
|
+
break;
|
|
349
|
+
case 'RINGING':
|
|
350
|
+
console.log('Incoming call received');
|
|
351
|
+
break;
|
|
352
|
+
case 'ACTIVE':
|
|
353
|
+
console.log('Call is now active');
|
|
354
|
+
break;
|
|
355
|
+
case 'ENDED':
|
|
356
|
+
console.log('Call has ended');
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
}, [callState.status]);
|
|
360
|
+
|
|
361
|
+
return (
|
|
362
|
+
<div>
|
|
363
|
+
<h3>Call Information</h3>
|
|
364
|
+
<p>Status: {callState.status}</p>
|
|
365
|
+
<p>Mode: {callState.mode || 'None'}</p>
|
|
366
|
+
<p>Room: {callState.roomName || 'None'}</p>
|
|
367
|
+
{callState.id && <p>Call ID: {callState.id}</p>}
|
|
368
|
+
</div>
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Hooks Reference
|
|
374
|
+
|
|
375
|
+
### Call Management Hooks
|
|
376
|
+
|
|
377
|
+
#### `useCallState()`
|
|
378
|
+
|
|
379
|
+
Returns current call state information:
|
|
380
|
+
|
|
381
|
+
```tsx
|
|
382
|
+
interface CallState {
|
|
383
|
+
id: string | undefined;
|
|
384
|
+
status: SessionStatus;
|
|
385
|
+
mode: "AUDIO" | "VIDEO" | undefined;
|
|
386
|
+
roomName: string | undefined;
|
|
387
|
+
incomingCall: IncomingCallInfo | undefined;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const callState = useCallState();
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
#### `useCallActions()`
|
|
394
|
+
|
|
395
|
+
Provides call action functions:
|
|
396
|
+
|
|
397
|
+
```tsx
|
|
398
|
+
interface CallActions {
|
|
399
|
+
initiate: (participants: string[], type: "AUDIO" | "VIDEO") => Promise<CallResponse>;
|
|
400
|
+
accept: (callId: string) => Promise<CallActionResponse>;
|
|
401
|
+
decline: (callId: string) => Promise<CallActionResponse>;
|
|
402
|
+
end: (callId: string) => Promise<CallActionResponse>;
|
|
403
|
+
cancel: (callId: string) => Promise<CallActionResponse>;
|
|
404
|
+
join: () => Promise<void>;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const actions = useCallActions();
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
#### `useCallQuality()`
|
|
411
|
+
|
|
412
|
+
Monitor call quality metrics:
|
|
413
|
+
|
|
414
|
+
```tsx
|
|
415
|
+
const quality = useCallQuality();
|
|
416
|
+
// Returns quality information and metrics
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Media Control Hooks
|
|
420
|
+
|
|
421
|
+
#### `useMediaControls()`
|
|
422
|
+
|
|
423
|
+
Comprehensive media control interface:
|
|
424
|
+
|
|
425
|
+
```tsx
|
|
426
|
+
const {
|
|
427
|
+
// State
|
|
428
|
+
isVideoEnabled,
|
|
429
|
+
isAudioEnabled,
|
|
430
|
+
isCameraAvailable,
|
|
431
|
+
isMicrophoneAvailable,
|
|
432
|
+
isConnected,
|
|
433
|
+
isLoading,
|
|
434
|
+
errors,
|
|
435
|
+
devices,
|
|
436
|
+
|
|
437
|
+
// Actions
|
|
438
|
+
enableCamera,
|
|
439
|
+
disableCamera,
|
|
440
|
+
enableMicrophone,
|
|
441
|
+
disableMicrophone,
|
|
442
|
+
toggleCamera,
|
|
443
|
+
toggleMicrophone,
|
|
444
|
+
toggleAudio,
|
|
445
|
+
toggleVideo,
|
|
446
|
+
switchCamera,
|
|
447
|
+
switchMicrophone
|
|
448
|
+
} = useMediaControls();
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
#### `useDevices()`
|
|
452
|
+
|
|
453
|
+
Access and manage media devices:
|
|
454
|
+
|
|
455
|
+
```tsx
|
|
456
|
+
const devices = useDevices();
|
|
457
|
+
// Returns cameras, microphones, speakers, and permissions
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Event System Hooks
|
|
461
|
+
|
|
462
|
+
#### `useEvent(eventType, callback?, filter?)`
|
|
463
|
+
|
|
464
|
+
Subscribe to specific SDK events:
|
|
465
|
+
|
|
466
|
+
```tsx
|
|
467
|
+
// Listen for specific event
|
|
468
|
+
const callEvent = useEvent(SdkEventType.CALL_ACCEPTED);
|
|
469
|
+
|
|
470
|
+
// Listen with callback
|
|
471
|
+
useEvent(SdkEventType.MEDIA_ENABLED, (event) => {
|
|
472
|
+
console.log('Media enabled:', event.payload);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// Listen with filter
|
|
476
|
+
useEvent('call:*', null, (event) =>
|
|
477
|
+
event.payload.callId === 'specific-call'
|
|
478
|
+
);
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
#### `useCallEvents(callId?)`
|
|
482
|
+
|
|
483
|
+
Subscribe to call-specific events:
|
|
484
|
+
|
|
485
|
+
```tsx
|
|
486
|
+
const {
|
|
487
|
+
callAccepted,
|
|
488
|
+
callDeclined,
|
|
489
|
+
callEnded,
|
|
490
|
+
participantJoined,
|
|
491
|
+
participantLeft
|
|
492
|
+
} = useCallEvents(callId);
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
#### `useMediaEvents(participantId?)`
|
|
496
|
+
|
|
497
|
+
Subscribe to media events for a specific participant:
|
|
498
|
+
|
|
499
|
+
```tsx
|
|
500
|
+
const { mediaEnabled, mediaDisabled } = useMediaEvents(participantId);
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Participant Hooks
|
|
504
|
+
|
|
505
|
+
#### `useParticipants()`
|
|
506
|
+
|
|
507
|
+
Get all call participants:
|
|
508
|
+
|
|
509
|
+
```tsx
|
|
510
|
+
const participants = useParticipants();
|
|
511
|
+
// Returns array of Participant objects
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
#### `useParticipantStatus(participantId)`
|
|
515
|
+
|
|
516
|
+
Get status for a specific participant:
|
|
517
|
+
|
|
518
|
+
```tsx
|
|
519
|
+
const status = useParticipantStatus('user-123');
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Auto-Join Hooks
|
|
523
|
+
|
|
524
|
+
#### `useAutoJoin()`
|
|
525
|
+
|
|
526
|
+
Access auto-join configuration:
|
|
527
|
+
|
|
528
|
+
```tsx
|
|
529
|
+
const {
|
|
530
|
+
config,
|
|
531
|
+
isEnabled,
|
|
532
|
+
retryOnFailure,
|
|
533
|
+
maxRetries
|
|
534
|
+
} = useAutoJoin();
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### Error Handling Hooks
|
|
538
|
+
|
|
539
|
+
#### `useErrorRecovery()`
|
|
540
|
+
|
|
541
|
+
Access error recovery functionality:
|
|
542
|
+
|
|
543
|
+
```tsx
|
|
544
|
+
const recovery = useErrorRecovery();
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
#### `useErrors()`
|
|
548
|
+
|
|
549
|
+
Get current error state:
|
|
550
|
+
|
|
551
|
+
```tsx
|
|
552
|
+
const errors = useErrors();
|
|
553
|
+
const connectionErrors = useErrorsByCode('CONNECTION_');
|
|
554
|
+
const errorCount = useErrorCount();
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
## Event System
|
|
558
|
+
|
|
559
|
+
The SDK provides a comprehensive event system for real-time communication updates.
|
|
560
|
+
|
|
561
|
+
### Event Types
|
|
562
|
+
|
|
563
|
+
```tsx
|
|
564
|
+
enum SdkEventType {
|
|
565
|
+
// Call lifecycle
|
|
566
|
+
CALL_INITIATED = "call:initiated",
|
|
567
|
+
CALL_INCOMING = "call:incoming",
|
|
568
|
+
CALL_ACCEPTED = "call:accepted",
|
|
569
|
+
CALL_DECLINED = "call:declined",
|
|
570
|
+
CALL_ENDED = "call:ended",
|
|
571
|
+
CALL_CANCELED = "call:canceled",
|
|
572
|
+
CALL_TIMEOUT = "call:timeout",
|
|
573
|
+
|
|
574
|
+
// Participants
|
|
575
|
+
PARTICIPANT_JOINED = "participant:joined",
|
|
576
|
+
PARTICIPANT_LEFT = "participant:left",
|
|
577
|
+
|
|
578
|
+
// Media
|
|
579
|
+
MEDIA_ENABLED = "media:enabled",
|
|
580
|
+
MEDIA_DISABLED = "media:disabled",
|
|
581
|
+
|
|
582
|
+
// Connection
|
|
583
|
+
CONNECTION_ESTABLISHED = "connection:established",
|
|
584
|
+
CONNECTION_LOST = "connection:lost",
|
|
585
|
+
CONNECTION_QUALITY_CHANGED = "connection:quality-changed",
|
|
586
|
+
|
|
587
|
+
// Errors
|
|
588
|
+
ERROR_OCCURRED = "error:occurred",
|
|
589
|
+
}
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Event Usage Examples
|
|
593
|
+
|
|
594
|
+
```tsx
|
|
595
|
+
// Single event listener
|
|
596
|
+
useEvent(SdkEventType.CALL_ACCEPTED, (event) => {
|
|
597
|
+
console.log('Call accepted:', event.payload);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
// Pattern matching
|
|
601
|
+
useEvent('call:*', (event) => {
|
|
602
|
+
console.log('Any call event:', event.type, event.payload);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// Event with filter
|
|
606
|
+
useEvent(
|
|
607
|
+
SdkEventType.PARTICIPANT_JOINED,
|
|
608
|
+
(event) => {
|
|
609
|
+
console.log('New participant:', event.payload.participant);
|
|
610
|
+
},
|
|
611
|
+
(event) => event.payload.callId === currentCallId
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
// One-time event listener
|
|
615
|
+
useEventOnce(SdkEventType.CALL_ENDED, (event) => {
|
|
616
|
+
console.log('Call ended, cleaning up...');
|
|
617
|
+
});
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
### Direct Event Bus Access
|
|
621
|
+
|
|
622
|
+
```tsx
|
|
623
|
+
const eventBus = useEventBus();
|
|
624
|
+
|
|
625
|
+
// Emit custom event
|
|
626
|
+
eventBus.emit('custom:event', { data: 'test' });
|
|
627
|
+
|
|
628
|
+
// Get event history
|
|
629
|
+
const history = eventBus.getEventHistory();
|
|
630
|
+
|
|
631
|
+
// Get filtered events
|
|
632
|
+
const callEvents = eventBus.getEventsWhere(
|
|
633
|
+
(event) => event.type.startsWith('call:')
|
|
634
|
+
);
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
## Media Controls
|
|
638
|
+
|
|
639
|
+
### Basic Media Controls
|
|
640
|
+
|
|
641
|
+
```tsx
|
|
642
|
+
function MediaControlPanel() {
|
|
643
|
+
const {
|
|
644
|
+
isVideoEnabled,
|
|
645
|
+
isAudioEnabled,
|
|
646
|
+
toggleVideo,
|
|
647
|
+
toggleAudio,
|
|
648
|
+
devices
|
|
649
|
+
} = useMediaControls();
|
|
650
|
+
|
|
651
|
+
return (
|
|
652
|
+
<div>
|
|
653
|
+
<button
|
|
654
|
+
onClick={toggleVideo}
|
|
655
|
+
className={isVideoEnabled ? 'enabled' : 'disabled'}
|
|
656
|
+
>
|
|
657
|
+
{isVideoEnabled ? 'Disable Video' : 'Enable Video'}
|
|
658
|
+
</button>
|
|
659
|
+
|
|
660
|
+
<button
|
|
661
|
+
onClick={toggleAudio}
|
|
662
|
+
className={isAudioEnabled ? 'enabled' : 'disabled'}
|
|
663
|
+
>
|
|
664
|
+
{isAudioEnabled ? 'Disable Audio' : 'Enable Audio'}
|
|
665
|
+
</button>
|
|
666
|
+
</div>
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
### Device Management
|
|
672
|
+
|
|
673
|
+
```tsx
|
|
674
|
+
function DeviceSelector() {
|
|
675
|
+
const { devices, switchCamera, switchMicrophone } = useMediaControls();
|
|
676
|
+
|
|
677
|
+
return (
|
|
678
|
+
<div>
|
|
679
|
+
<select onChange={(e) => switchCamera(e.target.value)}>
|
|
680
|
+
<option value="">Select Camera</option>
|
|
681
|
+
{devices.cameras.map(camera => (
|
|
682
|
+
<option key={camera.deviceId} value={camera.deviceId}>
|
|
683
|
+
{camera.label}
|
|
684
|
+
</option>
|
|
685
|
+
))}
|
|
686
|
+
</select>
|
|
687
|
+
|
|
688
|
+
<select onChange={(e) => switchMicrophone(e.target.value)}>
|
|
689
|
+
<option value="">Select Microphone</option>
|
|
690
|
+
{devices.microphones.map(mic => (
|
|
691
|
+
<option key={mic.deviceId} value={mic.deviceId}>
|
|
692
|
+
{mic.label}
|
|
693
|
+
</option>
|
|
694
|
+
))}
|
|
695
|
+
</select>
|
|
696
|
+
</div>
|
|
697
|
+
);
|
|
698
|
+
}
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
### Advanced Media Controls
|
|
702
|
+
|
|
703
|
+
```tsx
|
|
704
|
+
function AdvancedMediaControls() {
|
|
705
|
+
const {
|
|
706
|
+
enableCamera,
|
|
707
|
+
disableCamera,
|
|
708
|
+
enableMicrophone,
|
|
709
|
+
disableMicrophone,
|
|
710
|
+
isLoading,
|
|
711
|
+
errors
|
|
712
|
+
} = useMediaControls();
|
|
713
|
+
|
|
714
|
+
const handleCameraToggle = async () => {
|
|
715
|
+
try {
|
|
716
|
+
if (isVideoEnabled) {
|
|
717
|
+
await disableCamera();
|
|
718
|
+
} else {
|
|
719
|
+
await enableCamera();
|
|
720
|
+
}
|
|
721
|
+
} catch (error) {
|
|
722
|
+
console.error('Camera control failed:', error);
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
if (errors.length > 0) {
|
|
727
|
+
return (
|
|
728
|
+
<div className="media-errors">
|
|
729
|
+
{errors.map(error => (
|
|
730
|
+
<p key={error.code}>{error.message}</p>
|
|
731
|
+
))}
|
|
732
|
+
</div>
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
return (
|
|
737
|
+
<div>
|
|
738
|
+
<button onClick={handleCameraToggle} disabled={isLoading}>
|
|
739
|
+
{isLoading ? 'Loading...' : 'Toggle Camera'}
|
|
740
|
+
</button>
|
|
741
|
+
</div>
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
## Participant Management
|
|
747
|
+
|
|
748
|
+
### Displaying Participants
|
|
749
|
+
|
|
750
|
+
```tsx
|
|
751
|
+
function ParticipantList() {
|
|
752
|
+
const participants = useParticipants();
|
|
753
|
+
|
|
754
|
+
return (
|
|
755
|
+
<div className="participants">
|
|
756
|
+
{participants.map(participant => (
|
|
757
|
+
<div key={participant.id} className="participant">
|
|
758
|
+
<img src={participant.avatarUrl} alt={participant.firstName} />
|
|
759
|
+
<span>{participant.firstName} {participant.lastName}</span>
|
|
760
|
+
<span className={`status ${participant.callState.toLowerCase()}`}>
|
|
761
|
+
{participant.callState}
|
|
762
|
+
</span>
|
|
763
|
+
<div className="media-status">
|
|
764
|
+
{participant.audioEnabled && <span>🎤</span>}
|
|
765
|
+
{participant.videoEnabled && <span>📹</span>}
|
|
766
|
+
{participant.isSpeaking && <span>🔊</span>}
|
|
767
|
+
</div>
|
|
768
|
+
</div>
|
|
769
|
+
))}
|
|
770
|
+
</div>
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
### Participant Status Monitoring
|
|
776
|
+
|
|
777
|
+
```tsx
|
|
778
|
+
function ParticipantMonitor({ participantId }) {
|
|
779
|
+
const status = useParticipantStatus(participantId);
|
|
780
|
+
const { mediaEnabled, mediaDisabled } = useMediaEvents(participantId);
|
|
781
|
+
|
|
782
|
+
useEffect(() => {
|
|
783
|
+
if (mediaEnabled) {
|
|
784
|
+
console.log(`${participantId} enabled ${mediaEnabled.payload.mediaType}`);
|
|
785
|
+
}
|
|
786
|
+
}, [mediaEnabled, participantId]);
|
|
787
|
+
|
|
788
|
+
useEffect(() => {
|
|
789
|
+
if (mediaDisabled) {
|
|
790
|
+
console.log(`${participantId} disabled ${mediaDisabled.payload.mediaType}`);
|
|
791
|
+
}
|
|
792
|
+
}, [mediaDisabled, participantId]);
|
|
793
|
+
|
|
794
|
+
return (
|
|
795
|
+
<div>
|
|
796
|
+
<h4>Participant: {participantId}</h4>
|
|
797
|
+
<p>Connection: {status?.connectionQuality}</p>
|
|
798
|
+
<p>Speaking: {status?.isSpeaking ? 'Yes' : 'No'}</p>
|
|
799
|
+
</div>
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
## Auto-Join Configuration
|
|
805
|
+
|
|
806
|
+
### Using Auto-Join
|
|
807
|
+
|
|
808
|
+
```tsx
|
|
809
|
+
function AutoJoinExample() {
|
|
810
|
+
const autoJoin = useAutoJoin();
|
|
811
|
+
const { shouldAutoJoin, reason } = useAutoJoinForCurrentUser();
|
|
812
|
+
|
|
813
|
+
return (
|
|
814
|
+
<div>
|
|
815
|
+
<h3>Auto-Join Status</h3>
|
|
816
|
+
<p>Enabled: {autoJoin.isEnabled ? 'Yes' : 'No'}</p>
|
|
817
|
+
<p>Max Retries: {autoJoin.maxRetries}</p>
|
|
818
|
+
<p>Should Auto-Join: {shouldAutoJoin ? 'Yes' : 'No'}</p>
|
|
819
|
+
<p>Reason: {reason}</p>
|
|
820
|
+
</div>
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
## Error Handling
|
|
826
|
+
|
|
827
|
+
### Error Recovery
|
|
828
|
+
|
|
829
|
+
```tsx
|
|
830
|
+
function ErrorRecoveryExample() {
|
|
831
|
+
const recovery = useErrorRecovery();
|
|
832
|
+
const errors = useErrors();
|
|
833
|
+
|
|
834
|
+
const handleRecovery = async () => {
|
|
835
|
+
try {
|
|
836
|
+
await recovery.attemptRecovery();
|
|
837
|
+
} catch (error) {
|
|
838
|
+
console.error('Recovery failed:', error);
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
if (errors.length > 0) {
|
|
843
|
+
return (
|
|
844
|
+
<div className="error-panel">
|
|
845
|
+
<h3>Errors Detected</h3>
|
|
846
|
+
{errors.map(error => (
|
|
847
|
+
<div key={error.code} className="error">
|
|
848
|
+
<strong>{error.code}</strong>: {error.message}
|
|
849
|
+
</div>
|
|
850
|
+
))}
|
|
851
|
+
<button onClick={handleRecovery}>Attempt Recovery</button>
|
|
852
|
+
</div>
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
return null;
|
|
857
|
+
}
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
### Custom Error Handling
|
|
861
|
+
|
|
862
|
+
```tsx
|
|
863
|
+
function CustomErrorHandler() {
|
|
864
|
+
useEvent(SdkEventType.ERROR_OCCURRED, (event) => {
|
|
865
|
+
const { code, message, source } = event.payload;
|
|
866
|
+
|
|
867
|
+
// Custom error handling logic
|
|
868
|
+
switch (code) {
|
|
869
|
+
case 'CONNECTION_LOST':
|
|
870
|
+
showReconnectingNotification();
|
|
871
|
+
break;
|
|
872
|
+
case 'MEDIA_PERMISSION_DENIED':
|
|
873
|
+
showPermissionRequestDialog();
|
|
874
|
+
break;
|
|
875
|
+
default:
|
|
876
|
+
showGenericErrorToast(message);
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
return <div>Error handler active</div>;
|
|
881
|
+
}
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
## TypeScript Support
|
|
885
|
+
|
|
886
|
+
The SDK is built with full TypeScript support. All hooks, components, and utilities are fully typed.
|
|
887
|
+
|
|
888
|
+
### Type Definitions
|
|
889
|
+
|
|
890
|
+
```tsx
|
|
891
|
+
import type {
|
|
892
|
+
// Core types
|
|
893
|
+
SessionStatus,
|
|
894
|
+
Participant,
|
|
895
|
+
IncomingCallInfo,
|
|
896
|
+
RtcError,
|
|
897
|
+
|
|
898
|
+
// Config types
|
|
899
|
+
RtcOptions,
|
|
900
|
+
AutoJoinConfig,
|
|
901
|
+
|
|
902
|
+
// Event types
|
|
903
|
+
SdkEvent,
|
|
904
|
+
SdkEventType,
|
|
905
|
+
CallAcceptedEvent,
|
|
906
|
+
|
|
907
|
+
// API types
|
|
908
|
+
InitiateCallParams,
|
|
909
|
+
CallResponse,
|
|
910
|
+
CallActionResponse,
|
|
911
|
+
} from 'vg-x07df';
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
### Custom Hook Example
|
|
915
|
+
|
|
916
|
+
```tsx
|
|
917
|
+
import { useCallState, useCallActions } from 'vg-x07df';
|
|
918
|
+
import type { SessionStatus } from 'vg-x07df';
|
|
919
|
+
|
|
920
|
+
function useCallManager() {
|
|
921
|
+
const callState = useCallState();
|
|
922
|
+
const actions = useCallActions();
|
|
923
|
+
|
|
924
|
+
const isCallActive = (): boolean => {
|
|
925
|
+
return callState.status === 'ACTIVE';
|
|
926
|
+
};
|
|
927
|
+
|
|
928
|
+
const canInitiateCall = (): boolean => {
|
|
929
|
+
return callState.status === 'IDLE';
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
return {
|
|
933
|
+
...callState,
|
|
934
|
+
...actions,
|
|
935
|
+
isCallActive,
|
|
936
|
+
canInitiateCall,
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
## Advanced Usage
|
|
942
|
+
|
|
943
|
+
### Direct SDK Access
|
|
944
|
+
|
|
945
|
+
Access the SDK instance directly for advanced operations:
|
|
946
|
+
|
|
947
|
+
```tsx
|
|
948
|
+
import { useSdk } from 'vg-x07df';
|
|
949
|
+
|
|
950
|
+
function AdvancedComponent() {
|
|
951
|
+
const sdk = useSdk();
|
|
952
|
+
|
|
953
|
+
const performAdvancedOperation = async () => {
|
|
954
|
+
// Direct access to SDK components
|
|
955
|
+
const authToken = sdk.auth.getCurrentToken();
|
|
956
|
+
await sdk.signal.customApiCall();
|
|
957
|
+
sdk.livekit.performAdvancedMediaOperation();
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
return <button onClick={performAdvancedOperation}>Advanced Op</button>;
|
|
961
|
+
}
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
### Custom Logging
|
|
965
|
+
|
|
966
|
+
```tsx
|
|
967
|
+
const rtcOptions = {
|
|
968
|
+
appId: 'my-app',
|
|
969
|
+
signalHost: 'https://signal.example.com',
|
|
970
|
+
authProvider: () => getToken(),
|
|
971
|
+
|
|
972
|
+
// Custom logging
|
|
973
|
+
log: (level, message, meta) => {
|
|
974
|
+
// Send to your logging service
|
|
975
|
+
analyticsService.log({
|
|
976
|
+
level,
|
|
977
|
+
message,
|
|
978
|
+
meta,
|
|
979
|
+
timestamp: Date.now(),
|
|
980
|
+
userId: getCurrentUserId(),
|
|
981
|
+
});
|
|
982
|
+
},
|
|
983
|
+
};
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
### State Management Integration
|
|
987
|
+
|
|
988
|
+
```tsx
|
|
989
|
+
// Redux integration example
|
|
990
|
+
function useCallStateSync() {
|
|
991
|
+
const callState = useCallState();
|
|
992
|
+
const dispatch = useDispatch();
|
|
993
|
+
|
|
994
|
+
useEffect(() => {
|
|
995
|
+
dispatch(updateCallState(callState));
|
|
996
|
+
}, [callState, dispatch]);
|
|
997
|
+
}
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
## Examples
|
|
1001
|
+
|
|
1002
|
+
### Complete Call Application
|
|
1003
|
+
|
|
1004
|
+
```tsx
|
|
1005
|
+
import React from 'react';
|
|
1006
|
+
import {
|
|
1007
|
+
RtcProvider,
|
|
1008
|
+
useCallState,
|
|
1009
|
+
useCallActions,
|
|
1010
|
+
useMediaControls,
|
|
1011
|
+
useParticipants,
|
|
1012
|
+
useEvent,
|
|
1013
|
+
SdkEventType
|
|
1014
|
+
} from 'vg-x07df';
|
|
1015
|
+
|
|
1016
|
+
// Main app with provider
|
|
1017
|
+
function App() {
|
|
1018
|
+
const rtcOptions = {
|
|
1019
|
+
appId: 'demo-app',
|
|
1020
|
+
signalHost: 'https://your-signal-server.com',
|
|
1021
|
+
authProvider: () => localStorage.getItem('token'),
|
|
1022
|
+
logLevel: 'info',
|
|
1023
|
+
autoJoin: { enabled: true, maxRetries: 2 }
|
|
1024
|
+
};
|
|
1025
|
+
|
|
1026
|
+
return (
|
|
1027
|
+
<RtcProvider options={rtcOptions}>
|
|
1028
|
+
<CallApp />
|
|
1029
|
+
</RtcProvider>
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// Call application component
|
|
1034
|
+
function CallApp() {
|
|
1035
|
+
const callState = useCallState();
|
|
1036
|
+
const { initiate, accept, decline, end } = useCallActions();
|
|
1037
|
+
const { toggleVideo, toggleAudio, isVideoEnabled, isAudioEnabled } = useMediaControls();
|
|
1038
|
+
const participants = useParticipants();
|
|
1039
|
+
|
|
1040
|
+
// Handle incoming calls
|
|
1041
|
+
useEvent(SdkEventType.CALL_INCOMING, (event) => {
|
|
1042
|
+
const { caller, type } = event.payload;
|
|
1043
|
+
const response = confirm(`Accept ${type} call from ${caller.firstName}?`);
|
|
1044
|
+
if (response) {
|
|
1045
|
+
accept(event.payload.callId);
|
|
1046
|
+
} else {
|
|
1047
|
+
decline(event.payload.callId);
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
const startVideoCall = () => {
|
|
1052
|
+
initiate(['user-123'], 'VIDEO');
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
const endCall = () => {
|
|
1056
|
+
if (callState.id) {
|
|
1057
|
+
end(callState.id);
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
return (
|
|
1062
|
+
<div>
|
|
1063
|
+
<h1>CallPad Demo</h1>
|
|
1064
|
+
|
|
1065
|
+
<div>Status: {callState.status}</div>
|
|
1066
|
+
|
|
1067
|
+
{callState.status === 'IDLE' && (
|
|
1068
|
+
<button onClick={startVideoCall}>Start Video Call</button>
|
|
1069
|
+
)}
|
|
1070
|
+
|
|
1071
|
+
{callState.status === 'ACTIVE' && (
|
|
1072
|
+
<div>
|
|
1073
|
+
<button onClick={toggleVideo}>
|
|
1074
|
+
{isVideoEnabled ? 'Disable Video' : 'Enable Video'}
|
|
1075
|
+
</button>
|
|
1076
|
+
<button onClick={toggleAudio}>
|
|
1077
|
+
{isAudioEnabled ? 'Mute' : 'Unmute'}
|
|
1078
|
+
</button>
|
|
1079
|
+
<button onClick={endCall}>End Call</button>
|
|
1080
|
+
|
|
1081
|
+
<div>
|
|
1082
|
+
<h3>Participants ({participants.length})</h3>
|
|
1083
|
+
{participants.map(p => (
|
|
1084
|
+
<div key={p.id}>
|
|
1085
|
+
{p.firstName} {p.lastName} - {p.callState}
|
|
1086
|
+
</div>
|
|
1087
|
+
))}
|
|
1088
|
+
</div>
|
|
1089
|
+
</div>
|
|
1090
|
+
)}
|
|
1091
|
+
</div>
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
export default App;
|
|
1096
|
+
```
|
|
1097
|
+
|
|
1098
|
+
### Notification Service Integration
|
|
1099
|
+
|
|
1100
|
+
```tsx
|
|
1101
|
+
function NotificationService() {
|
|
1102
|
+
useEvent(SdkEventType.CALL_INCOMING, (event) => {
|
|
1103
|
+
const { callId, caller, type } = event.payload;
|
|
1104
|
+
|
|
1105
|
+
if ('Notification' in window && Notification.permission === 'granted') {
|
|
1106
|
+
const notification = new Notification(
|
|
1107
|
+
`Incoming ${type} Call`,
|
|
1108
|
+
{
|
|
1109
|
+
body: `${caller.firstName} ${caller.lastName} is calling`,
|
|
1110
|
+
icon: caller.avatarUrl,
|
|
1111
|
+
actions: [
|
|
1112
|
+
{ action: 'accept', title: 'Accept' },
|
|
1113
|
+
{ action: 'decline', title: 'Decline' }
|
|
1114
|
+
]
|
|
1115
|
+
}
|
|
1116
|
+
);
|
|
1117
|
+
|
|
1118
|
+
notification.onclick = (event) => {
|
|
1119
|
+
if (event.action === 'accept') {
|
|
1120
|
+
acceptCall(callId);
|
|
1121
|
+
} else if (event.action === 'decline') {
|
|
1122
|
+
declineCall(callId);
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
});
|
|
1127
|
+
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
```
|
|
1131
|
+
|
|
1132
|
+
This comprehensive documentation covers all major aspects of the CallPad SDK, providing developers with the information needed to integrate audio/video calling functionality into their React applications.
|