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.
Files changed (140) hide show
  1. package/.azure-pipelines/publish-public.yml +37 -0
  2. package/.azure-pipelines/publish.yml +39 -0
  3. package/.changeset/README.md +8 -0
  4. package/.changeset/config.json +11 -0
  5. package/AUTO_JOIN_GUIDE.md +411 -0
  6. package/README.md +215 -0
  7. package/Screenshot 2025-09-24 at 14.34.48.png +0 -0
  8. package/Screenshot 2025-10-04 at 12.58.54.png +0 -0
  9. package/biome.json +48 -0
  10. package/examples/demo/.env.example +19 -0
  11. package/examples/demo/CHANGELOG.md +22 -0
  12. package/examples/demo/README.md +72 -0
  13. package/examples/demo/eslint.config.js +23 -0
  14. package/examples/demo/index.html +13 -0
  15. package/examples/demo/package.json +34 -0
  16. package/examples/demo/pnpm-lock.yaml +2098 -0
  17. package/examples/demo/pnpm-workspace.yaml +1 -0
  18. package/examples/demo/public/vite.svg +1 -0
  19. package/examples/demo/src/App.css +52 -0
  20. package/examples/demo/src/App.tsx +176 -0
  21. package/examples/demo/src/assets/react.svg +1 -0
  22. package/examples/demo/src/components/auth/LoginForm.css +144 -0
  23. package/examples/demo/src/components/auth/LoginForm.tsx +80 -0
  24. package/examples/demo/src/components/calling/AutoJoinSettings.tsx +213 -0
  25. package/examples/demo/src/components/calling/AutoJoinStatus.tsx +72 -0
  26. package/examples/demo/src/components/calling/CallInitiator.css +258 -0
  27. package/examples/demo/src/components/calling/CallInitiator.tsx +142 -0
  28. package/examples/demo/src/components/calling/CallNotifications.css +119 -0
  29. package/examples/demo/src/components/calling/CallNotifications.tsx +108 -0
  30. package/examples/demo/src/components/calling/IncomingCallModal.css +192 -0
  31. package/examples/demo/src/components/calling/IncomingCallModal.tsx +78 -0
  32. package/examples/demo/src/components/calling/MinimizedCall.css +156 -0
  33. package/examples/demo/src/components/calling/MinimizedCall.tsx +78 -0
  34. package/examples/demo/src/components/conference/ConferenceHeader.css +265 -0
  35. package/examples/demo/src/components/conference/ConferenceHeader.tsx +78 -0
  36. package/examples/demo/src/components/conference/EnhancedControlBar.css +356 -0
  37. package/examples/demo/src/components/conference/EnhancedControlBar.tsx +262 -0
  38. package/examples/demo/src/components/conference/PaginationControls.css +67 -0
  39. package/examples/demo/src/components/conference/PaginationControls.tsx +64 -0
  40. package/examples/demo/src/components/conference/ParticipantGrid.css +153 -0
  41. package/examples/demo/src/components/conference/ParticipantGrid.tsx +87 -0
  42. package/examples/demo/src/components/conference/ParticipantTile.css +210 -0
  43. package/examples/demo/src/components/conference/ParticipantTile.tsx +114 -0
  44. package/examples/demo/src/components/conference/VideoConference.css +214 -0
  45. package/examples/demo/src/components/conference/VideoConference.tsx +93 -0
  46. package/examples/demo/src/contexts/AuthContext.tsx +105 -0
  47. package/examples/demo/src/hooks/useAuth.ts +5 -0
  48. package/examples/demo/src/hooks/useCallTimer.ts +42 -0
  49. package/examples/demo/src/index.css +68 -0
  50. package/examples/demo/src/main.tsx +10 -0
  51. package/examples/demo/src/services/auth.service.ts +153 -0
  52. package/examples/demo/src/types/auth.types.ts +31 -0
  53. package/examples/demo/tsconfig.app.json +28 -0
  54. package/examples/demo/tsconfig.json +7 -0
  55. package/examples/demo/tsconfig.node.json +26 -0
  56. package/examples/demo/vite.config.ts +15 -0
  57. package/images/callpad-without-ai.png +0 -0
  58. package/package.json +28 -0
  59. package/packages/sdk/CHANGELOG.md +33 -0
  60. package/packages/sdk/LICENSE +21 -0
  61. package/packages/sdk/README.md +97 -0
  62. package/packages/sdk/documentation.md +1132 -0
  63. package/packages/sdk/openapi-ts.config.ts +7 -0
  64. package/packages/sdk/package.json +88 -0
  65. package/packages/sdk/src/core/auth.manager.ts +52 -0
  66. package/packages/sdk/src/core/events/event-bus.ts +301 -0
  67. package/packages/sdk/src/core/events/index.ts +8 -0
  68. package/packages/sdk/src/core/events/types.ts +165 -0
  69. package/packages/sdk/src/core/index.ts +3 -0
  70. package/packages/sdk/src/core/signal/api.config.ts +49 -0
  71. package/packages/sdk/src/core/signal/index.ts +16 -0
  72. package/packages/sdk/src/core/signal/signal.client.ts +101 -0
  73. package/packages/sdk/src/core/signal/types.ts +110 -0
  74. package/packages/sdk/src/core/socketio/handlers/base.handler.ts +212 -0
  75. package/packages/sdk/src/core/socketio/handlers/call-accepted.handler.ts +34 -0
  76. package/packages/sdk/src/core/socketio/handlers/call-canceled.handler.ts +34 -0
  77. package/packages/sdk/src/core/socketio/handlers/call-declined.handler.ts +29 -0
  78. package/packages/sdk/src/core/socketio/handlers/call-ended.handler.ts +40 -0
  79. package/packages/sdk/src/core/socketio/handlers/call-incoming.handler.ts +72 -0
  80. package/packages/sdk/src/core/socketio/handlers/call-join-info.handler.ts +181 -0
  81. package/packages/sdk/src/core/socketio/handlers/call-participant-joined.handler.ts +42 -0
  82. package/packages/sdk/src/core/socketio/handlers/call-participant-joining.handler.ts +42 -0
  83. package/packages/sdk/src/core/socketio/handlers/call-timeout.handler.ts +31 -0
  84. package/packages/sdk/src/core/socketio/handlers/handler.registry.ts +62 -0
  85. package/packages/sdk/src/core/socketio/handlers/index.ts +21 -0
  86. package/packages/sdk/src/core/socketio/handlers/participant-left.handler.ts +37 -0
  87. package/packages/sdk/src/core/socketio/handlers/schema.ts +130 -0
  88. package/packages/sdk/src/core/socketio/index.ts +5 -0
  89. package/packages/sdk/src/core/socketio/socket.manager.ts +187 -0
  90. package/packages/sdk/src/core/socketio/types.ts +14 -0
  91. package/packages/sdk/src/core/types.ts +23 -0
  92. package/packages/sdk/src/generated/api/core/ApiError.ts +21 -0
  93. package/packages/sdk/src/generated/api/core/ApiRequestOptions.ts +13 -0
  94. package/packages/sdk/src/generated/api/core/ApiResult.ts +7 -0
  95. package/packages/sdk/src/generated/api/core/CancelablePromise.ts +126 -0
  96. package/packages/sdk/src/generated/api/core/OpenAPI.ts +55 -0
  97. package/packages/sdk/src/generated/api/core/request.ts +339 -0
  98. package/packages/sdk/src/generated/api/index.ts +5 -0
  99. package/packages/sdk/src/generated/api/models.ts +219 -0
  100. package/packages/sdk/src/generated/api/services.ts +225 -0
  101. package/packages/sdk/src/hooks/index.ts +21 -0
  102. package/packages/sdk/src/hooks/useAutoJoin.ts +66 -0
  103. package/packages/sdk/src/hooks/useCallActions.ts +28 -0
  104. package/packages/sdk/src/hooks/useCallQuality.ts +416 -0
  105. package/packages/sdk/src/hooks/useCallState.ts +23 -0
  106. package/packages/sdk/src/hooks/useConnection.ts +15 -0
  107. package/packages/sdk/src/hooks/useDevices.ts +296 -0
  108. package/packages/sdk/src/hooks/useErrorRecovery.ts +299 -0
  109. package/packages/sdk/src/hooks/useErrors.ts +84 -0
  110. package/packages/sdk/src/hooks/useEvent.ts +188 -0
  111. package/packages/sdk/src/hooks/useMediaControls.ts +215 -0
  112. package/packages/sdk/src/hooks/useParticipantStatus.ts +318 -0
  113. package/packages/sdk/src/hooks/useParticipants.ts +111 -0
  114. package/packages/sdk/src/index.ts +66 -0
  115. package/packages/sdk/src/livekit/constants.ts +76 -0
  116. package/packages/sdk/src/livekit/device.manager.ts +172 -0
  117. package/packages/sdk/src/livekit/error-classifier.ts +155 -0
  118. package/packages/sdk/src/livekit/events/eventBridge.ts +371 -0
  119. package/packages/sdk/src/livekit/events/trackRegistry.ts +114 -0
  120. package/packages/sdk/src/livekit/index.ts +49 -0
  121. package/packages/sdk/src/livekit/livekit.service.ts +110 -0
  122. package/packages/sdk/src/livekit/media.controls.ts +315 -0
  123. package/packages/sdk/src/livekit/room.manager.ts +79 -0
  124. package/packages/sdk/src/livekit/track.utils.ts +230 -0
  125. package/packages/sdk/src/livekit/types.ts +135 -0
  126. package/packages/sdk/src/provider/RtcProvider.tsx +78 -0
  127. package/packages/sdk/src/services/call-actions.ts +260 -0
  128. package/packages/sdk/src/services/error-recovery.ts +461 -0
  129. package/packages/sdk/src/services/index.ts +2 -0
  130. package/packages/sdk/src/services/sdk-builder.ts +104 -0
  131. package/packages/sdk/src/state/errors.ts +163 -0
  132. package/packages/sdk/src/state/selectors.ts +28 -0
  133. package/packages/sdk/src/state/store.ts +36 -0
  134. package/packages/sdk/src/state/types.ts +151 -0
  135. package/packages/sdk/src/utils/logger.ts +183 -0
  136. package/packages/sdk/tsconfig.json +49 -0
  137. package/packages/sdk/tsup.config.ts +51 -0
  138. package/pnpm-workspace.yaml +4 -0
  139. package/tsconfig.base.json +19 -0
  140. package/turbo.json +34 -0
