-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
diff --git a/packages/pkg.module.board/package.json b/packages/pkg.module.board/package.json
index 1ed4be4b..07fa870e 100644
--- a/packages/pkg.module.board/package.json
+++ b/packages/pkg.module.board/package.json
@@ -8,21 +8,21 @@
"lint": "eslint \"**/*.{ts,tsx}\""
},
"dependencies": {
- "@tldraw/editor": "3.4.0",
+ "@tldraw/editor": "3.6.1",
"@xipkg/button": "2.2.0",
- "@xipkg/dropdown": "^2.4.0",
+ "@xipkg/dropdown": "^2.5.0",
"@xipkg/icons": "1.6.0",
"@xipkg/tooltip": "1.3.0",
"@xipkg/slider": "1.2.0",
"pkg.logo": "*",
"pkg.utils.client": "*",
"sonner": "^1.5.0",
- "tldraw": "^3.4.0",
+ "tldraw": "^3.6.1",
"@tlsync-yjs/core": "0.2.1",
"pkg.utils": "*",
"pkg.stores": "*",
- "@hocuspocus/provider": "2.13.6",
- "yjs": "13.6.19",
+ "@hocuspocus/provider": "2.15.0",
+ "yjs": "13.6.21",
"y-utility": "0.1.4"
},
"devDependencies": {
diff --git a/packages/pkg.module.board/tsconfig.json b/packages/pkg.module.board/tsconfig.json
index ae94cb7e..bb768fa6 100644
--- a/packages/pkg.module.board/tsconfig.json
+++ b/packages/pkg.module.board/tsconfig.json
@@ -2,7 +2,7 @@
"extends": "@xipkg/typescript/react-library.json",
"include": [
"*",
- "components/Header.tsx",
+ "components/Header/Header.tsx",
"components/NavbarAction.tsx",
"components/Navbar.tsx",
"components/ZoomMenu.tsx",
diff --git a/packages/pkg.module.board/useYjsStore.ts b/packages/pkg.module.board/useYjsStore.ts
index ac9946cf..054e1efb 100644
--- a/packages/pkg.module.board/useYjsStore.ts
+++ b/packages/pkg.module.board/useYjsStore.ts
@@ -21,6 +21,7 @@ import { useEffect, useMemo, useState } from 'react';
import { YKeyValue } from 'y-utility/y-keyvalue';
import { HocuspocusProvider } from '@hocuspocus/provider';
import * as Y from 'yjs';
+import { toast } from 'sonner';
export function useYjsStore({
roomId = 'test/slate-yjs-demo',
@@ -64,6 +65,7 @@ export function useYjsStore({
onAuthenticationFailed: (data) => {
console.log('onAuthenticationFailed', data);
if (data.reason === 'permission-denied') {
+ toast('Ошибка доступа к серверу совместного редактирования');
console.error('hocuspocus: permission-denied');
}
},
diff --git a/packages/pkg.module.call/Call.tsx b/packages/pkg.module.call/Call.tsx
deleted file mode 100644
index e296e984..00000000
--- a/packages/pkg.module.call/Call.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-'use client';
-
-import React, { useEffect, useState } from 'react';
-import { Room } from 'livekit-client';
-import { ActiveRoom } from './components/ActiveRoom';
-import { PreJoinSection } from './components/PreJoin';
-
-type CallPropsT = {
- token: string;
-};
-
-export type LocalUserChoiceT = {
- audioEnabled: boolean;
- videoEnabled: boolean;
-};
-
-export const Call = ({ token }: CallPropsT) => {
- const [userChoice, setUserChoice] = useState
(undefined);
- const room = new Room();
- const [connect, setConnect] = useState(false);
- const [isConnected, setIsConnected] = useState(false);
- const [isStarted, setIsStarted] = useState(false);
-
- useEffect(() => {
- setIsStarted(connect);
- }, [isConnected || connect]);
-
- const preJoinDefaults = React.useMemo(
- () => ({
- username: '',
- videoEnabled: true,
- audioEnabled: true,
- }),
- [],
- );
-
- const onSubmit = (userChoices: LocalUserChoiceT) => {
- setUserChoice(userChoices);
- setConnect(true);
- };
-
- return (
-
- {isStarted ? (
-
- ) : (
-
- )}
-
- );
-};
diff --git a/packages/pkg.module.call/app/Call.tsx b/packages/pkg.module.call/app/Call.tsx
new file mode 100644
index 00000000..eca3ab5c
--- /dev/null
+++ b/packages/pkg.module.call/app/Call.tsx
@@ -0,0 +1,30 @@
+import { Room } from 'livekit-client';
+import { useParams } from 'next/navigation';
+import { ActiveRoom } from '../widgets/Room/ActiveRoom';
+import { PreJoin } from '../widgets/PreJoin';
+import { CallProvider } from './provider';
+import { useLivekitToken } from '../shared/hooks';
+import { useCallStore } from '../stores';
+
+export const Call = () => {
+ const room = new Room();
+
+ const isStarted = useCallStore((state) => state.isStarted);
+
+ const params = useParams<{ 'community-id': string; 'channel-id': string }>();
+ const { token } = useLivekitToken(params['community-id'], params['channel-id']);
+
+ return (
+
+
+ {isStarted && token ? (
+
+ ) : (
+
+ )}
+
+
+ );
+};
diff --git a/packages/pkg.module.call/app/index.tsx b/packages/pkg.module.call/app/index.tsx
new file mode 100644
index 00000000..2b4af578
--- /dev/null
+++ b/packages/pkg.module.call/app/index.tsx
@@ -0,0 +1 @@
+export { Call } from './Call';
diff --git a/packages/pkg.module.call/app/provider.tsx b/packages/pkg.module.call/app/provider.tsx
new file mode 100644
index 00000000..07025fe8
--- /dev/null
+++ b/packages/pkg.module.call/app/provider.tsx
@@ -0,0 +1,44 @@
+import React, { ReactNode } from 'react';
+import { useParams } from 'next/navigation';
+import { ErrorPage } from 'pkg.error-page';
+import { useLivekitToken } from '../shared/hooks';
+
+type CallPropsT = {
+ children: ReactNode;
+};
+
+export const CallProvider = ({ children }: CallPropsT) => {
+ const params = useParams<{ 'community-id': string; 'channel-id': string }>();
+ const { token, error } = useLivekitToken(params['community-id'], params['channel-id']);
+
+ if (error) {
+ return (
+
+ );
+ }
+
+ if (!token) {
+ return (
+
+ );
+ }
+
+ return children;
+};
diff --git a/packages/pkg.module.call/components/ActiveRoom.tsx b/packages/pkg.module.call/components/ActiveRoom.tsx
deleted file mode 100644
index 75f11edb..00000000
--- a/packages/pkg.module.call/components/ActiveRoom.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import { LiveKitRoom } from '@livekit/components-react';
-import { UpBar } from './Up';
-import { BottomBar } from './Bottom';
-import { LocalUserChoiceT } from '../Call';
-import { ISettingsRoom } from '../types/types';
-import { VideoConference } from './VideoConference';
-import { serverUrl, serverUrlDev, isDevMode, devToken } from '../config';
-
-export const ActiveRoom = ({
- token,
- room,
- connectInfo,
- isConnectInfo,
- userChoice,
-}: ISettingsRoom & { userChoice: LocalUserChoiceT | undefined }) => {
- const { connect, setConnect } = connectInfo;
- const { isConnected, setIsConnected } = isConnectInfo;
- const handleDisconnect = () => {
- setConnect(false);
- setIsConnected(false);
- };
-
- return (
- setIsConnected(true)}
- onDisconnected={handleDisconnect}
- audio={userChoice?.audioEnabled || false}
- video={userChoice?.videoEnabled || false}
- >
-
-
-
-
- {isConnected && }
-
-
-
-
-
- );
-};
diff --git a/packages/pkg.module.call/components/PreJoin/PreJoinSection.tsx b/packages/pkg.module.call/components/PreJoin/PreJoinSection.tsx
deleted file mode 100644
index bfe2265e..00000000
--- a/packages/pkg.module.call/components/PreJoin/PreJoinSection.tsx
+++ /dev/null
@@ -1,348 +0,0 @@
-/* eslint-disable jsx-a11y/media-has-caption */
-import type {
- CreateLocalTracksOptions,
- LocalAudioTrack,
- LocalTrack,
- LocalVideoTrack,
-} from 'livekit-client';
-import { createLocalTracks, facingModeFromLocalTrack, Track, Mutex } from 'livekit-client';
-import * as React from 'react';
-import { usePersistentUserChoices } from '@livekit/components-react';
-import type { LocalUserChoices } from '@livekit/components-core';
-import { log, defaultUserChoices } from '@livekit/components-core';
-import { Button } from '@xipkg/button';
-import { Avatar, AvatarFallback, AvatarImage } from '@xipkg/avatar';
-import { useMainSt } from 'pkg.stores';
-import { MediaDeviceMenu } from './MediaDeviceMenu';
-import { MessageBeforeJoin } from './MessageBeforeJoin';
-import { DevicesBar } from '../common/DevicesBar';
-import { Header } from './Header';
-
-/**
- * Props for the PreJoin component.
- * @public
- */
-export type PreJoinPropsT = Omit, 'onSubmit' | 'onError'> & {
- connect?: boolean;
- /** This function is called with the `LocalUserChoices` if validation is passed. */
- onSubmit?: (values: LocalUserChoices) => void;
- /**
- * Provide your
- * custom validation function.
- * Only if validation is successful the user choices are past to the onSubmit callback.
- */
- onValidate?: (values: LocalUserChoices) => boolean;
- onError?: (error: Error) => void;
- /** Prefill the input form with initial values. */
- defaults?: Partial;
- /** Display a debug window for your convenience. */
- username?: string;
- /**
- * If true, user choices are persisted across sessions.
- * @defaultValue true
- * @alpha
- */
- persistUserChoices?: boolean;
-};
-
-/** @alpha */
-export const usePreviewTracks = (
- options: CreateLocalTracksOptions,
- onError?: (err: Error) => void,
-) => {
- const [tracks, setTracks] = React.useState();
-
- const trackLock = React.useMemo(() => new Mutex(), []);
-
- React.useEffect(() => {
- let needsCleanup = false;
- let localTracks: Array = [];
- trackLock.lock().then(async (unlock) => {
- try {
- if (options.audio || options.video) {
- localTracks = await createLocalTracks(options);
-
- if (needsCleanup) {
- localTracks.forEach((tr) => tr.stop());
- } else {
- setTracks(localTracks);
- }
- }
- } catch (e: unknown) {
- if (onError && e instanceof Error) {
- onError(e);
- } else {
- log.error(e);
- }
- } finally {
- unlock();
- }
- });
-
- return () => {
- needsCleanup = true;
- localTracks.forEach((track) => {
- track.stop();
- });
- };
- }, [JSON.stringify(options), onError, trackLock]);
-
- return tracks;
-};
-
-/**
- * The `PreJoin` prefab component is normally presented to the user before he enters a room.
- * This component allows the user to check and select
- * the preferred media device (camera und microphone).
- * On submit the user decisions are returned,
- * which can then be passed on to the `LiveKitRoom`
- * so that the user enters the room with the correct media devices.
- *
- * @remarks
- * This component is independent of the `LiveKitRoom` component and should not be nested within it.
- * Because it only access the local media tracks this
- * component is self contained and works without connection to the LiveKit server.
- *
- * @example
- * ```tsx
- *
- * ```
- * @public
- */
-export const PreJoinSection = ({
- defaults = {},
- onValidate,
- onSubmit,
- onError,
- connect = false,
- username = 'username',
- persistUserChoices = true,
-}: PreJoinPropsT) => {
- const [userChoices, setUserChoices] = React.useState(defaultUserChoices);
-
- const partialDefaults: Partial = {
- ...(defaults.audioDeviceId !== undefined && { audioDeviceId: defaults.audioDeviceId }),
- ...(defaults.videoDeviceId !== undefined && { videoDeviceId: defaults.videoDeviceId }),
- ...(defaults.audioEnabled !== undefined && { audioEnabled: defaults.audioEnabled }),
- ...(defaults.videoEnabled !== undefined && { videoEnabled: defaults.videoEnabled }),
- ...(defaults.username !== undefined && { username: defaults.username }),
- };
-
- const {
- userChoices: initialUserChoices,
- saveAudioInputDeviceId,
- saveAudioInputEnabled,
- saveVideoInputDeviceId,
- saveVideoInputEnabled,
- } = usePersistentUserChoices({
- defaults: partialDefaults,
- preventSave: !persistUserChoices,
- preventLoad: !persistUserChoices,
- });
-
- const [permissionByBrowser, setPermissionByBrowser] = React.useState(true);
- // Initialize device settings
- const [audioEnabled, setAudioEnabled] = React.useState(initialUserChoices.audioEnabled);
- const [videoEnabled, setVideoEnabled] = React.useState(initialUserChoices.videoEnabled);
- const [audioDeviceId, setAudioDeviceId] = React.useState(
- initialUserChoices.audioDeviceId,
- );
- const [videoDeviceId, setVideoDeviceId] = React.useState(
- initialUserChoices.videoDeviceId,
- );
-
- // Save user choices to persistent storage.
- React.useEffect(() => {
- saveAudioInputEnabled(audioEnabled);
- }, [audioEnabled, saveAudioInputEnabled]);
- React.useEffect(() => {
- saveVideoInputEnabled(videoEnabled);
- }, [videoEnabled, saveVideoInputEnabled]);
- React.useEffect(() => {
- saveAudioInputDeviceId(audioDeviceId);
- }, [audioDeviceId, saveAudioInputDeviceId]);
- React.useEffect(() => {
- saveVideoInputDeviceId(videoDeviceId);
- }, [videoDeviceId, saveVideoInputDeviceId]);
-
- const tracks = usePreviewTracks(
- {
- audio: audioEnabled ? { deviceId: initialUserChoices.audioDeviceId } : false,
- video: videoEnabled ? { deviceId: initialUserChoices.videoDeviceId } : false,
- },
- onError,
- );
-
- const videoEl = React.useRef(null);
-
- const videoTrack = React.useMemo(
- () => tracks?.filter((track) => track.kind === Track.Kind.Video)[0] as LocalVideoTrack,
- [tracks],
- );
-
- const facingMode = React.useMemo(() => {
- if (videoTrack) {
- const { facingMode } = facingModeFromLocalTrack(videoTrack);
- return facingMode;
- }
- return 'undefined';
- }, [videoTrack]);
-
- const audioTrack = React.useMemo(
- () => tracks?.filter((track) => track.kind === Track.Kind.Audio)[0] as LocalAudioTrack,
- [tracks],
- );
-
- React.useEffect(() => {
- if (videoEl.current && videoTrack) {
- videoTrack.unmute();
- videoTrack.attach(videoEl.current);
- }
-
- return () => {
- videoTrack?.detach();
- };
- }, [videoTrack]);
-
- const handleValidation = React.useCallback(
- (values: LocalUserChoices) => {
- if (typeof onValidate === 'function') {
- return onValidate(values);
- }
- return values.username !== '';
- },
- [onValidate],
- );
-
- React.useEffect(() => {
- const newUserChoices = {
- username,
- videoEnabled,
- videoDeviceId,
- audioEnabled,
- audioDeviceId,
- };
- setUserChoices(newUserChoices);
- }, [username, videoEnabled, handleValidation, audioEnabled, audioDeviceId, videoDeviceId]);
-
- const handleSubmit = (event: React.FormEvent) => {
- event.preventDefault();
- if (handleValidation(userChoices)) {
- if (typeof onSubmit === 'function') {
- onSubmit(userChoices);
- }
- } else {
- log.warn('Validation failed with: ', userChoices);
- }
- };
-
- React.useEffect(() => {
- setPermissionByBrowser(true);
- }, [audioEnabled || videoEnabled]);
-
- const identity = useMainSt((state) => state.user.id);
-
- return (
-
-
-
-
-
- {videoTrack && videoEnabled && (
-
- {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
-
-
- )}
- {(!videoTrack || !videoEnabled) && (
-
- )}
-
-
-
- permissionByBrowser && setAudioEnabled(enabled),
- }}
- videoTrack={videoTrack}
- videoEnabled={videoEnabled}
- videoTrackToggle={{
- initialState: videoEnabled,
- showIcon: false,
- source: Track.Source.Camera,
- onChange: (enabled) => permissionByBrowser && setVideoEnabled(enabled),
- }}
- />
-
-
-
-
-
- {/* eslint-disable-next-line no-nested-ternary */}
- {!permissionByBrowser ? (
-
- ) : !connect ? (
-
- ) : null}
-
-
Камера
- setVideoDeviceId(id)}
- />
-
-
-
Звук
-
- setAudioDeviceId(id)}
- />
- setAudioDeviceId(id)}
- />
-
-
-
-
-
-
-
- );
-};
diff --git a/packages/pkg.module.call/components/PreJoin/index.ts b/packages/pkg.module.call/components/PreJoin/index.ts
deleted file mode 100644
index 3001e4ac..00000000
--- a/packages/pkg.module.call/components/PreJoin/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { PreJoinSection } from './PreJoinSection';
diff --git a/packages/pkg.module.call/components/Up/Settings.tsx b/packages/pkg.module.call/components/Up/Settings.tsx
deleted file mode 100644
index a07cd541..00000000
--- a/packages/pkg.module.call/components/Up/Settings.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-'use client';
-
-import * as React from 'react';
-
-import {
- Sheet,
- SheetClose,
- SheetContent,
- SheetHeader,
- SheetTitle,
- SheetTrigger,
-} from '@xipkg/sheet';
-import { Close } from '@xipkg/icons';
-
-type SettingsPropsT = {
- children: React.ReactNode;
-};
-
-export const Settings = ({ children }: SettingsPropsT) => {
- console.log('');
-
- return (
-
- {children}
-
-
- Настройки
-
-
-
-
-
-
- );
-};
diff --git a/packages/pkg.module.call/components/Participant/ParticipantName.tsx b/packages/pkg.module.call/entities/Participant/ParticipantName.tsx
similarity index 100%
rename from packages/pkg.module.call/components/Participant/ParticipantName.tsx
rename to packages/pkg.module.call/entities/Participant/ParticipantName.tsx
diff --git a/packages/pkg.module.call/components/Participant/ParticipantTile.tsx b/packages/pkg.module.call/entities/Participant/ParticipantTile.tsx
similarity index 99%
rename from packages/pkg.module.call/components/Participant/ParticipantTile.tsx
rename to packages/pkg.module.call/entities/Participant/ParticipantTile.tsx
index e1ee177f..cc2fb5cb 100644
--- a/packages/pkg.module.call/components/Participant/ParticipantTile.tsx
+++ b/packages/pkg.module.call/entities/Participant/ParticipantTile.tsx
@@ -25,8 +25,8 @@ import {
} from '@livekit/components-react';
import { MicrophoneOff, RedLine } from '@xipkg/icons';
import { Avatar, AvatarFallback, AvatarImage } from '@xipkg/avatar';
-import { FocusToggle } from './FocusToggle';
-import '../../utility/style.css';
+import { FocusToggle } from '../../shared/ui';
+import '../../shared/lib';
import { ParticipantName } from './ParticipantName';
type TrackRefContextIfNeededPropsT = {
diff --git a/packages/pkg.module.call/components/Participant/index.ts b/packages/pkg.module.call/entities/Participant/index.ts
similarity index 70%
rename from packages/pkg.module.call/components/Participant/index.ts
rename to packages/pkg.module.call/entities/Participant/index.ts
index 428b406b..271cb849 100644
--- a/packages/pkg.module.call/components/Participant/index.ts
+++ b/packages/pkg.module.call/entities/Participant/index.ts
@@ -1,3 +1,2 @@
export { ParticipantName } from './ParticipantName';
export { ParticipantTile } from './ParticipantTile';
-export { FocusToggle } from './FocusToggle';
diff --git a/packages/pkg.module.call/index.tsx b/packages/pkg.module.call/index.tsx
index 2b4af578..edef48f5 100644
--- a/packages/pkg.module.call/index.tsx
+++ b/packages/pkg.module.call/index.tsx
@@ -1 +1 @@
-export { Call } from './Call';
+export { Call } from './app';
diff --git a/packages/pkg.module.call/package.json b/packages/pkg.module.call/package.json
index 18da9346..dae2d281 100644
--- a/packages/pkg.module.call/package.json
+++ b/packages/pkg.module.call/package.json
@@ -9,11 +9,11 @@
},
"dependencies": {
"@xipkg/avatar": "2.2.0",
- "@livekit/components-react": "2.6.5",
- "@livekit/components-core": "0.11.9",
+ "@livekit/components-react": "2.6.11",
+ "@livekit/components-core": "0.11.11",
"@livekit/components-styles": "1.1.4",
- "@livekit/krisp-noise-filter": "^0.2.12",
- "livekit-client": "2.5.9",
+ "@livekit/krisp-noise-filter": "^0.2.14",
+ "livekit-client": "2.8.0",
"@xipkg/aspect-ratio": "^1.2.0",
"@xipkg/icons": "1.6.0",
"@xipkg/button": "2.2.0",
@@ -24,9 +24,11 @@
"pkg.utils.client": "*",
"pkg.utils": "*",
"pkg.stores": "*",
+ "pkg.error-page": "*",
"@xipkg/utils": "1.2.3",
"framer-motion": "11.3.28",
- "swr": "2.2.5"
+ "swr": "2.2.5",
+ "zustand": "5.0.3"
},
"devDependencies": {
"@types/node": "^20.3.1",
diff --git a/packages/pkg.module.call/shared/hooks/index.ts b/packages/pkg.module.call/shared/hooks/index.ts
new file mode 100644
index 00000000..cec358ea
--- /dev/null
+++ b/packages/pkg.module.call/shared/hooks/index.ts
@@ -0,0 +1,2 @@
+export { useSize } from './useSize';
+export { useLivekitToken } from './useLivekitToken';
diff --git a/packages/pkg.module.call/shared/hooks/useLivekitToken.ts b/packages/pkg.module.call/shared/hooks/useLivekitToken.ts
new file mode 100644
index 00000000..590de711
--- /dev/null
+++ b/packages/pkg.module.call/shared/hooks/useLivekitToken.ts
@@ -0,0 +1,46 @@
+import useSWRSubscription from 'swr/subscription';
+import { useMainSt } from 'pkg.stores';
+
+const subscribeToToken = (
+ key: [string, string],
+ { next }: { next: (error?: Error | null, data?: string | null) => void },
+) => {
+ const { socket } = useMainSt.getState();
+
+ if (!socket) {
+ next(new Error('Socket is not available'));
+ return () => {};
+ }
+
+ // Логика подписки на обновления токена
+ const handleToken = (status: number, data: string) => {
+ if (status === 200) {
+ next(null, data);
+ } else {
+ const error = new Error(`Server Error, ${status}`);
+ error.cause = status;
+ next(error, null);
+ }
+ };
+
+ // Запрос на получение токена
+ socket.emit(
+ 'generate-livekit-token',
+ {
+ community_id: key[0],
+ channel_id: key[1],
+ },
+ handleToken,
+ );
+
+ // Очистка при анмаунте
+ return () => {
+ socket.off('generate-livekit-token', handleToken);
+ };
+};
+
+export const useLivekitToken = (communityId: string, channelId: string) => {
+ const { data: token, error } = useSWRSubscription([communityId, channelId], subscribeToToken);
+
+ return { token, error };
+};
diff --git a/packages/pkg.module.call/utility/useSize.tsx b/packages/pkg.module.call/shared/hooks/useSize.tsx
similarity index 100%
rename from packages/pkg.module.call/utility/useSize.tsx
rename to packages/pkg.module.call/shared/hooks/useSize.tsx
diff --git a/packages/pkg.module.call/utility/getSourceIcon.tsx b/packages/pkg.module.call/shared/lib/getSourceIcon.tsx
similarity index 92%
rename from packages/pkg.module.call/utility/getSourceIcon.tsx
rename to packages/pkg.module.call/shared/lib/getSourceIcon.tsx
index 70245702..aba70100 100644
--- a/packages/pkg.module.call/utility/getSourceIcon.tsx
+++ b/packages/pkg.module.call/shared/lib/getSourceIcon.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { Track } from 'livekit-client';
import { Conference, Microphone, Screenshare } from '@xipkg/icons';
-import { ActionButton } from '../components/common';
+import { ActionButton } from '../ui';
export const getSourceIcon = (source: Track.Source, enabled: boolean) => {
switch (source) {
diff --git a/packages/pkg.module.call/shared/lib/index.ts b/packages/pkg.module.call/shared/lib/index.ts
new file mode 100644
index 00000000..1b7efe3a
--- /dev/null
+++ b/packages/pkg.module.call/shared/lib/index.ts
@@ -0,0 +1 @@
+export { getSourceIcon } from './getSourceIcon';
diff --git a/packages/pkg.module.call/utility/style.css b/packages/pkg.module.call/shared/lib/style.css
similarity index 100%
rename from packages/pkg.module.call/utility/style.css
rename to packages/pkg.module.call/shared/lib/style.css
diff --git a/packages/pkg.module.call/shared/types/index.ts b/packages/pkg.module.call/shared/types/index.ts
new file mode 100644
index 00000000..bdd6b14e
--- /dev/null
+++ b/packages/pkg.module.call/shared/types/index.ts
@@ -0,0 +1 @@
+export { type ConnectT, type IsConnectT, type SettingsRoomT, type LocalUserChoiceT } from './types';
diff --git a/packages/pkg.module.call/shared/types/types.ts b/packages/pkg.module.call/shared/types/types.ts
new file mode 100644
index 00000000..25f30f4a
--- /dev/null
+++ b/packages/pkg.module.call/shared/types/types.ts
@@ -0,0 +1,23 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+export type ConnectT = {
+ setConnect: (arg: boolean) => void;
+ connect: boolean;
+};
+
+export type IsConnectT = {
+ isConnected: boolean;
+ setIsConnected: (arg: boolean) => void;
+};
+
+export type SettingsRoomT = {
+ token: string;
+ room: any;
+ connectInfo: ConnectT;
+ isConnectInfo: IsConnectT;
+};
+
+export type LocalUserChoiceT = {
+ audioEnabled: boolean;
+ videoEnabled: boolean;
+};
diff --git a/packages/pkg.module.call/components/common/ActionButton.tsx b/packages/pkg.module.call/shared/ui/ActionButton/ActionButton.tsx
similarity index 100%
rename from packages/pkg.module.call/components/common/ActionButton.tsx
rename to packages/pkg.module.call/shared/ui/ActionButton/ActionButton.tsx
diff --git a/packages/pkg.module.call/shared/ui/ActionButton/index.ts b/packages/pkg.module.call/shared/ui/ActionButton/index.ts
new file mode 100644
index 00000000..ee0c64cd
--- /dev/null
+++ b/packages/pkg.module.call/shared/ui/ActionButton/index.ts
@@ -0,0 +1 @@
+export { ActionButton } from './ActionButton';
diff --git a/packages/pkg.module.call/components/common/DevicesBar.tsx b/packages/pkg.module.call/shared/ui/DevicesBar/DevicesBar.tsx
similarity index 96%
rename from packages/pkg.module.call/components/common/DevicesBar.tsx
rename to packages/pkg.module.call/shared/ui/DevicesBar/DevicesBar.tsx
index c45d6c68..df037581 100644
--- a/packages/pkg.module.call/components/common/DevicesBar.tsx
+++ b/packages/pkg.module.call/shared/ui/DevicesBar/DevicesBar.tsx
@@ -3,7 +3,7 @@ import { useTrackVolume } from '@livekit/components-react';
import { Conference, Microphone } from '@xipkg/icons';
import { motion } from 'framer-motion';
import { LocalAudioTrack, LocalVideoTrack, Track } from 'livekit-client';
-import { TrackToggle, TrackToggleProps } from '../../utility/TrackToggle';
+import { TrackToggle, TrackToggleProps } from '../TrackToggle/TrackToggle';
type DevicesBarPropsT = {
microTrack: LocalAudioTrack | undefined;
diff --git a/packages/pkg.module.call/shared/ui/DevicesBar/index.ts b/packages/pkg.module.call/shared/ui/DevicesBar/index.ts
new file mode 100644
index 00000000..72783356
--- /dev/null
+++ b/packages/pkg.module.call/shared/ui/DevicesBar/index.ts
@@ -0,0 +1 @@
+export { DevicesBar } from './DevicesBar';
diff --git a/packages/pkg.module.call/components/Participant/FocusToggle.tsx b/packages/pkg.module.call/shared/ui/FocusToggle/FocusToggle.tsx
similarity index 100%
rename from packages/pkg.module.call/components/Participant/FocusToggle.tsx
rename to packages/pkg.module.call/shared/ui/FocusToggle/FocusToggle.tsx
diff --git a/packages/pkg.module.call/shared/ui/FocusToggle/index.ts b/packages/pkg.module.call/shared/ui/FocusToggle/index.ts
new file mode 100644
index 00000000..14db86c3
--- /dev/null
+++ b/packages/pkg.module.call/shared/ui/FocusToggle/index.ts
@@ -0,0 +1 @@
+export { FocusToggle } from './FocusToggle';
diff --git a/packages/pkg.module.call/utility/TrackToggle.tsx b/packages/pkg.module.call/shared/ui/TrackToggle/TrackToggle.tsx
similarity index 94%
rename from packages/pkg.module.call/utility/TrackToggle.tsx
rename to packages/pkg.module.call/shared/ui/TrackToggle/TrackToggle.tsx
index 331331d7..ea830f6c 100644
--- a/packages/pkg.module.call/utility/TrackToggle.tsx
+++ b/packages/pkg.module.call/shared/ui/TrackToggle/TrackToggle.tsx
@@ -1,7 +1,7 @@
import type { CaptureOptionsBySource, ToggleSource } from '@livekit/components-core';
import * as React from 'react';
import { useTrackToggle } from '@livekit/components-react';
-import { getSourceIcon } from './getSourceIcon';
+import { getSourceIcon } from '../../lib';
/** @public */
export type TrackToggleProps = Omit<
diff --git a/packages/pkg.module.call/shared/ui/TrackToggle/index.ts b/packages/pkg.module.call/shared/ui/TrackToggle/index.ts
new file mode 100644
index 00000000..0790a285
--- /dev/null
+++ b/packages/pkg.module.call/shared/ui/TrackToggle/index.ts
@@ -0,0 +1 @@
+export { TrackToggle } from './TrackToggle';
diff --git a/packages/pkg.module.call/components/common/index.ts b/packages/pkg.module.call/shared/ui/index.ts
similarity index 66%
rename from packages/pkg.module.call/components/common/index.ts
rename to packages/pkg.module.call/shared/ui/index.ts
index 8206f3fe..fb508f2a 100644
--- a/packages/pkg.module.call/components/common/index.ts
+++ b/packages/pkg.module.call/shared/ui/index.ts
@@ -1,2 +1,3 @@
-export { ActionButton } from './ActionButton';
export { DevicesBar } from './DevicesBar';
+export { ActionButton } from './ActionButton';
+export { FocusToggle } from './FocusToggle';
diff --git a/packages/pkg.module.call/stores/callStore.ts b/packages/pkg.module.call/stores/callStore.ts
new file mode 100644
index 00000000..48e0acf1
--- /dev/null
+++ b/packages/pkg.module.call/stores/callStore.ts
@@ -0,0 +1,49 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { LiveKitRoomProps } from '@livekit/components-react';
+import { create } from 'zustand';
+import { persist } from 'zustand/middleware';
+
+type useCallStoreT = {
+ // разрешение от браузера на использование камеры
+ isCameraPermission: boolean | null;
+ isMicroPermission: boolean | null;
+ // включён ли у пользователя микро
+ audioEnabled: boolean;
+ videoEnabled: boolean;
+ // id-выбранного устройства
+ audioDeviceId: ConstrainDOMString | undefined;
+ audioOutputDeviceId: ConstrainDOMString | undefined;
+ videoDeviceId: ConstrainDOMString | undefined;
+ // подключена ли конференция
+ connect: LiveKitRoomProps['connect'];
+ // началась ли ВКС для пользователя
+ isStarted: boolean | undefined;
+
+ updateStore: (type: keyof useCallStoreT, value: any) => void;
+};
+
+export const useCallStore = create()(
+ persist(
+ (set) => ({
+ isCameraPermission: null,
+ isMicroPermission: null,
+ audioEnabled: false,
+ videoEnabled: false,
+ audioDeviceId: undefined,
+ audioOutputDeviceId: undefined,
+ videoDeviceId: undefined,
+ connect: undefined,
+ isStarted: undefined,
+ updateStore: (type: keyof useCallStoreT, value: any) => set({ [type]: value }),
+ }),
+ {
+ name: 'call-store', // Название ключа в localStorage
+ partialize: (state) => ({
+ isCameraPermission: state.isCameraPermission,
+ isMicroPermission: state.isMicroPermission,
+ audioEnabled: state.audioEnabled,
+ videoEnabled: state.videoEnabled,
+ }), // Сохраняем только нужные ключи
+ },
+ ),
+);
diff --git a/packages/pkg.module.call/stores/index.ts b/packages/pkg.module.call/stores/index.ts
new file mode 100644
index 00000000..f5c596ac
--- /dev/null
+++ b/packages/pkg.module.call/stores/index.ts
@@ -0,0 +1 @@
+export { useCallStore } from './callStore';
diff --git a/packages/pkg.module.call/tsconfig.json b/packages/pkg.module.call/tsconfig.json
index 101f22f8..0ea3935f 100644
--- a/packages/pkg.module.call/tsconfig.json
+++ b/packages/pkg.module.call/tsconfig.json
@@ -1,5 +1,12 @@
{
"extends": "@xipkg/typescript/react-library.json",
- "include": ["*"],
- "exclude": ["dist", "build", "node_modules"]
+ "include": ["*", "app/Call.tsx", "shared/types"],
+ "exclude": ["dist", "build", "node_modules"],
+ "compilerOptions": {
+ "target": "es2022",
+ "lib": ["es2022", "dom"],
+ "strict": true,
+ "esModuleInterop": true,
+ "skipLibCheck": false
+ }
}
diff --git a/packages/pkg.module.call/types/types.ts b/packages/pkg.module.call/types/types.ts
deleted file mode 100644
index 310157d2..00000000
--- a/packages/pkg.module.call/types/types.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
-
-export interface ISettingsRoom {
- token: string;
- room: any;
- connectInfo: IConnect;
- isConnectInfo: IisConnect;
-}
-
-export interface IisConnect {
- isConnected: boolean;
- setIsConnected: (arg: boolean) => void;
-}
-
-export interface IConnect {
- setConnect: (arg: boolean) => void;
- connect: boolean;
-}
diff --git a/packages/pkg.module.call/components/Bottom/BottomBar.tsx b/packages/pkg.module.call/widgets/Bottom/BottomBar.tsx
similarity index 92%
rename from packages/pkg.module.call/components/Bottom/BottomBar.tsx
rename to packages/pkg.module.call/widgets/Bottom/BottomBar.tsx
index e10c9526..164c82be 100644
--- a/packages/pkg.module.call/components/Bottom/BottomBar.tsx
+++ b/packages/pkg.module.call/widgets/Bottom/BottomBar.tsx
@@ -8,11 +8,11 @@ import {
useLocalParticipantPermissions,
usePersistentUserChoices,
} from '@livekit/components-react';
-import { Chat, Endcall, Group, Hand } from '@xipkg/icons';
+import { Endcall } from '@xipkg/icons';
import { LocalAudioTrack, LocalVideoTrack, Track } from 'livekit-client';
import { supportsScreenSharing } from '@livekit/components-core';
-import { TrackToggle } from '../../utility/TrackToggle';
-import { ActionButton, DevicesBar } from '../common';
+import { TrackToggle } from '../../shared/ui/TrackToggle/TrackToggle';
+import { DevicesBar } from '../../shared/ui';
const DisconnectButton = () => {
const { buttonProps } = useDisconnectButton({});
@@ -112,12 +112,13 @@ export const BottomBar = ({ variation, controls, saveUserChoices = true }: Contr
)}