@xipkg/calls 0.0.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/README.md ADDED
@@ -0,0 +1 @@
1
+ Основной компонент приложения
@@ -0,0 +1,5 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ declare const Call: () => react_jsx_runtime.JSX.Element;
4
+
5
+ export { Call };
package/dist/index.mjs ADDED
@@ -0,0 +1,874 @@
1
+ import React, { useEffect, useRef, useCallback, useState, useMemo } from 'react';
2
+ import { useCallsNavigation, useRoom, useCalls } from '@xipkg/calls-providers';
3
+ import { useInitUserDevices, useVideoSecurity, useParticipantJoinSync, useVideoBlur, usePersistentUserChoices, useResolveInitiallyDefaultDeviceId, useCannotUseDevice } from '@xipkg/calls-hooks';
4
+ import { useCallStore, useFocusModeStore, useFeaturesStore, usePermissionsStore, openPermissionsDialog } from '@xipkg/calls-store';
5
+ import { ScrollArea } from '@xipkg/scrollarea';
6
+ import { Button } from '@xipkg/button';
7
+ import { WhiteBoard, ArrowLeft, InfoCircle, Settings, Conference, SoundTwo, Microphone } from '@xipkg/icons';
8
+ import { Tooltip, TooltipTrigger, TooltipContent } from '@xipkg/tooltip';
9
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
+ import { Avatar, AvatarImage, AvatarFallback } from '@xipkg/avatar';
11
+ import { Track, createLocalVideoTrack, createLocalAudioTrack, facingModeFromLocalTrack } from 'livekit-client';
12
+ import { CallsOnboarding, UpBar, VideoGrid, PermissionsDialog, DevicesBar, ScreenShareButton, WhiteBoardButton, DisconnectButton, NoiseCancellationSettings, SecureVideo } from '@xipkg/calls-ui';
13
+ import { isSafari } from '@xipkg/calls-utils';
14
+ import { Switch } from '@xipkg/switcher';
15
+ import { Label } from '@xipkg/label';
16
+ import { Alert, AlertIcon, AlertContainer, AlertDescription } from '@xipkg/alert';
17
+ import { computeMenuPosition, wasClickOutside } from '@livekit/components-core';
18
+ import { Select, SelectTrigger, SelectValue, SelectContent, SelectGroup, SelectItem } from '@xipkg/select';
19
+ import { useLocalParticipant, usePreviewTracks, usePersistentUserChoices as usePersistentUserChoices$1, useTrackToggle, useMediaDeviceSelect } from '@livekit/components-react';
20
+ import { supportsBackgroundProcessors } from '@livekit/track-processors';
21
+ import { Chat, useChatStore, ChatButton } from '@xipkg/calls-chat';
22
+ import { useHandFocus, RaiseHandButton } from '@xipkg/calls-risehand';
23
+ import { cn } from '@xipkg/utils';
24
+ import '@xipkg/calls-ui/video-security.css';
25
+
26
+ // src/ui/Call.tsx
27
+ var Header = () => {
28
+ const navigation = useCallsNavigation();
29
+ const callId = navigation.getCallId();
30
+ const { room } = useCalls();
31
+ const { data: classroom } = room.useGetClassroom(Number(callId));
32
+ return /* @__PURE__ */ jsxs("div", { className: "mb-4 flex flex-col items-start gap-2 sm:flex-row sm:items-center", children: [
33
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-row items-center gap-2", children: [
34
+ /* @__PURE__ */ jsxs(Tooltip, { delayDuration: 1e3, children: [
35
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
36
+ Button,
37
+ {
38
+ onClick: () => {
39
+ if (callId) {
40
+ navigation.navigateToClassroom(callId);
41
+ }
42
+ },
43
+ type: "button",
44
+ variant: "none",
45
+ className: "flex size-[40px] min-h-[40xp] min-w-[40px] items-center justify-center rounded-[12px] p-0",
46
+ children: /* @__PURE__ */ jsx(ArrowLeft, { className: "fill-gray-100" })
47
+ }
48
+ ) }),
49
+ /* @__PURE__ */ jsx(TooltipContent, { side: "bottom", align: "start", children: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F \u0432 \u043A\u0430\u0431\u0438\u043D\u0435\u0442" })
50
+ ] }),
51
+ /* @__PURE__ */ jsx("h1", { className: "text-xl-base font-semibold text-gray-100", children: "\u041F\u0440\u0438\u0441\u043E\u0435\u0434\u0438\u043D\u0438\u0442\u044C\u0441\u044F \u043A\xA0\u0437\u0430\u043D\u044F\u0442\u0438\u044E" })
52
+ ] }),
53
+ /* @__PURE__ */ jsx("p", { className: "text-s-base text-gray-60 pt-0 pl-12 align-baseline sm:pt-2 sm:pl-0", children: classroom?.name })
54
+ ] });
55
+ };
56
+ var Controls = ({ audioTrack, videoTrack }) => {
57
+ const {
58
+ userChoices: { audioEnabled, videoEnabled },
59
+ saveAudioInputEnabled,
60
+ saveVideoInputEnabled
61
+ } = usePersistentUserChoices();
62
+ const handleAudioChange = useCallback(
63
+ async (enabled) => {
64
+ saveAudioInputEnabled(enabled);
65
+ if (audioTrack) {
66
+ if (enabled) {
67
+ console.log("Controls: unmuting audio track");
68
+ await audioTrack.unmute();
69
+ } else {
70
+ console.log("Controls: muting audio track");
71
+ await audioTrack.mute();
72
+ }
73
+ console.log("Controls: audio track state after change", { muted: audioTrack.isMuted });
74
+ } else {
75
+ console.log("Controls: no audio track available");
76
+ }
77
+ },
78
+ [audioTrack, saveAudioInputEnabled]
79
+ );
80
+ const handleVideoChange = useCallback(
81
+ async (enabled) => {
82
+ saveVideoInputEnabled(enabled);
83
+ if (videoTrack) {
84
+ if (enabled) {
85
+ console.log("Controls: unmuting video track");
86
+ await videoTrack.unmute();
87
+ } else {
88
+ console.log("Controls: muting video track");
89
+ await videoTrack.mute();
90
+ }
91
+ console.log("Controls: video track state after change", { muted: videoTrack.isMuted });
92
+ } else {
93
+ console.log("Controls: no video track available");
94
+ }
95
+ },
96
+ [videoTrack, saveVideoInputEnabled]
97
+ );
98
+ const microTrackToggle = useMemo(
99
+ () => ({
100
+ showIcon: true,
101
+ source: Track.Source.Microphone,
102
+ onChange: handleAudioChange
103
+ }),
104
+ [handleAudioChange]
105
+ );
106
+ const videoTrackToggle = useMemo(
107
+ () => ({
108
+ showIcon: true,
109
+ source: Track.Source.Camera,
110
+ onChange: handleVideoChange
111
+ }),
112
+ [handleVideoChange]
113
+ );
114
+ return /* @__PURE__ */ jsx("div", { className: "bg-gray-0 border-gray-10 flex h-12 w-23 items-center justify-center gap-1 rounded-2xl border", children: /* @__PURE__ */ jsx(
115
+ DevicesBar,
116
+ {
117
+ microTrack: audioTrack,
118
+ microEnabled: audioEnabled,
119
+ microTrackToggle,
120
+ videoTrack,
121
+ videoEnabled,
122
+ videoTrackToggle
123
+ }
124
+ ) });
125
+ };
126
+ var UserTileUI = ({
127
+ audioTrack,
128
+ videoTrack,
129
+ videoEnabled,
130
+ facingMode,
131
+ videoEl,
132
+ userId,
133
+ isCameraDeniedOrPrompted,
134
+ isMicrophoneDeniedOrPrompted,
135
+ isVideoInitiated
136
+ }) => {
137
+ const isPermissionsBlocked = isCameraDeniedOrPrompted || isMicrophoneDeniedOrPrompted;
138
+ const hintMessage = useMemo(() => {
139
+ if (isPermissionsBlocked) {
140
+ return null;
141
+ }
142
+ if (isCameraDeniedOrPrompted) {
143
+ return isMicrophoneDeniedOrPrompted ? "\u041A\u0430\u043C\u0435\u0440\u0430 \u0438 \u043C\u0438\u043A\u0440\u043E\u0444\u043E\u043D \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u044B" : "\u041A\u0430\u043C\u0435\u0440\u0430 \u043D\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0430";
144
+ }
145
+ if (!videoEnabled) {
146
+ return "\u041A\u0430\u043C\u0435\u0440\u0430 \u043E\u0442\u043A\u043B\u044E\u0447\u0435\u043D\u0430";
147
+ }
148
+ if (!isVideoInitiated) {
149
+ return "\u0417\u0430\u043F\u0443\u0441\u043A \u043A\u0430\u043C\u0435\u0440\u044B...";
150
+ }
151
+ if (videoTrack && videoEnabled) {
152
+ return "";
153
+ }
154
+ return "\u041A\u0430\u043C\u0435\u0440\u0430 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430";
155
+ }, [
156
+ videoTrack,
157
+ videoEnabled,
158
+ isCameraDeniedOrPrompted,
159
+ isMicrophoneDeniedOrPrompted,
160
+ isVideoInitiated,
161
+ isPermissionsBlocked
162
+ ]);
163
+ const permissionsInstructions = useMemo(() => {
164
+ if (isSafari()) {
165
+ const origin = typeof window !== "undefined" ? window.location?.origin?.replace("https://", "") ?? "" : "";
166
+ return [
167
+ `\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043D\u0430 \u0438\u043A\u043E\u043D\u043A\u0443 ${origin} \u0432 \u0430\u0434\u0440\u0435\u0441\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0435`,
168
+ "\u0421\u043D\u0438\u043C\u0438\u0442\u0435 \u0437\u0430\u043F\u0440\u0435\u0442 \u043D\u0430 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435 \u043A\u0430\u043C\u0435\u0440\u044B \u0438 \u043C\u0438\u043A\u0440\u043E\u0444\u043E\u043D\u0430"
169
+ ];
170
+ }
171
+ return [
172
+ "\u041D\u0430\u0436\u043C\u0438\u0442\u0435 \u043D\u0430 \u0437\u043D\u0430\u0447\u043E\u043A \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043A \u0432 \u0430\u0434\u0440\u0435\u0441\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430",
173
+ "\u0421\u043D\u0438\u043C\u0438\u0442\u0435 \u0437\u0430\u043F\u0440\u0435\u0442 \u043D\u0430 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435 \u043A\u0430\u043C\u0435\u0440\u044B \u0438 \u043C\u0438\u043A\u0440\u043E\u0444\u043E\u043D\u0430"
174
+ ];
175
+ }, []);
176
+ const permissionsButtonLabel = useMemo(() => {
177
+ if (!isMicrophoneDeniedOrPrompted && !isCameraDeniedOrPrompted) {
178
+ return null;
179
+ }
180
+ if (isCameraDeniedOrPrompted && isMicrophoneDeniedOrPrompted) {
181
+ return "\u041A\u0430\u043A \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u043A\u0430\u043C\u0435\u0440\u0443 \u0438 \u043C\u0438\u043A\u0440\u043E\u0444\u043E\u043D";
182
+ }
183
+ if (isMicrophoneDeniedOrPrompted) {
184
+ return "\u041A\u0430\u043A \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u043C\u0438\u043A\u0440\u043E\u0444\u043E\u043D";
185
+ }
186
+ if (isCameraDeniedOrPrompted) {
187
+ return "\u041A\u0430\u043A \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u043A\u0430\u043C\u0435\u0440\u0443";
188
+ }
189
+ return null;
190
+ }, [isMicrophoneDeniedOrPrompted, isCameraDeniedOrPrompted]);
191
+ const renderVideo = useMemo(() => {
192
+ if (!videoTrack || isCameraDeniedOrPrompted) {
193
+ return null;
194
+ }
195
+ return /* @__PURE__ */ jsx("div", { className: "aspect-video h-full w-full transform-[rotateY(180deg)]", children: /* @__PURE__ */ jsx(
196
+ SecureVideo,
197
+ {
198
+ ref: videoEl,
199
+ "data-lk-facing-mode": facingMode,
200
+ className: "h-full w-full object-cover",
201
+ playsInline: true,
202
+ muted: true,
203
+ style: {
204
+ display: !videoEnabled || isCameraDeniedOrPrompted ? "none" : void 0,
205
+ opacity: videoTrack?.isMuted || !isVideoInitiated ? 0 : 1,
206
+ transition: "opacity 0.3s ease-in-out"
207
+ },
208
+ disablePictureInPicture: true,
209
+ disableRemotePlayback: true
210
+ }
211
+ ) });
212
+ }, [videoTrack, facingMode, videoEl, videoEnabled, isCameraDeniedOrPrompted, isVideoInitiated]);
213
+ const renderAvatar = useMemo(() => {
214
+ if (videoTrack && !videoTrack.isMuted && !isCameraDeniedOrPrompted) return null;
215
+ return /* @__PURE__ */ jsx("div", { className: "bg-gray-40 flex items-center justify-center rounded-[16px]", children: /* @__PURE__ */ jsxs(Avatar, { size: "xxl", children: [
216
+ /* @__PURE__ */ jsx(
217
+ AvatarImage,
218
+ {
219
+ src: `https://api.sovlium.ru/files/users/${userId}/avatar.webp`,
220
+ alt: "user avatar"
221
+ }
222
+ ),
223
+ /* @__PURE__ */ jsx(AvatarFallback, { size: "xxl", loading: true })
224
+ ] }) });
225
+ }, [videoTrack, userId, isCameraDeniedOrPrompted]);
226
+ return /* @__PURE__ */ jsxs("div", { className: "bg-gray-40 relative flex aspect-video h-full w-full items-center justify-center overflow-hidden rounded-[16px]", children: [
227
+ /* @__PURE__ */ jsxs("div", { className: "relative h-full w-full", children: [
228
+ renderVideo,
229
+ renderAvatar,
230
+ isPermissionsBlocked && /* @__PURE__ */ jsxs("div", { className: "bg-opacity-60 absolute inset-0 flex flex-col items-center justify-center gap-4 bg-black p-6 text-center", children: [
231
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-normal text-white", children: "\u0425\u043E\u0442\u0438\u0442\u0435, \u0447\u0442\u043E\u0431\u044B \u0434\u0440\u0443\u0433\u0438\u0435 \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0438 \u0443\u0441\u043B\u044B\u0448\u0430\u043B\u0438 \u0432\u0430\u0441?" }),
232
+ /* @__PURE__ */ jsx("ol", { className: "list-inside list-decimal space-y-2 text-left text-sm text-white", children: permissionsInstructions.map((instruction, index) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2", children: [
233
+ index === 0 && !isSafari() && /* @__PURE__ */ jsx(Settings, { className: "mt-0.5 h-4 w-4 shrink-0" }),
234
+ /* @__PURE__ */ jsx("span", { children: instruction })
235
+ ] }, index)) }),
236
+ /* @__PURE__ */ jsx("p", { className: "text-gray-30 text-sm", children: "\u041A\u0430\u043C\u0435\u0440\u0443 \u0438\u043B\u0438 \u043C\u0438\u043A\u0440\u043E\u0444\u043E\u043D \u043C\u043E\u0436\u043D\u043E \u043E\u0442\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0432 \u043B\u044E\u0431\u043E\u0439 \u043C\u043E\u043C\u0435\u043D\u0442." }),
237
+ permissionsButtonLabel && /* @__PURE__ */ jsx(Button, { size: "m", variant: "ghost", onClick: openPermissionsDialog, children: permissionsButtonLabel })
238
+ ] }),
239
+ !isPermissionsBlocked && hintMessage && /* @__PURE__ */ jsx("div", { className: "bg-opacity-60 absolute inset-0 flex flex-col items-center justify-center gap-4 bg-black p-6 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-lg font-normal text-white", children: hintMessage }) })
240
+ ] }),
241
+ /* @__PURE__ */ jsx("div", { className: "absolute bottom-5 left-5", children: /* @__PURE__ */ jsx(Controls, { audioTrack, videoTrack }) })
242
+ ] });
243
+ };
244
+ var UserTile = ({ audioTrack, videoTrack }) => {
245
+ const { auth } = useCalls();
246
+ const { data: user } = auth.useCurrentUser();
247
+ const { userId } = user ?? {};
248
+ const {
249
+ userChoices: { videoEnabled }
250
+ } = usePersistentUserChoices();
251
+ const videoEl = useRef(null);
252
+ const [isVideoInitiated, setIsVideoInitiated] = useState(false);
253
+ const isCameraDeniedOrPrompted = useCannotUseDevice("videoinput");
254
+ const isMicrophoneDeniedOrPrompted = useCannotUseDevice("audioinput");
255
+ const facingMode = useMemo(() => {
256
+ if (videoTrack) {
257
+ const { facingMode: facingMode2 } = facingModeFromLocalTrack(videoTrack);
258
+ return facingMode2;
259
+ }
260
+ return "undefined";
261
+ }, [videoTrack]);
262
+ useEffect(() => {
263
+ if (!videoEnabled) {
264
+ setIsVideoInitiated(false);
265
+ }
266
+ }, [videoEnabled]);
267
+ useEffect(() => {
268
+ if (videoTrack) {
269
+ const handleTrackMuted = () => {
270
+ setIsVideoInitiated(false);
271
+ };
272
+ const handleTrackUnmuted = () => {
273
+ if (videoEnabled) {
274
+ setIsVideoInitiated(true);
275
+ }
276
+ };
277
+ videoTrack.on("muted", handleTrackMuted);
278
+ videoTrack.on("unmuted", handleTrackUnmuted);
279
+ return () => {
280
+ videoTrack.off("muted", handleTrackMuted);
281
+ videoTrack.off("unmuted", handleTrackUnmuted);
282
+ };
283
+ }
284
+ }, [videoTrack, videoEnabled]);
285
+ useEffect(() => {
286
+ const currentVideoEl = videoEl.current;
287
+ const currentVideoTrack = videoTrack;
288
+ const handleVideoLoaded = () => {
289
+ if (currentVideoEl && videoEnabled) {
290
+ setIsVideoInitiated(true);
291
+ currentVideoEl.style.opacity = "1";
292
+ } else if (currentVideoEl) {
293
+ currentVideoEl.style.opacity = "0";
294
+ }
295
+ };
296
+ const handleVideoError = () => {
297
+ setIsVideoInitiated(false);
298
+ };
299
+ if (currentVideoEl && currentVideoTrack && videoEnabled) {
300
+ currentVideoTrack.attach(currentVideoEl);
301
+ currentVideoEl.addEventListener("loadedmetadata", handleVideoLoaded);
302
+ currentVideoEl.addEventListener("error", handleVideoError);
303
+ }
304
+ return () => {
305
+ if (currentVideoTrack) {
306
+ currentVideoTrack.detach();
307
+ }
308
+ if (currentVideoEl) {
309
+ currentVideoEl.removeEventListener("loadedmetadata", handleVideoLoaded);
310
+ currentVideoEl.removeEventListener("error", handleVideoError);
311
+ currentVideoEl.style.opacity = "0";
312
+ }
313
+ };
314
+ }, [videoTrack, videoEnabled]);
315
+ return /* @__PURE__ */ jsx(
316
+ UserTileUI,
317
+ {
318
+ audioTrack,
319
+ videoTrack,
320
+ videoEnabled,
321
+ facingMode,
322
+ videoEl,
323
+ userId: userId || "unknown",
324
+ isCameraDeniedOrPrompted,
325
+ isMicrophoneDeniedOrPrompted,
326
+ isVideoInitiated
327
+ }
328
+ );
329
+ };
330
+ var MediaDeviceSelect = ({ devices }) => /* @__PURE__ */ jsx("ul", { children: devices && devices.map((device) => /* @__PURE__ */ jsx("li", { id: device.deviceId, children: /* @__PURE__ */ jsx(SelectItem, { value: device.deviceId ?? "default", children: device.label }) }, device.deviceId)) });
331
+ var placeholders = {
332
+ audioinput: "\u0412\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u044B\u0439 \u043C\u0438\u043A\u0440\u043E\u0444\u043E\u043D",
333
+ audiooutput: "\u0412\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u044B\u0435 \u0434\u0438\u043D\u0430\u043C\u0438\u043A\u0438",
334
+ videoinput: "\u0412\u0441\u0442\u0440\u043E\u0435\u043D\u043D\u0430\u044F \u043A\u0430\u043C\u0435\u0440\u0430",
335
+ default: "\u041F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E"
336
+ };
337
+ var MediaDeviceMenu = ({
338
+ warnDisable,
339
+ kind,
340
+ initialSelection,
341
+ onActiveDeviceChange,
342
+ disabled,
343
+ requestPermissions = false
344
+ }) => {
345
+ const [isOpen, setIsOpen] = React.useState(false);
346
+ const [updateRequired, setUpdateRequired] = React.useState(true);
347
+ const [, setNeedPermissions] = React.useState(requestPermissions);
348
+ const button = React.useRef(null);
349
+ const tooltip = React.useRef(null);
350
+ const handleError = React.useCallback((e) => {
351
+ console.error("Media device error:", e);
352
+ }, []);
353
+ const { devices, setActiveMediaDevice } = useMediaDeviceSelect({
354
+ kind,
355
+ room: void 0,
356
+ // Для PreJoin не нужна комната
357
+ requestPermissions,
358
+ onError: handleError
359
+ });
360
+ React.useLayoutEffect(() => {
361
+ if (isOpen) {
362
+ setNeedPermissions(true);
363
+ }
364
+ }, [isOpen]);
365
+ React.useLayoutEffect(() => {
366
+ if (button.current && tooltip.current && (devices || updateRequired)) {
367
+ const handlePositionChange = (x, y) => {
368
+ if (tooltip.current) {
369
+ tooltip.current.style.left = `${x}px`;
370
+ tooltip.current.style.top = `${y}px`;
371
+ }
372
+ };
373
+ computeMenuPosition(button.current, tooltip.current, handlePositionChange);
374
+ }
375
+ setUpdateRequired(false);
376
+ }, [button, tooltip, updateRequired, devices]);
377
+ const handleClickOutside = React.useCallback(
378
+ (event) => {
379
+ if (!tooltip.current) {
380
+ return;
381
+ }
382
+ if (event.target === button.current) {
383
+ return;
384
+ }
385
+ if (isOpen && wasClickOutside(tooltip.current, event)) {
386
+ setIsOpen(false);
387
+ }
388
+ },
389
+ [isOpen, tooltip, button]
390
+ );
391
+ React.useEffect(() => {
392
+ document.addEventListener("click", handleClickOutside);
393
+ window.addEventListener("resize", () => setUpdateRequired(true));
394
+ return () => {
395
+ document.removeEventListener("click", handleClickOutside);
396
+ window.removeEventListener("resize", () => setUpdateRequired(true));
397
+ };
398
+ }, [handleClickOutside, setUpdateRequired]);
399
+ const getPlaceholder = () => {
400
+ if (initialSelection === "") return placeholders.default;
401
+ if (!initialSelection && kind) {
402
+ return placeholders[kind] || placeholders.default;
403
+ }
404
+ return placeholders.default;
405
+ };
406
+ async function handleActiveChange(deviceId, kind2) {
407
+ setIsOpen(false);
408
+ onActiveDeviceChange?.(kind2, deviceId);
409
+ await setActiveMediaDevice(deviceId);
410
+ }
411
+ return /* @__PURE__ */ jsx("div", { className: `${warnDisable ? "border-orange-80 rounded-lg border-2" : null}`, children: /* @__PURE__ */ jsxs(
412
+ Select,
413
+ {
414
+ onValueChange: (value) => handleActiveChange(value, kind),
415
+ defaultValue: devices?.length > 0 ? initialSelection : void 0,
416
+ disabled: disabled || warnDisable || !devices || devices.length === 0 || devices[0].deviceId === "",
417
+ children: [
418
+ /* @__PURE__ */ jsx(
419
+ SelectTrigger,
420
+ {
421
+ className: "flex w-full flex-row",
422
+ before: /* @__PURE__ */ jsxs("div", { children: [
423
+ kind === "videoinput" && /* @__PURE__ */ jsx(Conference, { width: 14 }),
424
+ kind === "audiooutput" && /* @__PURE__ */ jsx(SoundTwo, { width: 14 }),
425
+ !(kind === "videoinput" || kind === "audiooutput") && /* @__PURE__ */ jsx(Microphone, { width: 14 })
426
+ ] }),
427
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder: getPlaceholder() })
428
+ }
429
+ ),
430
+ /* @__PURE__ */ jsx(
431
+ SelectContent,
432
+ {
433
+ ref: (ref) => ref?.addEventListener("touchend", (e) => e.preventDefault()),
434
+ className: "w-full",
435
+ children: devices.length !== 0 && devices[0].deviceId !== "" && /* @__PURE__ */ jsx(SelectGroup, { children: /* @__PURE__ */ jsx(MediaDeviceSelect, { devices }) })
436
+ }
437
+ )
438
+ ]
439
+ }
440
+ ) });
441
+ };
442
+ var MediaDevices = ({ audioTrack, videoTrack, noiseCancellation }) => {
443
+ const {
444
+ userChoices: { audioDeviceId, audioOutputDeviceId, videoDeviceId, blurEnabled },
445
+ saveAudioInputDeviceId,
446
+ saveAudioOutputDeviceId,
447
+ saveVideoInputDeviceId,
448
+ saveAudioInputEnabled,
449
+ saveVideoInputEnabled,
450
+ saveBlurEnabled
451
+ } = usePersistentUserChoices();
452
+ const { updateStore, token, isConnecting } = useCallStore();
453
+ const { room } = useRoom();
454
+ const cameraPermission = usePermissionsStore((s) => s.cameraPermission);
455
+ const microphonePermission = usePermissionsStore((s) => s.microphonePermission);
456
+ const isBlurSupported = supportsBackgroundProcessors();
457
+ const videoMenuKey = `videoinput-${cameraPermission}`;
458
+ const audioInputMenuKey = `audioinput-${microphonePermission}`;
459
+ const audioOutputMenuKey = `audiooutput-${microphonePermission}`;
460
+ const handleJoin = async () => {
461
+ if (!token) {
462
+ console.error("No token available for joining the call");
463
+ return;
464
+ }
465
+ if (room.state === "connected") {
466
+ console.log("Already connected to room, just updating store...");
467
+ updateStore("connect", true);
468
+ updateStore("isStarted", true);
469
+ updateStore("isConnecting", false);
470
+ return;
471
+ }
472
+ if (isConnecting) {
473
+ return;
474
+ }
475
+ updateStore("isConnecting", true);
476
+ try {
477
+ updateStore("audioDeviceId", audioDeviceId);
478
+ updateStore("audioOutputDeviceId", audioOutputDeviceId);
479
+ updateStore("videoDeviceId", videoDeviceId);
480
+ updateStore("audioEnabled", audioTrack ? !audioTrack.isMuted : false);
481
+ updateStore("videoEnabled", videoTrack ? !videoTrack.isMuted : false);
482
+ updateStore("connect", true);
483
+ updateStore("isStarted", true);
484
+ updateStore("isConnecting", false);
485
+ } catch (error) {
486
+ console.error("Failed to join room:", error);
487
+ updateStore("connect", false);
488
+ updateStore("isStarted", false);
489
+ updateStore("isConnecting", false);
490
+ if (error instanceof Error && error.message.includes("Client initiated disconnect")) {
491
+ console.log("Connection was cancelled by client - this is normal during navigation");
492
+ return;
493
+ }
494
+ console.warn("Connection failed, please try again");
495
+ }
496
+ };
497
+ const handleAudioDeviceChange = useMemo(
498
+ () => async (_kind, deviceId) => {
499
+ try {
500
+ saveAudioInputDeviceId(deviceId);
501
+ if (audioTrack) {
502
+ await audioTrack.setDeviceId({ exact: deviceId });
503
+ const isActuallyEnabled = !audioTrack.isMuted;
504
+ saveAudioInputEnabled(isActuallyEnabled);
505
+ }
506
+ } catch (err) {
507
+ console.error("Failed to switch microphone device", err);
508
+ }
509
+ },
510
+ [audioTrack, saveAudioInputDeviceId, saveAudioInputEnabled]
511
+ );
512
+ const handleVideoDeviceChange = useMemo(
513
+ () => async (_kind, deviceId) => {
514
+ try {
515
+ saveVideoInputDeviceId(deviceId);
516
+ if (videoTrack) {
517
+ await videoTrack.setDeviceId({ exact: deviceId });
518
+ const isActuallyEnabled = !videoTrack.isMuted;
519
+ saveVideoInputEnabled(isActuallyEnabled);
520
+ }
521
+ } catch (err) {
522
+ console.error("Failed to switch camera device", err);
523
+ }
524
+ },
525
+ [videoTrack, saveVideoInputDeviceId, saveVideoInputEnabled]
526
+ );
527
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
528
+ /* @__PURE__ */ jsxs("div", { className: "border-gray-30 flex flex-col justify-between rounded-[16px] border p-5", children: [
529
+ /* @__PURE__ */ jsxs("div", { children: [
530
+ /* @__PURE__ */ jsxs("div", { className: "mb-8", children: [
531
+ /* @__PURE__ */ jsx("h2", { className: "mb-1 font-sans", children: "\u041A\u0430\u043C\u0435\u0440\u0430" }),
532
+ /* @__PURE__ */ jsx(
533
+ MediaDeviceMenu,
534
+ {
535
+ initialSelection: videoDeviceId,
536
+ kind: "videoinput",
537
+ onActiveDeviceChange: handleVideoDeviceChange,
538
+ disabled: cameraPermission !== "granted"
539
+ },
540
+ videoMenuKey
541
+ )
542
+ ] }),
543
+ /* @__PURE__ */ jsxs("div", { className: "my-4", children: [
544
+ /* @__PURE__ */ jsx("h2", { className: "mb-1 font-sans", children: "\u0417\u0432\u0443\u043A" }),
545
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
546
+ /* @__PURE__ */ jsx(
547
+ MediaDeviceMenu,
548
+ {
549
+ initialSelection: audioDeviceId,
550
+ kind: "audioinput",
551
+ onActiveDeviceChange: handleAudioDeviceChange,
552
+ disabled: microphonePermission !== "granted"
553
+ },
554
+ audioInputMenuKey
555
+ ),
556
+ /* @__PURE__ */ jsx(
557
+ MediaDeviceMenu,
558
+ {
559
+ initialSelection: audioOutputDeviceId,
560
+ kind: "audiooutput",
561
+ onActiveDeviceChange: (_, id) => saveAudioOutputDeviceId(id),
562
+ disabled: microphonePermission !== "granted"
563
+ },
564
+ audioOutputMenuKey
565
+ )
566
+ ] })
567
+ ] }),
568
+ isBlurSupported && /* @__PURE__ */ jsx("div", { className: "my-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
569
+ /* @__PURE__ */ jsx(Label, { className: "font-medium text-gray-100", children: "\u0420\u0430\u0437\u043C\u044B\u0442\u0438\u0435 \u0444\u043E\u043D\u0430" }),
570
+ /* @__PURE__ */ jsx(Switch, { checked: blurEnabled, onCheckedChange: saveBlurEnabled })
571
+ ] }) }),
572
+ noiseCancellation && /* @__PURE__ */ jsx("div", { className: "my-4", children: /* @__PURE__ */ jsx(NoiseCancellationSettings, { nc: noiseCancellation, hideOffOption: true }) })
573
+ ] }),
574
+ /* @__PURE__ */ jsx(Button, { onClick: () => handleJoin(), className: "w-full", disabled: isConnecting, children: isConnecting ? "\u041F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u0435..." : "\u041F\u0440\u0438\u0441\u043E\u0435\u0434\u0438\u043D\u0438\u0442\u044C\u0441\u044F" })
575
+ ] }),
576
+ /* @__PURE__ */ jsxs(Alert, { className: "h-full w-full max-w-[1720px]", variant: "brand", children: [
577
+ /* @__PURE__ */ jsx(AlertIcon, { children: /* @__PURE__ */ jsx(InfoCircle, { className: "fill-brand-100" }) }),
578
+ /* @__PURE__ */ jsx(AlertContainer, { className: "h-full", children: /* @__PURE__ */ jsx(AlertDescription, { children: "\u041F\u0435\u0440\u0435\u0434 \u043D\u0430\u0447\u0430\u043B\u043E\u043C \u0437\u0430\u043D\u044F\u0442\u0438\u044F \u0440\u0435\u043A\u043E\u043C\u0435\u043D\u0434\u0443\u0435\u0442\u0441\u044F \u0432\u044B\u0431\u0440\u0430\u0442\u044C \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0430 \u0434\u043B\u044F \u0432\u0438\u0434\u0435\u043E \u0438 \u0437\u0432\u0443\u043A\u0430. \u0415\u0441\u043B\u0438 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0430 \u043D\u0435 \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B, \u043F\u0440\u043E\u0432\u0435\u0440\u044C\u0442\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430. \u041D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043D\u0430 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435 \u043C\u0438\u043A\u0440\u043E\u0444\u043E\u043D\u0430 \u0438 \u043A\u0430\u043C\u0435\u0440\u044B \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043F\u0440\u043E\u0448\u0435\u043D\u043E \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438." }) })
579
+ ] })
580
+ ] });
581
+ };
582
+ var PreJoin = () => {
583
+ const {
584
+ userChoices: { audioEnabled, videoEnabled, audioDeviceId, videoDeviceId },
585
+ saveAudioInputDeviceId,
586
+ saveVideoInputDeviceId
587
+ } = usePersistentUserChoices();
588
+ const initialUserChoices = useRef(null);
589
+ if (initialUserChoices.current === null) {
590
+ initialUserChoices.current = {
591
+ audioEnabled,
592
+ videoEnabled,
593
+ audioDeviceId,
594
+ videoDeviceId
595
+ };
596
+ }
597
+ const onError = useCallback((e) => {
598
+ console.error("PreJoin ERROR:", e);
599
+ }, []);
600
+ useEffect(() => {
601
+ const requestPermissions = async () => {
602
+ try {
603
+ const permissions = await navigator.permissions.query({ name: "camera" });
604
+ if (permissions.state === "prompt") {
605
+ const stream = await navigator.mediaDevices.getUserMedia({
606
+ video: true,
607
+ audio: true
608
+ });
609
+ stream.getTracks().forEach((track) => track.stop());
610
+ }
611
+ } catch (error) {
612
+ console.log("Permission request failed:", error);
613
+ }
614
+ };
615
+ requestPermissions();
616
+ }, []);
617
+ const tracks = usePreviewTracks(
618
+ {
619
+ audio: !!initialUserChoices.current && initialUserChoices.current?.audioEnabled && {
620
+ deviceId: initialUserChoices.current.audioDeviceId
621
+ },
622
+ video: !!initialUserChoices.current && initialUserChoices.current?.videoEnabled && {
623
+ deviceId: initialUserChoices.current.videoDeviceId
624
+ }
625
+ },
626
+ onError
627
+ );
628
+ const [dynamicVideoTrack, setDynamicVideoTrack] = useState(null);
629
+ const [dynamicAudioTrack, setDynamicAudioTrack] = useState(null);
630
+ const previewVideoTrack = useMemo(
631
+ () => tracks?.filter((track) => track.kind === Track.Kind.Video)[0],
632
+ [tracks]
633
+ );
634
+ const previewAudioTrack = useMemo(
635
+ () => tracks?.filter((track) => track.kind === Track.Kind.Audio)[0],
636
+ [tracks]
637
+ );
638
+ useEffect(() => {
639
+ const createVideoTrack = async () => {
640
+ try {
641
+ const track = await createLocalVideoTrack({
642
+ deviceId: { exact: videoDeviceId }
643
+ });
644
+ setDynamicVideoTrack(track);
645
+ } catch (error) {
646
+ onError(error);
647
+ }
648
+ };
649
+ if (videoEnabled && !initialUserChoices.current?.videoEnabled && !previewVideoTrack && !dynamicVideoTrack) {
650
+ createVideoTrack();
651
+ }
652
+ }, [videoEnabled, videoDeviceId, previewVideoTrack, dynamicVideoTrack, onError]);
653
+ useEffect(() => {
654
+ const createAudioTrack = async () => {
655
+ try {
656
+ const track = await createLocalAudioTrack({
657
+ deviceId: { exact: audioDeviceId }
658
+ });
659
+ setDynamicAudioTrack(track);
660
+ } catch (error) {
661
+ onError(error);
662
+ }
663
+ };
664
+ if (audioEnabled && !initialUserChoices.current?.audioEnabled && !previewAudioTrack && !dynamicAudioTrack) {
665
+ createAudioTrack();
666
+ }
667
+ }, [audioEnabled, audioDeviceId, previewAudioTrack, dynamicAudioTrack, onError]);
668
+ useEffect(() => {
669
+ return () => {
670
+ dynamicVideoTrack?.stop();
671
+ };
672
+ }, [dynamicVideoTrack]);
673
+ useEffect(() => {
674
+ return () => {
675
+ dynamicAudioTrack?.stop();
676
+ };
677
+ }, [dynamicAudioTrack]);
678
+ const videoTrack = dynamicVideoTrack || previewVideoTrack;
679
+ const audioTrack = dynamicAudioTrack || previewAudioTrack;
680
+ useEffect(() => {
681
+ console.log("PreJoin tracks debug:", {
682
+ initialUserChoices: initialUserChoices.current,
683
+ videoEnabled,
684
+ audioEnabled,
685
+ videoDeviceId,
686
+ audioDeviceId,
687
+ tracks: tracks?.map((t) => ({ kind: t.kind, enabled: !t.isMuted })),
688
+ previewVideoTrack: previewVideoTrack ? { enabled: !previewVideoTrack.isMuted } : null,
689
+ previewAudioTrack: previewAudioTrack ? { enabled: !previewAudioTrack.isMuted } : null,
690
+ dynamicVideoTrack: dynamicVideoTrack ? { enabled: !dynamicVideoTrack.isMuted } : null,
691
+ dynamicAudioTrack: dynamicAudioTrack ? { enabled: !dynamicAudioTrack.isMuted } : null,
692
+ finalVideoTrack: videoTrack ? { enabled: !videoTrack.isMuted } : null,
693
+ finalAudioTrack: audioTrack ? { enabled: !audioTrack.isMuted } : null
694
+ });
695
+ }, [
696
+ tracks,
697
+ previewVideoTrack,
698
+ previewAudioTrack,
699
+ dynamicVideoTrack,
700
+ dynamicAudioTrack,
701
+ videoTrack,
702
+ audioTrack,
703
+ videoEnabled,
704
+ audioEnabled,
705
+ videoDeviceId,
706
+ audioDeviceId
707
+ ]);
708
+ useResolveInitiallyDefaultDeviceId(audioDeviceId, audioTrack, saveAudioInputDeviceId);
709
+ useResolveInitiallyDefaultDeviceId(videoDeviceId, videoTrack, saveVideoInputDeviceId);
710
+ useVideoBlur(videoTrack);
711
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
712
+ /* @__PURE__ */ jsx(ScrollArea, { className: "h-full w-full", children: /* @__PURE__ */ jsxs("div", { className: "max-xs:p-4 p-4 pt-1", children: [
713
+ /* @__PURE__ */ jsx(Header, {}),
714
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-8 lg:grid-cols-2", children: [
715
+ /* @__PURE__ */ jsx(UserTile, { audioTrack, videoTrack }),
716
+ /* @__PURE__ */ jsx(MediaDevices, { audioTrack, videoTrack })
717
+ ] })
718
+ ] }) }),
719
+ /* @__PURE__ */ jsx(PermissionsDialog, {})
720
+ ] });
721
+ };
722
+ var BottomBar = ({ saveUserChoices = true }) => {
723
+ const { saveAudioInputEnabled, saveVideoInputEnabled } = usePersistentUserChoices$1({
724
+ preventSave: !saveUserChoices
725
+ });
726
+ const { isMicrophoneEnabled, isCameraEnabled, microphoneTrack, cameraTrack } = useLocalParticipant();
727
+ const microphoneToggle = useTrackToggle({
728
+ source: Track.Source.Microphone,
729
+ onChange: (enabled, isUserInitiated) => {
730
+ if (isUserInitiated) {
731
+ saveAudioInputEnabled(enabled);
732
+ }
733
+ }
734
+ });
735
+ const cameraToggle = useTrackToggle({
736
+ source: Track.Source.Camera,
737
+ onChange: (enabled, isUserInitiated) => {
738
+ if (isUserInitiated) {
739
+ saveVideoInputEnabled(enabled);
740
+ }
741
+ }
742
+ });
743
+ const handleMicrophoneToggle = useCallback(async () => {
744
+ microphoneToggle.toggle();
745
+ }, [microphoneToggle]);
746
+ const handleCameraToggle = useCallback(async () => {
747
+ cameraToggle.toggle();
748
+ }, [cameraToggle]);
749
+ const { isChatOpen } = useChatStore();
750
+ const { mode, activeBoardId, activeClassroom, token } = useCallStore();
751
+ const updateStore = useCallStore((state) => state.updateStore);
752
+ const { room } = useRoom();
753
+ const navigation = useCallsNavigation();
754
+ const { useCurrentUser } = useCalls().auth;
755
+ const { data: user } = useCurrentUser();
756
+ const isTutor = user?.default_layout === "tutor";
757
+ const {
758
+ chat: isChatEnabled,
759
+ raiseHand: isRiseHandEnabled,
760
+ whiteboard: isWhiteboardEnabled
761
+ } = useFeaturesStore((s) => s.features);
762
+ const showBackToBoardButton = mode === "full" && activeBoardId && activeClassroom && room && token && room.state === "connected";
763
+ const handleBackToBoard = () => {
764
+ if (!activeBoardId || !activeClassroom) {
765
+ return;
766
+ }
767
+ if (!room || !token || room.state !== "connected") {
768
+ return;
769
+ }
770
+ updateStore("localFullView", false);
771
+ updateStore("mode", "compact");
772
+ navigation.navigateToClassroomBoard(activeClassroom, activeBoardId);
773
+ };
774
+ return /* @__PURE__ */ jsx("div", { className: cn("relative w-full", isChatOpen && "invisible sm:visible"), children: /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-row justify-between p-4 pt-1", children: [
775
+ /* @__PURE__ */ jsx("div", {}),
776
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-row gap-4", children: [
777
+ /* @__PURE__ */ jsx("div", { className: "bg-gray-0 border-gray-10 flex h-[48px] w-[92px] items-center justify-center gap-1 rounded-[16px] border", children: /* @__PURE__ */ jsx(
778
+ DevicesBar,
779
+ {
780
+ microTrack: microphoneTrack?.track,
781
+ microEnabled: isMicrophoneEnabled,
782
+ microTrackToggle: {
783
+ showIcon: true,
784
+ source: Track.Source.Microphone,
785
+ onChange: handleMicrophoneToggle
786
+ },
787
+ videoTrack: cameraTrack?.track,
788
+ videoEnabled: isCameraEnabled,
789
+ videoTrackToggle: {
790
+ showIcon: true,
791
+ source: Track.Source.Camera,
792
+ onChange: handleCameraToggle
793
+ },
794
+ className: "relative"
795
+ }
796
+ ) }),
797
+ /* @__PURE__ */ jsxs("div", { className: "bg-gray-0 border-gray-10 flex h-[48px] items-center justify-center gap-1 rounded-[16px] border p-1", children: [
798
+ /* @__PURE__ */ jsx(ScreenShareButton, {}),
799
+ isWhiteboardEnabled && isTutor && /* @__PURE__ */ jsx(WhiteBoardButton, {}),
800
+ isChatEnabled && /* @__PURE__ */ jsx(ChatButton, {}),
801
+ isRiseHandEnabled && /* @__PURE__ */ jsx(RaiseHandButton, {})
802
+ ] })
803
+ ] }),
804
+ /* @__PURE__ */ jsxs("div", { className: "relative flex flex-row items-center justify-center gap-4", children: [
805
+ showBackToBoardButton && /* @__PURE__ */ jsxs(Tooltip, { delayDuration: 1e3, children: [
806
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
807
+ Button,
808
+ {
809
+ size: "m",
810
+ variant: "default",
811
+ onClick: handleBackToBoard,
812
+ className: "bg-brand-100 hover:bg-brand-80 absolute top-1 left-[-132px] m-0 h-10 w-[128px] rounded-xl px-2",
813
+ "data-umami-event": "call-back-to-board",
814
+ children: [
815
+ /* @__PURE__ */ jsx(WhiteBoard, { className: "fill-gray-0 h-5 w-5" }),
816
+ /* @__PURE__ */ jsx("span", { className: "text-gray-0 ml-2", children: "\u041A \u0434\u043E\u0441\u043A\u0435" })
817
+ ]
818
+ }
819
+ ) }),
820
+ /* @__PURE__ */ jsx(TooltipContent, { side: "top", align: "center", children: "\u0412\u0435\u0440\u043D\u0443\u0442\u044C\u0441\u044F \u043A \u0434\u043E\u0441\u043A\u0435 \u0434\u043B\u044F \u0441\u043E\u0432\u043C\u0435\u0441\u0442\u043D\u043E\u0439 \u0440\u0430\u0431\u043E\u0442\u044B" })
821
+ ] }),
822
+ /* @__PURE__ */ jsx("div", { className: "bg-gray-0 border-gray-10 flex h-[48px] w-[48px] items-center justify-center gap-1 rounded-[16px] border p-1", children: /* @__PURE__ */ jsx(DisconnectButton, {}) })
823
+ ] })
824
+ ] }) });
825
+ };
826
+ var ActiveRoom = () => {
827
+ const { chat: isChatEnabled } = useFeaturesStore((s) => s.features);
828
+ useHandFocus();
829
+ useParticipantJoinSync();
830
+ const { cameraTrack } = useLocalParticipant();
831
+ const videoTrack = cameraTrack?.track;
832
+ const mode = useCallStore((state) => state.mode);
833
+ const videoTrackForBlur = mode === "full" ? videoTrack : null;
834
+ useVideoBlur(videoTrackForBlur);
835
+ return /* @__PURE__ */ jsx("div", { id: "videoConferenceContainer", className: "bg-gray-0 h-full", children: /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col justify-stretch", children: [
836
+ /* @__PURE__ */ jsx(CallsOnboarding, {}),
837
+ /* @__PURE__ */ jsx(UpBar, {}),
838
+ /* @__PURE__ */ jsxs("div", { className: "flex h-full items-center justify-center gap-4 overflow-hidden p-4", children: [
839
+ /* @__PURE__ */ jsx("div", { className: "flex h-full w-full justify-center text-center text-gray-100", children: /* @__PURE__ */ jsx(VideoGrid, {}) }),
840
+ isChatEnabled && /* @__PURE__ */ jsx(Chat, {})
841
+ ] }),
842
+ /* @__PURE__ */ jsx(BottomBar, {})
843
+ ] }) });
844
+ };
845
+ var Call = () => {
846
+ const isStarted = useCallStore((state) => state.isStarted);
847
+ const focusMode = useFocusModeStore((s) => s.focusMode);
848
+ const { pathname } = useCallsNavigation();
849
+ useInitUserDevices();
850
+ useVideoSecurity();
851
+ const mode = useCallStore((state) => state.mode);
852
+ const updateStore = useCallStore((state) => state.updateStore);
853
+ useEffect(() => {
854
+ const isOnCallPage = /^\/call\/[^/]+$/.test(pathname);
855
+ if (isOnCallPage && mode === "compact") {
856
+ updateStore("mode", "full");
857
+ }
858
+ }, [pathname, mode, updateStore]);
859
+ return /* @__PURE__ */ jsx(
860
+ "div",
861
+ {
862
+ className: "h-full",
863
+ style: focusMode ? {
864
+ "--header-height": "0px",
865
+ "--available-height": "calc(100dvh - 0px - var(--upbar-height) - var(--bottom-bar-height))"
866
+ } : void 0,
867
+ children: /* @__PURE__ */ jsx("div", { className: "flex h-full w-full flex-col", children: isStarted ? /* @__PURE__ */ jsx("div", { id: "videoConferenceContainer", className: "bg-gray-5 h-full", children: /* @__PURE__ */ jsx(ActiveRoom, {}) }) : /* @__PURE__ */ jsx(PreJoin, {}) })
868
+ }
869
+ );
870
+ };
871
+
872
+ export { Call };
873
+ //# sourceMappingURL=index.mjs.map
874
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui/PreJoin/components/Header/Header.tsx","../src/ui/PreJoin/components/UserTile/Controls.tsx","../src/ui/PreJoin/components/UserTile/UserTile.tsx","../src/ui/PreJoin/components/MediaDevices/MediaDeviceSelect.tsx","../src/ui/PreJoin/components/MediaDevices/MediaDeviceMenu.tsx","../src/ui/PreJoin/components/MediaDevices/MediaDevices.tsx","../src/ui/PreJoin/PreJoin.tsx","../src/ui/Bottom/BottomBar.tsx","../src/ui/Room/ActiveRoom.tsx","../src/ui/Call.tsx"],"names":["jsx","useMemo","jsxs","Button","useCalls","usePersistentUserChoices","facingMode","kind","useRef","useCallback","useEffect","useState","Track","useCallStore","useRoom","useCallsNavigation","DevicesBar","Tooltip","TooltipTrigger","TooltipContent","useFeaturesStore","useLocalParticipant","useVideoBlur"],"mappings":"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","file":"index.mjs","sourcesContent":["import { Button } from '@xipkg/button';\nimport { ArrowLeft } from '@xipkg/icons';\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@xipkg/tooltip';\nimport { useCalls, useCallsNavigation } from '@xipkg/calls-providers';\n\n/* eslint-disable no-irregular-whitespace */\nexport const Header = () => {\n const navigation = useCallsNavigation();\n const callId = navigation.getCallId();\n\n const { room } = useCalls();\n const { data: classroom } = room.useGetClassroom(Number(callId));\n\n return (\n <div className=\"mb-4 flex flex-col items-start gap-2 sm:flex-row sm:items-center\">\n <div className=\"flex flex-row items-center gap-2\">\n <Tooltip delayDuration={1000}>\n <TooltipTrigger asChild>\n <Button\n onClick={() => {\n if (callId) {\n navigation.navigateToClassroom(callId);\n }\n }}\n type=\"button\"\n variant=\"none\"\n className=\"flex size-[40px] min-h-[40xp] min-w-[40px] items-center justify-center rounded-[12px] p-0\"\n >\n <ArrowLeft className=\"fill-gray-100\" />\n </Button>\n </TooltipTrigger>\n <TooltipContent side=\"bottom\" align=\"start\">\n Вернуться в кабинет\n </TooltipContent>\n </Tooltip>\n <h1 className=\"text-xl-base font-semibold text-gray-100\">Присоединиться к занятию</h1>\n </div>\n <p className=\"text-s-base text-gray-60 pt-0 pl-12 align-baseline sm:pt-2 sm:pl-0\">\n {classroom?.name}\n </p>\n </div>\n );\n};\n","import { LocalAudioTrack, LocalVideoTrack, Track } from 'livekit-client';\nimport { useCallback, useMemo } from 'react';\n\nimport { DevicesBar } from '@xipkg/calls-ui';\nimport { usePersistentUserChoices } from '@xipkg/calls-hooks';\n\ntype ControlsProps = {\n audioTrack?: LocalAudioTrack;\n videoTrack?: LocalVideoTrack;\n};\n\nexport const Controls = ({ audioTrack, videoTrack }: ControlsProps) => {\n const {\n userChoices: { audioEnabled, videoEnabled },\n saveAudioInputEnabled,\n saveVideoInputEnabled,\n } = usePersistentUserChoices();\n\n const handleAudioChange = useCallback(\n async (enabled: boolean) => {\n // console.log('Controls: handleAudioChange', {\n // enabled,\n // audioTrack: !!audioTrack,\n // currentMuted: audioTrack?.isMuted,\n // });\n saveAudioInputEnabled(enabled);\n if (audioTrack) {\n if (enabled) {\n console.log('Controls: unmuting audio track');\n await audioTrack.unmute();\n } else {\n console.log('Controls: muting audio track');\n await audioTrack.mute();\n }\n console.log('Controls: audio track state after change', { muted: audioTrack.isMuted });\n } else {\n console.log('Controls: no audio track available');\n }\n },\n [audioTrack, saveAudioInputEnabled],\n );\n\n const handleVideoChange = useCallback(\n async (enabled: boolean) => {\n // console.log('Controls: handleVideoChange', {\n // enabled,\n // videoTrack: !!videoTrack,\n // currentMuted: videoTrack?.isMuted,\n // });\n saveVideoInputEnabled(enabled);\n if (videoTrack) {\n if (enabled) {\n console.log('Controls: unmuting video track');\n await videoTrack.unmute();\n } else {\n console.log('Controls: muting video track');\n await videoTrack.mute();\n }\n console.log('Controls: video track state after change', { muted: videoTrack.isMuted });\n } else {\n console.log('Controls: no video track available');\n }\n },\n [videoTrack, saveVideoInputEnabled],\n );\n\n const microTrackToggle = useMemo(\n () => ({\n showIcon: true,\n source: Track.Source.Microphone,\n onChange: handleAudioChange,\n }),\n [handleAudioChange],\n );\n\n const videoTrackToggle = useMemo(\n () => ({\n showIcon: true,\n source: Track.Source.Camera,\n onChange: handleVideoChange,\n }),\n [handleVideoChange],\n );\n\n return (\n <div className=\"bg-gray-0 border-gray-10 flex h-12 w-23 items-center justify-center gap-1 rounded-2xl border\">\n <DevicesBar\n microTrack={audioTrack}\n microEnabled={audioEnabled}\n microTrackToggle={microTrackToggle}\n videoTrack={videoTrack}\n videoEnabled={videoEnabled}\n videoTrackToggle={videoTrackToggle}\n />\n </div>\n );\n};\n","import { Avatar, AvatarFallback, AvatarImage } from '@xipkg/avatar';\nimport { useMemo, useRef, useEffect, useState } from 'react';\nimport { facingModeFromLocalTrack, LocalVideoTrack, LocalAudioTrack } from 'livekit-client';\nimport { Controls } from './Controls';\nimport { useCannotUseDevice, usePersistentUserChoices } from '@xipkg/calls-hooks';\nimport { openPermissionsDialog } from '@xipkg/calls-store';\nimport { Button } from '@xipkg/button';\nimport { SecureVideo } from '@xipkg/calls-ui';\nimport { Settings } from '@xipkg/icons';\nimport { isSafari } from '@xipkg/calls-utils';\nimport { useCalls } from '@xipkg/calls-providers';\n\nconst UserTileUI = ({\n audioTrack,\n videoTrack,\n videoEnabled,\n facingMode,\n videoEl,\n userId,\n isCameraDeniedOrPrompted,\n isMicrophoneDeniedOrPrompted,\n isVideoInitiated,\n}: {\n audioTrack?: LocalAudioTrack;\n videoTrack?: LocalVideoTrack;\n videoEnabled: boolean;\n facingMode: string;\n videoEl: React.RefObject<HTMLVideoElement | null>;\n userId: string;\n isCameraDeniedOrPrompted: boolean;\n isMicrophoneDeniedOrPrompted: boolean;\n isVideoInitiated: boolean;\n}) => {\n const isPermissionsBlocked = isCameraDeniedOrPrompted || isMicrophoneDeniedOrPrompted;\n\n const hintMessage = useMemo(() => {\n if (isPermissionsBlocked) {\n return null;\n }\n if (isCameraDeniedOrPrompted) {\n return isMicrophoneDeniedOrPrompted\n ? 'Камера и микрофон не разрешены'\n : 'Камера не разрешена';\n }\n if (!videoEnabled) {\n return 'Камера отключена';\n }\n if (!isVideoInitiated) {\n return 'Запуск камеры...';\n }\n if (videoTrack && videoEnabled) {\n return '';\n }\n return 'Камера недоступна';\n }, [\n videoTrack,\n videoEnabled,\n isCameraDeniedOrPrompted,\n isMicrophoneDeniedOrPrompted,\n isVideoInitiated,\n isPermissionsBlocked,\n ]);\n\n const permissionsInstructions = useMemo(() => {\n if (isSafari()) {\n const origin =\n typeof window !== 'undefined'\n ? (window.location?.origin?.replace('https://', '') ?? '')\n : '';\n return [\n `Нажмите на иконку ${origin} в адресной строке`,\n 'Снимите запрет на использование камеры и микрофона',\n ];\n }\n return [\n 'Нажмите на значок настроек в адресной строке браузера',\n 'Снимите запрет на использование камеры и микрофона',\n ];\n }, []);\n\n const permissionsButtonLabel = useMemo(() => {\n if (!isMicrophoneDeniedOrPrompted && !isCameraDeniedOrPrompted) {\n return null;\n }\n if (isCameraDeniedOrPrompted && isMicrophoneDeniedOrPrompted) {\n return 'Как разрешить камеру и микрофон';\n }\n if (isMicrophoneDeniedOrPrompted) {\n return 'Как разрешить микрофон';\n }\n if (isCameraDeniedOrPrompted) {\n return 'Как разрешить камеру';\n }\n return null;\n }, [isMicrophoneDeniedOrPrompted, isCameraDeniedOrPrompted]);\n\n const renderVideo = useMemo(() => {\n if (!videoTrack || isCameraDeniedOrPrompted) {\n return null;\n }\n\n return (\n <div className=\"aspect-video h-full w-full transform-[rotateY(180deg)]\">\n <SecureVideo\n ref={videoEl}\n data-lk-facing-mode={facingMode}\n className=\"h-full w-full object-cover\"\n playsInline\n muted\n style={{\n display: !videoEnabled || isCameraDeniedOrPrompted ? 'none' : undefined,\n opacity: videoTrack?.isMuted || !isVideoInitiated ? 0 : 1,\n transition: 'opacity 0.3s ease-in-out',\n }}\n disablePictureInPicture\n disableRemotePlayback\n />\n </div>\n );\n }, [videoTrack, facingMode, videoEl, videoEnabled, isCameraDeniedOrPrompted, isVideoInitiated]);\n\n const renderAvatar = useMemo(() => {\n if (videoTrack && !videoTrack.isMuted && !isCameraDeniedOrPrompted) return null;\n\n return (\n <div className=\"bg-gray-40 flex items-center justify-center rounded-[16px]\">\n <Avatar size=\"xxl\">\n <AvatarImage\n src={`https://api.sovlium.ru/files/users/${userId}/avatar.webp`}\n alt=\"user avatar\"\n />\n <AvatarFallback size=\"xxl\" loading />\n </Avatar>\n </div>\n );\n }, [videoTrack, userId, isCameraDeniedOrPrompted]);\n\n return (\n <div className=\"bg-gray-40 relative flex aspect-video h-full w-full items-center justify-center overflow-hidden rounded-[16px]\">\n <div className=\"relative h-full w-full\">\n {renderVideo}\n {renderAvatar}\n\n {isPermissionsBlocked && (\n <div className=\"bg-opacity-60 absolute inset-0 flex flex-col items-center justify-center gap-4 bg-black p-6 text-center\">\n <p className=\"text-lg font-normal text-white\">\n Хотите, чтобы другие участники услышали вас?\n </p>\n <ol className=\"list-inside list-decimal space-y-2 text-left text-sm text-white\">\n {permissionsInstructions.map((instruction, index) => (\n <li key={index} className=\"flex items-start gap-2\">\n {index === 0 && !isSafari() && <Settings className=\"mt-0.5 h-4 w-4 shrink-0\" />}\n <span>{instruction}</span>\n </li>\n ))}\n </ol>\n <p className=\"text-gray-30 text-sm\">\n Камеру или микрофон можно отключить в любой момент.\n </p>\n {permissionsButtonLabel && (\n <Button size=\"m\" variant=\"ghost\" onClick={openPermissionsDialog}>\n {permissionsButtonLabel}\n </Button>\n )}\n </div>\n )}\n\n {!isPermissionsBlocked && hintMessage && (\n <div className=\"bg-opacity-60 absolute inset-0 flex flex-col items-center justify-center gap-4 bg-black p-6 text-center\">\n <p className=\"text-lg font-normal text-white\">{hintMessage}</p>\n </div>\n )}\n </div>\n\n <div className=\"absolute bottom-5 left-5\">\n <Controls audioTrack={audioTrack} videoTrack={videoTrack} />\n </div>\n </div>\n );\n};\n\ninterface UserTileProps {\n audioTrack?: LocalAudioTrack;\n videoTrack?: LocalVideoTrack;\n}\n\nexport const UserTile = ({ audioTrack, videoTrack }: UserTileProps) => {\n const { auth } = useCalls();\n const { data: user } = auth.useCurrentUser();\n const { userId } = user ?? {};\n\n const {\n userChoices: { videoEnabled },\n } = usePersistentUserChoices();\n\n const videoEl = useRef<HTMLVideoElement>(null);\n const [isVideoInitiated, setIsVideoInitiated] = useState(false);\n\n const isCameraDeniedOrPrompted = useCannotUseDevice('videoinput');\n const isMicrophoneDeniedOrPrompted = useCannotUseDevice('audioinput');\n\n const facingMode = useMemo(() => {\n if (videoTrack) {\n const { facingMode } = facingModeFromLocalTrack(videoTrack);\n return facingMode;\n }\n return 'undefined';\n }, [videoTrack]);\n\n useEffect(() => {\n if (!videoEnabled) {\n setIsVideoInitiated(false);\n }\n }, [videoEnabled]);\n\n useEffect(() => {\n if (videoTrack) {\n const handleTrackMuted = () => {\n setIsVideoInitiated(false);\n };\n\n const handleTrackUnmuted = () => {\n if (videoEnabled) {\n setIsVideoInitiated(true);\n }\n };\n\n videoTrack.on('muted', handleTrackMuted);\n videoTrack.on('unmuted', handleTrackUnmuted);\n\n return () => {\n videoTrack.off('muted', handleTrackMuted);\n videoTrack.off('unmuted', handleTrackUnmuted);\n };\n }\n }, [videoTrack, videoEnabled]);\n\n useEffect(() => {\n const currentVideoEl = videoEl.current;\n const currentVideoTrack = videoTrack;\n\n const handleVideoLoaded = () => {\n if (currentVideoEl && videoEnabled) {\n setIsVideoInitiated(true);\n currentVideoEl.style.opacity = '1';\n } else if (currentVideoEl) {\n currentVideoEl.style.opacity = '0';\n }\n };\n\n const handleVideoError = () => {\n setIsVideoInitiated(false);\n };\n\n if (currentVideoEl && currentVideoTrack && videoEnabled) {\n currentVideoTrack.attach(currentVideoEl);\n currentVideoEl.addEventListener('loadedmetadata', handleVideoLoaded);\n currentVideoEl.addEventListener('error', handleVideoError);\n }\n\n return () => {\n if (currentVideoTrack) {\n currentVideoTrack.detach();\n }\n if (currentVideoEl) {\n currentVideoEl.removeEventListener('loadedmetadata', handleVideoLoaded);\n currentVideoEl.removeEventListener('error', handleVideoError);\n currentVideoEl.style.opacity = '0';\n }\n };\n }, [videoTrack, videoEnabled]);\n\n return (\n <UserTileUI\n audioTrack={audioTrack}\n videoTrack={videoTrack}\n videoEnabled={videoEnabled}\n facingMode={facingMode}\n videoEl={videoEl}\n userId={userId || 'unknown'}\n isCameraDeniedOrPrompted={isCameraDeniedOrPrompted}\n isMicrophoneDeniedOrPrompted={isMicrophoneDeniedOrPrompted}\n isVideoInitiated={isVideoInitiated}\n />\n );\n};\n","import { SelectItem } from '@xipkg/select';\n\nexport type MediaDeviceKind = 'videoinput' | 'audiooutput' | 'audioinput';\n\ntype MediaDeviceSelectPropsT = {\n devices: MediaDeviceInfo[];\n};\n\nexport const MediaDeviceSelect = ({ devices }: MediaDeviceSelectPropsT) => (\n <ul>\n {devices &&\n devices.map((device) => (\n <li key={device.deviceId} id={device.deviceId}>\n <SelectItem value={device.deviceId ?? 'default'}>{device.label}</SelectItem>\n </li>\n ))}\n </ul>\n);\n","import React from 'react';\nimport { computeMenuPosition, wasClickOutside } from '@livekit/components-core';\nimport { Select, SelectContent, SelectGroup, SelectTrigger, SelectValue } from '@xipkg/select';\nimport { Conference, Microphone, SoundTwo } from '@xipkg/icons';\nimport { useMediaDeviceSelect } from '@livekit/components-react';\nimport { MediaDeviceKind, MediaDeviceSelect } from './MediaDeviceSelect';\n\nconst placeholders = {\n audioinput: 'Встроенный микрофон',\n audiooutput: 'Встроенные динамики',\n videoinput: 'Встроенная камера',\n default: 'По умолчанию',\n};\n\nexport interface MediaDeviceMenuProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n disabled?: boolean;\n kind: MediaDeviceKind;\n initialSelection: string | undefined;\n onActiveDeviceChange?: (kind: MediaDeviceKind, deviceId: string) => void;\n warnDisable?: boolean;\n requestPermissions?: boolean;\n}\n\nexport const MediaDeviceMenu = ({\n warnDisable,\n kind,\n initialSelection,\n onActiveDeviceChange,\n disabled,\n requestPermissions = false,\n}: MediaDeviceMenuProps) => {\n const [isOpen, setIsOpen] = React.useState(false);\n const [updateRequired, setUpdateRequired] = React.useState<boolean>(true);\n const [, setNeedPermissions] = React.useState(requestPermissions);\n const button = React.useRef<HTMLButtonElement>(null);\n const tooltip = React.useRef<HTMLDivElement>(null);\n\n const handleError = React.useCallback((e: Error) => {\n console.error('Media device error:', e);\n }, []);\n const { devices, setActiveMediaDevice } = useMediaDeviceSelect({\n kind,\n room: undefined, // Для PreJoin не нужна комната\n requestPermissions,\n onError: handleError,\n });\n\n React.useLayoutEffect(() => {\n if (isOpen) {\n setNeedPermissions(true);\n }\n }, [isOpen]);\n\n React.useLayoutEffect(() => {\n if (button.current && tooltip.current && (devices || updateRequired)) {\n const handlePositionChange = (x: number, y: number) => {\n if (tooltip.current) {\n tooltip.current.style.left = `${x}px`;\n tooltip.current.style.top = `${y}px`;\n }\n };\n\n computeMenuPosition(button.current, tooltip.current, handlePositionChange);\n }\n setUpdateRequired(false);\n }, [button, tooltip, updateRequired, devices]);\n\n const handleClickOutside = React.useCallback(\n (event: MouseEvent) => {\n if (!tooltip.current) {\n return;\n }\n if (event.target === button.current) {\n return;\n }\n if (isOpen && wasClickOutside(tooltip.current, event)) {\n setIsOpen(false);\n }\n },\n [isOpen, tooltip, button],\n );\n\n React.useEffect(() => {\n document.addEventListener<'click'>('click', handleClickOutside);\n window.addEventListener<'resize'>('resize', () => setUpdateRequired(true));\n return () => {\n document.removeEventListener<'click'>('click', handleClickOutside);\n window.removeEventListener<'resize'>('resize', () => setUpdateRequired(true));\n };\n }, [handleClickOutside, setUpdateRequired]);\n\n const getPlaceholder = () => {\n if (initialSelection === '') return placeholders.default;\n if (!initialSelection && kind) {\n return placeholders[kind] || placeholders.default;\n }\n return placeholders.default;\n };\n async function handleActiveChange(deviceId: string, kind: MediaDeviceKind) {\n setIsOpen(false);\n onActiveDeviceChange?.(kind, deviceId);\n await setActiveMediaDevice(deviceId);\n }\n\n return (\n <div className={`${warnDisable ? 'border-orange-80 rounded-lg border-2' : null}`}>\n <Select\n onValueChange={(value) => handleActiveChange(value, kind)}\n defaultValue={devices?.length > 0 ? initialSelection : undefined}\n disabled={\n disabled || warnDisable || !devices || devices.length === 0 || devices[0].deviceId === ''\n }\n >\n <SelectTrigger\n className=\"flex w-full flex-row\"\n before={\n <div>\n {kind === 'videoinput' && <Conference width={14} />}\n {kind === 'audiooutput' && <SoundTwo width={14} />}\n {!(kind === 'videoinput' || kind === 'audiooutput') && <Microphone width={14} />}\n </div>\n }\n >\n <SelectValue placeholder={getPlaceholder()} />\n </SelectTrigger>\n <SelectContent\n ref={(ref) => ref?.addEventListener('touchend', (e) => e.preventDefault())}\n className=\"w-full\"\n >\n {devices.length !== 0 && devices[0].deviceId !== '' && (\n <SelectGroup>\n <MediaDeviceSelect devices={devices} />\n </SelectGroup>\n )}\n </SelectContent>\n </Select>\n </div>\n );\n};\n","import { useMemo } from 'react';\nimport { Button } from '@xipkg/button';\nimport { Switch } from '@xipkg/switcher';\nimport { Label } from '@xipkg/label';\nimport { Alert, AlertIcon, AlertContainer, AlertDescription } from '@xipkg/alert';\nimport { InfoCircle } from '@xipkg/icons';\nimport { MediaDeviceMenu } from './MediaDeviceMenu';\nimport { LocalAudioTrack, LocalVideoTrack } from 'livekit-client';\nimport { UseNoiseCancellationResult, usePersistentUserChoices } from '@xipkg/calls-hooks';\nimport { useCallStore, usePermissionsStore } from '@xipkg/calls-store';\nimport { useRoom } from '@xipkg/calls-providers';\nimport { supportsBackgroundProcessors } from '@livekit/track-processors';\nimport { NoiseCancellationSettings } from '@xipkg/calls-ui';\n\ninterface MediaDevicesProps {\n audioTrack?: LocalAudioTrack;\n videoTrack?: LocalVideoTrack;\n noiseCancellation?: UseNoiseCancellationResult;\n}\n\nexport const MediaDevices = ({ audioTrack, videoTrack, noiseCancellation }: MediaDevicesProps) => {\n const {\n userChoices: { audioDeviceId, audioOutputDeviceId, videoDeviceId, blurEnabled },\n saveAudioInputDeviceId,\n saveAudioOutputDeviceId,\n saveVideoInputDeviceId,\n saveAudioInputEnabled,\n saveVideoInputEnabled,\n saveBlurEnabled,\n } = usePersistentUserChoices();\n\n const { updateStore, token, isConnecting } = useCallStore();\n const { room } = useRoom();\n const cameraPermission = usePermissionsStore((s) => s.cameraPermission);\n const microphonePermission = usePermissionsStore((s) => s.microphonePermission);\n\n const isBlurSupported = supportsBackgroundProcessors();\n\n // Ключи по разрешениям: при смене denied → granted меню перемонтируется и заново запрашивает список устройств\n const videoMenuKey = `videoinput-${cameraPermission}`;\n const audioInputMenuKey = `audioinput-${microphonePermission}`;\n const audioOutputMenuKey = `audiooutput-${microphonePermission}`;\n\n const handleJoin = async () => {\n if (!token) {\n console.error('No token available for joining the call');\n return;\n }\n\n // Проверяем, не подключены ли уже\n if (room.state === 'connected') {\n console.log('Already connected to room, just updating store...');\n // Если уже подключены, просто обновляем store\n updateStore('connect', true);\n updateStore('isStarted', true);\n updateStore('isConnecting', false);\n return;\n }\n\n if (isConnecting) {\n // console.log('Already connecting to room...');\n return;\n }\n\n // Устанавливаем флаг подключения\n updateStore('isConnecting', true);\n\n try {\n // Сохраняем текущие настройки устройств в store\n updateStore('audioDeviceId', audioDeviceId);\n updateStore('audioOutputDeviceId', audioOutputDeviceId);\n updateStore('videoDeviceId', videoDeviceId);\n\n // Сохраняем состояние аудио и видео\n updateStore('audioEnabled', audioTrack ? !audioTrack.isMuted : false);\n updateStore('videoEnabled', videoTrack ? !videoTrack.isMuted : false);\n\n // console.log('Preparing to join room...');\n\n // LiveKitRoom автоматически управляет подключением\n // Нам нужно только установить флаг подключения\n updateStore('connect', true);\n updateStore('isStarted', true);\n updateStore('isConnecting', false);\n\n // console.log('Successfully joined room with devices:', {\n // audioDeviceId,\n // audioOutputDeviceId,\n // videoDeviceId,\n // audioEnabled: audioTrack ? !audioTrack.isMuted : false,\n // videoEnabled: videoTrack ? !videoTrack.isMuted : false,\n // });\n } catch (error) {\n console.error('Failed to join room:', error);\n\n // Сбрасываем состояние при ошибке\n updateStore('connect', false);\n updateStore('isStarted', false);\n updateStore('isConnecting', false);\n\n // Если это ошибка отключения клиента, не показываем пользователю\n if (error instanceof Error && error.message.includes('Client initiated disconnect')) {\n console.log('Connection was cancelled by client - this is normal during navigation');\n return;\n }\n\n // Для других ошибок можно показать уведомление пользователю\n console.warn('Connection failed, please try again');\n }\n };\n\n // Обработчики переключения устройств с обработкой ошибок\n const handleAudioDeviceChange = useMemo(\n () => async (_kind: MediaDeviceKind, deviceId: string) => {\n try {\n saveAudioInputDeviceId(deviceId);\n if (audioTrack) {\n await audioTrack.setDeviceId({ exact: deviceId });\n // Синхронизируем состояние после смены устройства\n const isActuallyEnabled = !audioTrack.isMuted;\n // console.log('MediaDevices: audio device changed, syncing state', {\n // deviceId,\n // trackMuted: audioTrack.isMuted,\n // shouldBeEnabled: isActuallyEnabled,\n // });\n saveAudioInputEnabled(isActuallyEnabled);\n }\n } catch (err) {\n console.error('Failed to switch microphone device', err);\n }\n },\n [audioTrack, saveAudioInputDeviceId, saveAudioInputEnabled],\n );\n\n const handleVideoDeviceChange = useMemo(\n () => async (_kind: MediaDeviceKind, deviceId: string) => {\n try {\n saveVideoInputDeviceId(deviceId);\n if (videoTrack) {\n await videoTrack.setDeviceId({ exact: deviceId });\n // Синхронизируем состояние после смены устройства\n const isActuallyEnabled = !videoTrack.isMuted;\n // console.log('MediaDevices: video device changed, syncing state', {\n // deviceId,\n // trackMuted: videoTrack.isMuted,\n // shouldBeEnabled: isActuallyEnabled,\n // });\n saveVideoInputEnabled(isActuallyEnabled);\n }\n } catch (err) {\n console.error('Failed to switch camera device', err);\n }\n },\n [videoTrack, saveVideoInputDeviceId, saveVideoInputEnabled],\n );\n\n return (\n <div className=\"flex flex-col gap-4\">\n <div className=\"border-gray-30 flex flex-col justify-between rounded-[16px] border p-5\">\n <div>\n <div className=\"mb-8\">\n <h2 className=\"mb-1 font-sans\">Камера</h2>\n <MediaDeviceMenu\n key={videoMenuKey}\n initialSelection={videoDeviceId}\n kind=\"videoinput\"\n onActiveDeviceChange={handleVideoDeviceChange}\n disabled={cameraPermission !== 'granted'}\n />\n </div>\n <div className=\"my-4\">\n <h2 className=\"mb-1 font-sans\">Звук</h2>\n <div className=\"flex flex-col gap-2\">\n <MediaDeviceMenu\n key={audioInputMenuKey}\n initialSelection={audioDeviceId}\n kind=\"audioinput\"\n onActiveDeviceChange={handleAudioDeviceChange}\n disabled={microphonePermission !== 'granted'}\n />\n <MediaDeviceMenu\n key={audioOutputMenuKey}\n initialSelection={audioOutputDeviceId}\n kind=\"audiooutput\"\n onActiveDeviceChange={(_, id) => saveAudioOutputDeviceId(id)}\n disabled={microphonePermission !== 'granted'}\n />\n </div>\n </div>\n {isBlurSupported && (\n <div className=\"my-4\">\n <div className=\"flex items-center justify-between\">\n <Label className=\"font-medium text-gray-100\">Размытие фона</Label>\n <Switch checked={blurEnabled} onCheckedChange={saveBlurEnabled} />\n </div>\n </div>\n )}\n {noiseCancellation && (\n <div className=\"my-4\">\n <NoiseCancellationSettings nc={noiseCancellation} hideOffOption />\n </div>\n )}\n </div>\n <Button onClick={() => handleJoin()} className=\"w-full\" disabled={isConnecting}>\n {isConnecting ? 'Подключение...' : 'Присоединиться'}\n </Button>\n </div>\n <Alert className=\"h-full w-full max-w-[1720px]\" variant=\"brand\">\n <AlertIcon>\n <InfoCircle className=\"fill-brand-100\" />\n </AlertIcon>\n <AlertContainer className=\"h-full\">\n <AlertDescription>\n Перед началом занятия рекомендуется выбрать устройства для видео и звука. Если\n устройства не доступны, проверьте настройки браузера. Необходимое разрешение на\n использование микрофона и камеры будет запрошено автоматически.\n </AlertDescription>\n </AlertContainer>\n </Alert>\n </div>\n );\n};\n","import { ScrollArea } from '@xipkg/scrollarea';\nimport { Header, UserTile, MediaDevices } from './components';\nimport { PermissionsDialog } from '@xipkg/calls-ui';\nimport { useMemo, useRef, useEffect, useCallback, useState } from 'react';\nimport {\n Track,\n LocalVideoTrack,\n LocalAudioTrack,\n createLocalVideoTrack,\n createLocalAudioTrack,\n} from 'livekit-client';\nimport { usePreviewTracks } from '@livekit/components-react';\nimport {\n useVideoBlur,\n useResolveInitiallyDefaultDeviceId,\n usePersistentUserChoices,\n} from '@xipkg/calls-hooks';\n\nexport const PreJoin = () => {\n const {\n userChoices: { audioEnabled, videoEnabled, audioDeviceId, videoDeviceId },\n saveAudioInputDeviceId,\n saveVideoInputDeviceId,\n } = usePersistentUserChoices();\n\n const initialUserChoices = useRef<{\n audioEnabled: boolean;\n videoEnabled: boolean;\n audioDeviceId: string;\n videoDeviceId: string;\n } | null>(null);\n\n // Сохраняем начальные настройки пользователя\n if (initialUserChoices.current === null) {\n initialUserChoices.current = {\n audioEnabled,\n videoEnabled,\n audioDeviceId,\n videoDeviceId,\n };\n }\n\n const onError = useCallback((e: Error) => {\n console.error('PreJoin ERROR:', e);\n }, []);\n\n // Автоматически запрашиваем разрешения при загрузке\n useEffect(() => {\n const requestPermissions = async () => {\n try {\n // Проверяем, есть ли уже разрешения\n const permissions = await navigator.permissions.query({ name: 'camera' as PermissionName });\n if (permissions.state === 'prompt') {\n // Запрашиваем разрешения\n const stream = await navigator.mediaDevices.getUserMedia({\n video: true,\n audio: true,\n });\n // Останавливаем поток, нам нужны только разрешения\n stream.getTracks().forEach((track) => track.stop());\n }\n } catch (error) {\n console.log('Permission request failed:', error);\n }\n };\n\n requestPermissions();\n }, []);\n\n // Preview треки - создаются только если пользователь изначально включил их\n const tracks = usePreviewTracks(\n {\n audio: !!initialUserChoices.current &&\n initialUserChoices.current?.audioEnabled && {\n deviceId: initialUserChoices.current.audioDeviceId,\n },\n video: !!initialUserChoices.current &&\n initialUserChoices.current?.videoEnabled && {\n deviceId: initialUserChoices.current.videoDeviceId,\n },\n },\n onError,\n );\n\n // Динамические треки - создаются \"just-in-time\" когда пользователь включает их\n const [dynamicVideoTrack, setDynamicVideoTrack] = useState<LocalVideoTrack | null>(null);\n const [dynamicAudioTrack, setDynamicAudioTrack] = useState<LocalAudioTrack | null>(null);\n\n const previewVideoTrack = useMemo(\n () => tracks?.filter((track) => track.kind === Track.Kind.Video)[0] as LocalVideoTrack,\n [tracks],\n );\n\n const previewAudioTrack = useMemo(\n () => tracks?.filter((track) => track.kind === Track.Kind.Audio)[0] as LocalAudioTrack,\n [tracks],\n );\n\n // Создаем динамический видео трек если пользователь включил камеру после загрузки\n useEffect(() => {\n const createVideoTrack = async () => {\n try {\n const track = await createLocalVideoTrack({\n deviceId: { exact: videoDeviceId },\n });\n setDynamicVideoTrack(track);\n } catch (error) {\n onError(error as Error);\n }\n };\n\n if (\n videoEnabled &&\n !initialUserChoices.current?.videoEnabled &&\n !previewVideoTrack &&\n !dynamicVideoTrack\n ) {\n createVideoTrack();\n }\n }, [videoEnabled, videoDeviceId, previewVideoTrack, dynamicVideoTrack, onError]);\n\n // Создаем динамический аудио трек если пользователь включил микрофон после загрузки\n useEffect(() => {\n const createAudioTrack = async () => {\n try {\n const track = await createLocalAudioTrack({\n deviceId: { exact: audioDeviceId },\n });\n setDynamicAudioTrack(track);\n } catch (error) {\n onError(error as Error);\n }\n };\n\n if (\n audioEnabled &&\n !initialUserChoices.current?.audioEnabled &&\n !previewAudioTrack &&\n !dynamicAudioTrack\n ) {\n createAudioTrack();\n }\n }, [audioEnabled, audioDeviceId, previewAudioTrack, dynamicAudioTrack, onError]);\n\n // Очистка динамических треков\n useEffect(() => {\n return () => {\n dynamicVideoTrack?.stop();\n };\n }, [dynamicVideoTrack]);\n\n useEffect(() => {\n return () => {\n dynamicAudioTrack?.stop();\n };\n }, [dynamicAudioTrack]);\n\n // Финальные треки (динамические имеют приоритет над preview)\n const videoTrack = dynamicVideoTrack || previewVideoTrack;\n const audioTrack = dynamicAudioTrack || previewAudioTrack;\n\n // Отладочная информация\n useEffect(() => {\n console.log('PreJoin tracks debug:', {\n initialUserChoices: initialUserChoices.current,\n videoEnabled,\n audioEnabled,\n videoDeviceId,\n audioDeviceId,\n tracks: tracks?.map((t) => ({ kind: t.kind, enabled: !t.isMuted })),\n previewVideoTrack: previewVideoTrack ? { enabled: !previewVideoTrack.isMuted } : null,\n previewAudioTrack: previewAudioTrack ? { enabled: !previewAudioTrack.isMuted } : null,\n dynamicVideoTrack: dynamicVideoTrack ? { enabled: !dynamicVideoTrack.isMuted } : null,\n dynamicAudioTrack: dynamicAudioTrack ? { enabled: !dynamicAudioTrack.isMuted } : null,\n finalVideoTrack: videoTrack ? { enabled: !videoTrack.isMuted } : null,\n finalAudioTrack: audioTrack ? { enabled: !audioTrack.isMuted } : null,\n });\n }, [\n tracks,\n previewVideoTrack,\n previewAudioTrack,\n dynamicVideoTrack,\n dynamicAudioTrack,\n videoTrack,\n audioTrack,\n videoEnabled,\n audioEnabled,\n videoDeviceId,\n audioDeviceId,\n ]);\n\n // Разрешаем device ID для треков\n useResolveInitiallyDefaultDeviceId(audioDeviceId, audioTrack, saveAudioInputDeviceId);\n useResolveInitiallyDefaultDeviceId(videoDeviceId, videoTrack, saveVideoInputDeviceId);\n\n // Передаем видеотрек для использования блюра\n useVideoBlur(videoTrack);\n\n return (\n <>\n <ScrollArea className=\"h-full w-full\">\n <div className=\"max-xs:p-4 p-4 pt-1\">\n <Header />\n <div className=\"grid grid-cols-1 gap-8 lg:grid-cols-2\">\n <UserTile audioTrack={audioTrack} videoTrack={videoTrack} />\n <MediaDevices audioTrack={audioTrack} videoTrack={videoTrack} />\n </div>\n </div>\n </ScrollArea>\n <PermissionsDialog />\n </>\n );\n};\n","import {\n ControlBarProps,\n useLocalParticipant,\n usePersistentUserChoices,\n useTrackToggle,\n} from '@livekit/components-react';\nimport { LocalAudioTrack, LocalVideoTrack, Track } from 'livekit-client';\nimport { useCallback } from 'react';\nimport { DisconnectButton, ScreenShareButton, WhiteBoardButton, DevicesBar } from '@xipkg/calls-ui';\nimport { ChatButton, useChatStore } from '@xipkg/calls-chat';\nimport { useCallStore, useFeaturesStore } from '@xipkg/calls-store';\nimport { cn } from '@xipkg/utils';\nimport { WhiteBoard } from '@xipkg/icons';\nimport { Tooltip, TooltipContent, TooltipTrigger } from '@xipkg/tooltip';\nimport { Button } from '@xipkg/button';\nimport { useRoom, useCalls, useCallsNavigation } from '@xipkg/calls-providers';\nimport { RaiseHandButton } from '@xipkg/calls-risehand';\n\nexport const BottomBar = ({ saveUserChoices = true }: ControlBarProps) => {\n const { saveAudioInputEnabled, saveVideoInputEnabled } = usePersistentUserChoices({\n preventSave: !saveUserChoices,\n });\n\n const { isMicrophoneEnabled, isCameraEnabled, microphoneTrack, cameraTrack } =\n useLocalParticipant();\n\n const microphoneToggle = useTrackToggle({\n source: Track.Source.Microphone,\n onChange: (enabled: boolean, isUserInitiated: boolean) => {\n if (isUserInitiated) {\n saveAudioInputEnabled(enabled);\n }\n },\n });\n\n const cameraToggle = useTrackToggle({\n source: Track.Source.Camera,\n onChange: (enabled: boolean, isUserInitiated: boolean) => {\n if (isUserInitiated) {\n saveVideoInputEnabled(enabled);\n }\n },\n });\n\n const handleMicrophoneToggle = useCallback(async () => {\n microphoneToggle.toggle();\n }, [microphoneToggle]);\n\n const handleCameraToggle = useCallback(async () => {\n cameraToggle.toggle();\n }, [cameraToggle]);\n\n const { isChatOpen } = useChatStore();\n const { mode, activeBoardId, activeClassroom, token } = useCallStore();\n const updateStore = useCallStore((state) => state.updateStore);\n const { room } = useRoom();\n const navigation = useCallsNavigation();\n\n const { useCurrentUser } = useCalls().auth;\n const { data: user } = useCurrentUser();\n const isTutor = user?.default_layout === 'tutor';\n\n const {\n chat: isChatEnabled,\n raiseHand: isRiseHandEnabled,\n whiteboard: isWhiteboardEnabled,\n } = useFeaturesStore((s) => s.features);\n\n const showBackToBoardButton =\n mode === 'full' &&\n activeBoardId &&\n activeClassroom &&\n room &&\n token &&\n room.state === 'connected';\n\n const handleBackToBoard = () => {\n if (!activeBoardId || !activeClassroom) {\n return;\n }\n\n if (!room || !token || room.state !== 'connected') {\n return;\n }\n\n updateStore('localFullView', false);\n updateStore('mode', 'compact');\n\n navigation.navigateToClassroomBoard(activeClassroom, activeBoardId);\n };\n\n return (\n <div className={cn('relative w-full', isChatOpen && 'invisible sm:visible')}>\n <div className=\"flex w-full flex-row justify-between p-4 pt-1\">\n <div />\n <div className=\"flex flex-row gap-4\">\n <div className=\"bg-gray-0 border-gray-10 flex h-[48px] w-[92px] items-center justify-center gap-1 rounded-[16px] border\">\n <DevicesBar\n microTrack={microphoneTrack?.track as LocalAudioTrack}\n microEnabled={isMicrophoneEnabled}\n microTrackToggle={{\n showIcon: true,\n source: Track.Source.Microphone,\n onChange: handleMicrophoneToggle,\n }}\n videoTrack={cameraTrack?.track as unknown as LocalVideoTrack}\n videoEnabled={isCameraEnabled}\n videoTrackToggle={{\n showIcon: true,\n source: Track.Source.Camera,\n onChange: handleCameraToggle,\n }}\n className=\"relative\"\n />\n </div>\n <div className=\"bg-gray-0 border-gray-10 flex h-[48px] items-center justify-center gap-1 rounded-[16px] border p-1\">\n <ScreenShareButton />\n {isWhiteboardEnabled && isTutor && <WhiteBoardButton />}\n {isChatEnabled && <ChatButton />}\n {isRiseHandEnabled && <RaiseHandButton />}\n </div>\n </div>\n <div className=\"relative flex flex-row items-center justify-center gap-4\">\n {showBackToBoardButton && (\n <Tooltip delayDuration={1000}>\n <TooltipTrigger asChild>\n <Button\n size=\"m\"\n variant=\"default\"\n onClick={handleBackToBoard}\n className=\"bg-brand-100 hover:bg-brand-80 absolute top-1 left-[-132px] m-0 h-10 w-[128px] rounded-xl px-2\"\n data-umami-event=\"call-back-to-board\"\n >\n <WhiteBoard className=\"fill-gray-0 h-5 w-5\" />\n <span className=\"text-gray-0 ml-2\">К доске</span>\n </Button>\n </TooltipTrigger>\n <TooltipContent side=\"top\" align=\"center\">\n Вернуться к доске для совместной работы\n </TooltipContent>\n </Tooltip>\n )}\n <div className=\"bg-gray-0 border-gray-10 flex h-[48px] w-[48px] items-center justify-center gap-1 rounded-[16px] border p-1\">\n <DisconnectButton />\n </div>\n </div>\n </div>\n </div>\n );\n};\n","import { useLocalParticipant } from '@livekit/components-react';\nimport { LocalVideoTrack } from 'livekit-client';\nimport { Chat } from '@xipkg/calls-chat';\nimport { UpBar, VideoGrid, CallsOnboarding } from '@xipkg/calls-ui';\nimport { useCallStore, useFeaturesStore } from '@xipkg/calls-store';\nimport { useVideoBlur, useParticipantJoinSync } from '@xipkg/calls-hooks';\nimport { useHandFocus } from '@xipkg/calls-risehand';\nimport { BottomBar } from '../Bottom/BottomBar';\nimport '@xipkg/calls-ui/video-security.css';\n\nexport const ActiveRoom = () => {\n const { chat: isChatEnabled } = useFeaturesStore((s) => s.features);\n\n // Автоматический фокус на участниках с поднятыми руками\n useHandFocus();\n // Синхронизация состояния при подключении новых участников\n useParticipantJoinSync();\n // Получаем видео трек для применения блюра\n const { cameraTrack } = useLocalParticipant();\n const videoTrack = cameraTrack?.track as LocalVideoTrack | undefined;\n\n // Применяем блюр только в полном режиме\n const mode = useCallStore((state) => state.mode);\n const videoTrackForBlur = mode === 'full' ? videoTrack : null;\n useVideoBlur(videoTrackForBlur);\n\n return (\n <div id=\"videoConferenceContainer\" className=\"bg-gray-0 h-full\">\n <div className=\"flex h-full flex-col justify-stretch\">\n <CallsOnboarding />\n <UpBar />\n <div className=\"flex h-full items-center justify-center gap-4 overflow-hidden p-4\">\n <div className=\"flex h-full w-full justify-center text-center text-gray-100\">\n <VideoGrid />\n </div>\n {isChatEnabled && <Chat />}\n </div>\n <BottomBar />\n </div>\n </div>\n );\n};\n","import { useEffect } from 'react';\nimport { useCallsNavigation } from '@xipkg/calls-providers';\nimport { useInitUserDevices, useVideoSecurity } from '@xipkg/calls-hooks';\nimport { useCallStore, useFocusModeStore } from '@xipkg/calls-store';\nimport { PreJoin } from './PreJoin';\nimport { ActiveRoom } from './Room';\nimport '@xipkg/calls-ui/video-security.css';\n\nexport const Call = () => {\n const isStarted = useCallStore((state) => state.isStarted);\n const focusMode = useFocusModeStore((s) => s.focusMode);\n const { pathname } = useCallsNavigation();\n\n useInitUserDevices();\n useVideoSecurity();\n\n const mode = useCallStore((state) => state.mode);\n const updateStore = useCallStore((state) => state.updateStore);\n\n useEffect(() => {\n const isOnCallPage = /^\\/call\\/[^/]+$/.test(pathname);\n\n if (isOnCallPage && mode === 'compact') {\n updateStore('mode', 'full');\n }\n }, [pathname, mode, updateStore]);\n\n return (\n <div\n className=\"h-full\"\n style={\n focusMode\n ? ({\n '--header-height': '0px',\n '--available-height':\n 'calc(100dvh - 0px - var(--upbar-height) - var(--bottom-bar-height))',\n } as React.CSSProperties)\n : undefined\n }\n >\n <div className=\"flex h-full w-full flex-col\">\n {isStarted ? (\n <div id=\"videoConferenceContainer\" className=\"bg-gray-5 h-full\">\n <ActiveRoom />\n </div>\n ) : (\n <PreJoin />\n )}\n </div>\n </div>\n );\n};\n"]}
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@xipkg/calls",
3
+ "version": "0.0.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "development": "./index.ts",
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.mjs",
10
+ "default": "./dist/index.mjs"
11
+ }
12
+ },
13
+ "license": "MIT",
14
+ "scripts": {
15
+ "lint": "eslint \"**/*.{ts,tsx}\"",
16
+ "dev": "tsup --watch",
17
+ "build": "tsup"
18
+ },
19
+ "dependencies": {
20
+ "@dnd-kit/core": "6.3.1",
21
+ "@dnd-kit/modifiers": "9.0.0",
22
+ "@dnd-kit/utilities": "3.2.2",
23
+ "@tanstack/react-router": "1.128.8",
24
+ "@livekit/components-core": "0.12.9",
25
+ "@livekit/components-react": "2.9.14",
26
+ "@livekit/components-styles": "1.1.6",
27
+ "@livekit/krisp-noise-filter": "^0.3.4",
28
+ "@livekit/track-processors": "^0.6.1",
29
+ "livekit-client": "2.15.6",
30
+ "@react-hook/latest": "1.0.3",
31
+ "driver.js": "^1.3.6",
32
+ "@xipkg/avatar": "3.1.0",
33
+ "@xipkg/scrollarea": "2.2.0",
34
+ "@xipkg/label": "2.0.12",
35
+ "@xipkg/alert": "1.1.0",
36
+ "@xipkg/switcher": "3.0.13",
37
+ "@xipkg/select": "2.2.5",
38
+ "@xipkg/tooltip": "2.1.0",
39
+ "@xipkg/icons": "^2.7.0",
40
+ "@xipkg/button": "3.2.0",
41
+ "@xipkg/calls-ui": "*",
42
+ "@xipkg/calls-utils": "*",
43
+ "@xipkg/calls-types": "*",
44
+ "@xipkg/calls-config": "*",
45
+ "@xipkg/calls-providers": "*",
46
+ "@xipkg/calls-store": "*",
47
+ "@xipkg/calls-hooks": "*",
48
+ "@xipkg/calls-chat": "*",
49
+ "@xipkg/calls-risehand": "*"
50
+ },
51
+ "devDependencies": {
52
+ "@tanstack/react-query-devtools": "5.73.3",
53
+ "@eslint/js": "^9.19.0",
54
+ "common.typescript": "*",
55
+ "@types/node": "^20.3.1",
56
+ "@types/react": "^19.0.2",
57
+ "@types/react-dom": "^19.0.2",
58
+ "@xipkg/eslint": "3.2.0",
59
+ "@xipkg/typescript": "latest",
60
+ "common.eslint": "*",
61
+ "eslint": "^9.19.0",
62
+ "eslint-plugin-react-hooks": "^5.0.0",
63
+ "eslint-plugin-react-refresh": "^0.4.18",
64
+ "globals": "^15.14.0",
65
+ "typescript": "~5.7.2",
66
+ "typescript-eslint": "^8.22.0"
67
+ },
68
+ "peerDependencies": {
69
+ "react": "19"
70
+ },
71
+ "description": "calls package",
72
+ "author": "xi.effect",
73
+ "main": "./dist/index.mjs",
74
+ "types": "./dist/index.d.ts",
75
+ "files": [
76
+ "dist"
77
+ ],
78
+ "publishConfig": {
79
+ "access": "public"
80
+ }
81
+ }