@@ -0,0 +1,416 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import { useSdk } from "../provider/RtcProvider";
3
+ import { useRtcStore } from "../state/store";
4
+ import { createLogger } from "../utils/logger";
5
+
6
+ const logger = createLogger("hooks:call-quality");
7
+
8
+ /**
9
+ * Call quality metrics interface following spec requirements
10
+ */
11
+ export interface CallQuality {
12
+ overall: "excellent" | "good" | "poor" | "failed";
13
+ metrics: {
14
+ latency: number; // ms
15
+ packetLoss: number; // percentage
16
+ bandwidth: {
17
+ upload: number; // kbps
18
+ download: number; // kbps
19
+ };
20
+ resolution?: {
21
+ width: number;
22
+ height: number;
23
+ };
24
+ };
25
+ timestamp: number;
26
+ }
27
+
28
+ /**
29
+ * Hook for monitoring call quality metrics
30
+ */
31
+ export function useCallQuality(): {
32
+ quality: CallQuality | null;
33
+ history: CallQuality[];
34
+ } {
35
+ const sdk = useSdk();
36
+ const isConnected = useRtcStore((state) => state.connection.connected);
37
+ const [quality, setQuality] = useState<CallQuality | null>(null);
38
+ const [history, setHistory] = useState<CallQuality[]>([]);
39
+ const intervalRef = useRef<number | null>(null);
40
+
41
+ useEffect(() => {
42
+ if (!isConnected || !sdk.livekit?.room) {
43
+ // Clear quality when disconnected
44
+ setQuality(null);
45
+ if (intervalRef.current) {
46
+ clearInterval(intervalRef.current);
47
+ }
48
+ intervalRef.current = null;
49
+ return;
50
+ }
51
+
52
+ // Start quality monitoring
53
+ const startMonitoring = async () => {
54
+ try {
55
+ await collectQualityMetrics();
56
+
57
+ // Set up periodic collection every 5 seconds
58
+ intervalRef.current = setInterval(collectQualityMetrics, 5000);
59
+ } catch (error) {
60
+ logger.error("Failed to start quality monitoring", { error });
61
+ }
62
+ };
63
+
64
+ const collectQualityMetrics = async () => {
65
+ try {
66
+ const newQuality = await getCallQualityMetrics();
67
+ if (newQuality) {
68
+ setQuality(newQuality);
69
+ setHistory((prev) => {
70
+ const updated = [...prev, newQuality];
71
+ // Keep only last 50 entries (about 4 minutes of history)
72
+ return updated.slice(-50);
73
+ });
74
+ }
75
+ } catch (error) {
76
+ logger.error("Failed to collect quality metrics", { error });
77
+ }
78
+ };
79
+
80
+ startMonitoring();
81
+
82
+ return () => {
83
+ if (intervalRef.current) {
84
+ clearInterval(intervalRef.current);
85
+ intervalRef.current = null;
86
+ }
87
+ };
88
+ }, [isConnected, sdk.livekit]);
89
+
90
+ const getCallQualityMetrics = async (): Promise<CallQuality | null> => {
91
+ const room = sdk.livekit?.room;
92
+ if (!room || !room.localParticipant) {
93
+ return null;
94
+ }
95
+
96
+ try {
97
+ // Get WebRTC stats
98
+ const stats = await getWebRTCStats(room);
99
+ if (!stats) {
100
+ return null;
101
+ }
102
+
103
+ // Calculate overall quality based on metrics
104
+ const overall = calculateOverallQuality(stats);
105
+
106
+ return {
107
+ overall,
108
+ metrics: {
109
+ latency: stats.latency,
110
+ packetLoss: stats.packetLoss,
111
+ bandwidth: {
112
+ upload: stats.bandwidth.upload,
113
+ download: stats.bandwidth.download,
114
+ },
115
+ ...(stats.resolution ? { resolution: stats.resolution } : {}),
116
+ },
117
+ timestamp: Date.now(),
118
+ };
119
+ } catch (error) {
120
+ logger.error("Error getting quality metrics", { error });
121
+ return null;
122
+ }
123
+ };
124
+
125
+ return { quality, history };
126
+ }
127
+
128
+ /**
129
+ * Hook for monitoring call quality with custom intervals
130
+ */
131
+ export function useCallQualityWithConfig(
132
+ intervalMs = 5000,
133
+ maxHistorySize = 50
134
+ ): {
135
+ quality: CallQuality | null;
136
+ history: CallQuality[];
137
+ } {
138
+ const sdk = useSdk();
139
+ const isConnected = useRtcStore((state) => state.connection.connected);
140
+ const [quality, setQuality] = useState<CallQuality | null>(null);
141
+ const [history, setHistory] = useState<CallQuality[]>([]);
142
+ const intervalRef = useRef<number | null>(null);
143
+
144
+ useEffect(() => {
145
+ if (!isConnected || !sdk.livekit?.room) {
146
+ setQuality(null);
147
+ if (intervalRef.current) {
148
+ clearInterval(intervalRef.current);
149
+ }
150
+ intervalRef.current = null;
151
+ return;
152
+ }
153
+
154
+ const collectMetrics = async () => {
155
+ const room = sdk.livekit?.room;
156
+ if (!room) return;
157
+
158
+ try {
159
+ const stats = await getWebRTCStats(room);
160
+ if (!stats) return;
161
+
162
+ const newQuality: CallQuality = {
163
+ overall: calculateOverallQuality(stats),
164
+ metrics: {
165
+ latency: stats.latency,
166
+ packetLoss: stats.packetLoss,
167
+ bandwidth: {
168
+ upload: stats.bandwidth.upload,
169
+ download: stats.bandwidth.download,
170
+ },
171
+ ...(stats.resolution ? { resolution: stats.resolution } : {}),
172
+ },
173
+ timestamp: Date.now(),
174
+ };
175
+
176
+ setQuality(newQuality);
177
+ setHistory((prev) => {
178
+ const updated = [...prev, newQuality];
179
+ return updated.slice(-maxHistorySize);
180
+ });
181
+ } catch (error) {
182
+ logger.error("Failed to collect quality metrics", { error });
183
+ }
184
+ };
185
+
186
+ // Initial collection
187
+ collectMetrics();
188
+
189
+ // Set up interval
190
+ intervalRef.current = setInterval(collectMetrics, intervalMs);
191
+
192
+ return () => {
193
+ if (intervalRef.current) {
194
+ clearInterval(intervalRef.current);
195
+ intervalRef.current = null;
196
+ }
197
+ };
198
+ }, [isConnected, sdk.livekit, intervalMs, maxHistorySize]);
199
+
200
+ return { quality, history };
201
+ }
202
+
203
+ /**
204
+ * Hook for getting quality metrics for a specific participant
205
+ */
206
+ export function useParticipantQuality(
207
+ participantId?: string
208
+ ): CallQuality | null {
209
+ const sdk = useSdk();
210
+ const isConnected = useRtcStore((state) => state.connection.connected);
211
+ const [quality, setQuality] = useState<CallQuality | null>(null);
212
+ const intervalRef = useRef<number | null>(null);
213
+
214
+ useEffect(() => {
215
+ if (!isConnected || !sdk.livekit?.room) {
216
+ setQuality(null);
217
+ if (intervalRef.current) {
218
+ clearInterval(intervalRef.current);
219
+ }
220
+ intervalRef.current = null;
221
+ return;
222
+ }
223
+
224
+ const room = sdk.livekit.room;
225
+ const participant = participantId
226
+ ? room.remoteParticipants.get(participantId) || room.localParticipant
227
+ : room.localParticipant;
228
+
229
+ if (!participant) {
230
+ setQuality(null);
231
+ return;
232
+ }
233
+
234
+ const collectParticipantMetrics = async () => {
235
+ try {
236
+ const stats = await getParticipantStats(room, participant);
237
+ if (stats) {
238
+ const newQuality: CallQuality = {
239
+ overall: calculateOverallQuality(stats),
240
+ metrics: {
241
+ latency: stats.latency,
242
+ packetLoss: stats.packetLoss,
243
+ bandwidth: {
244
+ upload: stats.bandwidth.upload,
245
+ download: stats.bandwidth.download,
246
+ },
247
+ ...(stats.resolution ? { resolution: stats.resolution } : {}),
248
+ },
249
+ timestamp: Date.now(),
250
+ };
251
+ setQuality(newQuality);
252
+ }
253
+ } catch (error) {
254
+ logger.error("Failed to collect participant quality metrics", {
255
+ error,
256
+ participantId,
257
+ });
258
+ }
259
+ };
260
+
261
+ // Initial collection
262
+ collectParticipantMetrics();
263
+
264
+ // Set up interval
265
+ intervalRef.current = setInterval(collectParticipantMetrics, 5000);
266
+
267
+ return () => {
268
+ if (intervalRef.current) {
269
+ clearInterval(intervalRef.current);
270
+ intervalRef.current = null;
271
+ }
272
+ };
273
+ }, [isConnected, sdk.livekit, participantId]);
274
+
275
+ return quality;
276
+ }
277
+
278
+ // Helper functions
279
+ interface WebRTCStats {
280
+ latency: number;
281
+ packetLoss: number;
282
+ bandwidth: {
283
+ upload: number;
284
+ download: number;
285
+ };
286
+ resolution?: {
287
+ width: number;
288
+ height: number;
289
+ };
290
+ }
291
+
292
+ async function getWebRTCStats(room: any): Promise<WebRTCStats | null> {
293
+ try {
294
+ // Get the underlying WebRTC peer connection
295
+ const pc = room.engine?.publisher?.pc || room.engine?.subscriber?.pc;
296
+ if (!pc) {
297
+ return null;
298
+ }
299
+
300
+ const stats = await pc.getStats();
301
+ const statsArray = Array.from(stats.values());
302
+
303
+ // Extract relevant metrics
304
+ let latency = 0;
305
+ let packetLoss = 0;
306
+ let uploadBandwidth = 0;
307
+ let downloadBandwidth = 0;
308
+ let resolution: { width: number; height: number } | undefined;
309
+
310
+ for (const stat of statsArray) {
311
+ const s = stat as any;
312
+ // RTT (Round Trip Time) for latency
313
+ if (s.type === "candidate-pair" && s.state === "succeeded") {
314
+ latency = s.currentRoundTripTime ? s.currentRoundTripTime * 1000 : 0;
315
+ }
316
+
317
+ // Packet loss from outbound RTP
318
+ if (s.type === "outbound-rtp" && s.mediaType === "audio") {
319
+ if (s.packetsLost && s.packetsSent) {
320
+ packetLoss = (s.packetsLost / s.packetsSent) * 100;
321
+ }
322
+
323
+ // Upload bandwidth (estimate from bytes sent)
324
+ if (s.bytesSent && s.timestamp) {
325
+ uploadBandwidth = (s.bytesSent * 8) / 1000; // Convert to kbps
326
+ }
327
+ }
328
+
329
+ // Download bandwidth from inbound RTP
330
+ if (s.type === "inbound-rtp" && s.mediaType === "audio") {
331
+ if (s.bytesReceived && s.timestamp) {
332
+ downloadBandwidth = (s.bytesReceived * 8) / 1000; // Convert to kbps
333
+ }
334
+ }
335
+
336
+ // Video resolution from outbound video
337
+ if (s.type === "outbound-rtp" && s.mediaType === "video") {
338
+ if (s.frameWidth && s.frameHeight) {
339
+ resolution = {
340
+ width: s.frameWidth,
341
+ height: s.frameHeight,
342
+ };
343
+ }
344
+ }
345
+ }
346
+
347
+ const result: WebRTCStats = {
348
+ latency: Math.round(latency),
349
+ packetLoss: Math.round(packetLoss * 100) / 100, // Round to 2 decimal places
350
+ bandwidth: {
351
+ upload: Math.round(uploadBandwidth),
352
+ download: Math.round(downloadBandwidth),
353
+ },
354
+ };
355
+
356
+ if (resolution) {
357
+ result.resolution = resolution;
358
+ }
359
+
360
+ return result;
361
+ } catch (error) {
362
+ logger.error("Error getting WebRTC stats", { error });
363
+ return null;
364
+ }
365
+ }
366
+
367
+ async function getParticipantStats(
368
+ room: any,
369
+ participant: any
370
+ ): Promise<WebRTCStats | null> {
371
+ // For now, return the same stats as the room
372
+ // In a more sophisticated implementation, this could get participant-specific stats
373
+ return getWebRTCStats(room);
374
+ }
375
+
376
+ function calculateOverallQuality(stats: WebRTCStats): CallQuality["overall"] {
377
+ let score = 100;
378
+
379
+ // Deduct points for high latency
380
+ if (stats.latency > 300) {
381
+ score -= 40; // Very high latency
382
+ } else if (stats.latency > 150) {
383
+ score -= 20; // High latency
384
+ } else if (stats.latency > 100) {
385
+ score -= 10; // Moderate latency
386
+ }
387
+
388
+ // Deduct points for packet loss
389
+ if (stats.packetLoss > 5) {
390
+ score -= 30; // High packet loss
391
+ } else if (stats.packetLoss > 2) {
392
+ score -= 15; // Moderate packet loss
393
+ } else if (stats.packetLoss > 1) {
394
+ score -= 5; // Low packet loss
395
+ }
396
+
397
+ // Deduct points for low bandwidth
398
+ const totalBandwidth = stats.bandwidth.upload + stats.bandwidth.download;
399
+ if (totalBandwidth < 50) {
400
+ score -= 25; // Very low bandwidth
401
+ } else if (totalBandwidth < 100) {
402
+ score -= 10; // Low bandwidth
403
+ }
404
+
405
+ // Determine overall quality
406
+ if (score >= 80) {
407
+ return "excellent";
408
+ }
409
+ if (score >= 60) {
410
+ return "good";
411
+ }
412
+ if (score >= 30) {
413
+ return "poor";
414
+ }
415
+ return "failed";
416
+ }
@@ -0,0 +1,23 @@
1
+ import { useRtcStore } from "../state/store";
2
+ import type { IncomingCallInfo, SessionStatus } from "../state/types";
3
+
4
+ export interface CallState {
5
+ id: string | undefined;
6
+ status: SessionStatus;
7
+ mode: "AUDIO" | "VIDEO" | undefined;
8
+ roomName: string | undefined;
9
+ incomingCall: IncomingCallInfo | undefined;
10
+ }
11
+
12
+ export function useCallState(): CallState {
13
+ const session = useRtcStore((state) => state.session);
14
+ const incomingCall = useRtcStore((state) => state.incomingCall);
15
+
16
+ return {
17
+ id: session.id,
18
+ status: session.status,
19
+ mode: session.mode,
20
+ roomName: session.livekitInfo?.roomName,
21
+ incomingCall,
22
+ };
23
+ }
@@ -0,0 +1,15 @@
1
+ import { useRtcStore } from "../state/store";
2
+
3
+ export interface ConnectionState {
4
+ connected: boolean;
5
+ reconnecting: boolean;
6
+ quality?: "excellent" | "good" | "poor" | "lost";
7
+ }
8
+
9
+ export function useConnection(): ConnectionState {
10
+ return useRtcStore((state) => state.connection);
11
+ }
12
+
13
+ export function useIsConnected(): boolean {
14
+ return useRtcStore((state) => state.connection.connected);
15
+ }