This commit is contained in:
2023-09-15 18:07:20 +05:00
parent 9a0dbe390e
commit 16768bfe7d
12 changed files with 607 additions and 241 deletions
+4
View File
@@ -11,11 +11,15 @@
},
"dependencies": {
"@epicgames-ps/lib-pixelstreamingfrontend-ue5.3": "^0.0.2",
"@livekit/components-react": "^1.1.6",
"@livekit/components-styles": "^1.0.6",
"@uidotdev/usehooks": "^2.0.1",
"agora-rtc-sdk-ng": "^4.18.2",
"date-fns": "^2.30.0",
"i18next": "^22.5.0",
"i18next-browser-languagedetector": "^7.0.2",
"ky": "^0.33.3",
"livekit-client": "^1.13.2",
"react": "^18.2.0",
"react-calendar": "^4.3.0",
"react-countdown": "^2.3.5",
+5 -15
View File
@@ -23,20 +23,16 @@ import "react-toastify/dist/ReactToastify.css";
import AlertIcon from "./components/icons/AlertIcon";
function App() {
const [isOpen] = useSidebarStore((state) => [state.isOpen]);
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const [isOpen] = useSidebarStore((state) => [state.isOpen]);
const [loading, setLoading] = useState<boolean>(false);
const [countdownTimer, setCountdownTimer] = useState(10);
// const host = window.location.host;
const [searchParams] = useSearchParams();
// const location = searchParams.get("location") || "a1";
const { t, i18n } = useTranslation();
const location = i18n.language === "ru" ? "a1" : "a2";
const build = searchParams.get("build") || null;
const location =
searchParams.get("location") || (i18n.language === "ru" ? "a1" : "a2");
const [setIsOpenSidebar] = useSidebarStore((state) => [state.setIsOpen]);
function toastError(text: string) {
@@ -241,15 +237,9 @@ function App() {
<div className="p-6 absolute bottom-0 space-y-6">
<div>
<p className="xl:text-2xl text-xl font-gilroy font-semibold">
{/* <Trans i18nKey={"main.cards.title3"}> */}
IMI Saudi Shipyard
{/* </Trans> */}
</p>
<p className="lg:text-sm text-xs">
{/* <Trans i18nKey={"main.cards.city2"}> */}
Saudi Arabia
{/* </Trans> */}
</p>
<p className="lg:text-sm text-xs">Saudi Arabia</p>
</div>
<button
+152 -134
View File
@@ -8,6 +8,7 @@ import QRCode from "react-qr-code";
function MonitoringPage() {
const [sessions, setSessions] = useState<any>([]);
const [servers, setServers] = useState<any>([]);
const [pin, setPin] = useState<string>("");
async function getSessionServers() {
const response = await ky
@@ -55,143 +56,160 @@ function MonitoringPage() {
return (
<div className="min-h-screen text-[#F2F2F2] p-4 flex flex-col">
<div className="flex flex-col gap-2 border-b border-[#22222A] pb-4 text-sm">
<p>Сервера:</p>
<div className="grid lg:grid-cols-3 gap-2">
{servers.length > 0 ? (
servers.map((server: any) => (
<div key={server.id} className="p-4 bg-[#22222A] rounded">
<div>
{differenceInSeconds(
new Date(),
parseISO(server.updatedAt)
) >= 10 ? (
<p className="flex items-center gap-2">
<span className="h-2 w-2 rounded-full bg-red-500"></span>
<span>Не в сети</span>
{pin === "4499" ? (
<>
<div className="flex flex-col gap-2 border-b border-[#22222A] pb-4 text-sm">
<p>Сервера:</p>
<div className="grid lg:grid-cols-3 gap-2">
{servers.length > 0 ? (
servers.map((server: any) => (
<div key={server.id} className="p-4 bg-[#22222A] rounded">
<div>
{differenceInSeconds(
new Date(),
parseISO(server.updatedAt)
) >= 10 ? (
<p className="flex items-center gap-2">
<span className="h-2 w-2 rounded-full bg-red-500"></span>
<span>Не в сети</span>
</p>
) : (
<p className="flex items-center gap-2">
<span className="h-2 w-2 rounded-full bg-green-500"></span>
<span>В сети</span>
</p>
)}
</div>
<p>
Локация:{" "}
<span className="font-medium">{server.location}</span>
</p>
) : (
<p className="flex items-center gap-2">
<span className="h-2 w-2 rounded-full bg-green-500"></span>
<span>В сети</span>
<p>
Имя сервера:{" "}
<span className="font-medium">{server.name}</span>
</p>
)}
</div>
<p>
Локация:{" "}
<span className="font-medium">{server.location}</span>
</p>
<p>
Имя сервера:{" "}
<span className="font-medium">{server.name}</span>
</p>
<p>
Лимит процессов:{" "}
<span className="font-medium">{server.limit_process}</span>
</p>
<div>
<p>
CPU: <span className="font-medium">{server.cpu}</span>
</p>
<p>
RAM: <span className="font-medium">{server.ram}</span>
</p>
{/* <p className="flex space-x-2">
<span>GPU: </span>
<span className="flex space-x-4">
{server.gpu?.map((item: any, index: number) => (
<span key={index}>
{item} {index === 2 && "°C"}
</span>
))}
</span>
</p> */}
</div>
</div>
))
) : (
<p className="opacity-50">Нет запущенных серверов</p>
)}
</div>
</div>
<div className="mt-4 flex flex-col gap-2 text-sm">
<p>Запущенные сессии:</p>
{sessions.length > 0 ? (
sessions.map((session: any) => (
<div
key={session.id}
className="flex flex-wrap gap-4 justify-between items-center p-4 bg-[#22222A] rounded"
>
<div className="flex items-center gap-4">
<div className="">
<QRCode
bgColor="#22222A"
fgColor="#F2F2F2"
size={128}
value={`https://stream.graff.tech/stream/?data=wss://${session.location}.sess.stream.graff.tech/${session.server}/${session.cirrusPort}/`}
viewBox={`0 0 128 128`}
/>
</div>
<div>
<p>
Локация:{" "}
<span className="font-medium">{session.location}</span>
</p>
<p>
Сервер:{" "}
<span className="font-medium">{session.server}</span>
</p>
<p>
Сборка: <span className="font-medium">{session.title}</span>
</p>
<p>
Порт:{" "}
<span className="font-medium">{session.cirrusPort}</span>
</p>
<p>
Пользователи:{" "}
<span className="font-medium">
{session.connectedPlayersCount || 0}
</span>
</p>
<p>
Дата и время запуска:{" "}
<span className="font-medium">
{new Date(session.createdAt).toLocaleString()}
</span>
</p>
</div>
</div>
<div className="grid lg:grid-cols-1 grid-cols-2 gap-4 lg:w-auto w-full">
<a
href={`https://stream.graff.tech/stream/${session.id}`}
target="_blank"
className="inline-block text-center px-4 py-2 bg-blue-600 hover:bg-blue-500 transition-colors rounded"
>
Открыть в новом окне
</a>
<button
onClick={() =>
endActiveSession(
session.location,
session.server,
session.uePort,
session.cirrusPort
)
}
className="px-4 py-2 bg-red-600 hover:bg-red-500 transition-colors rounded"
>
Завершить сессию
</button>
</div>
<p>
Лимит процессов:{" "}
<span className="font-medium">
{server.limit_process}
</span>
</p>
<div>
<p>
CPU: <span className="font-medium">{server.cpu}</span>
</p>
<p>
RAM: <span className="font-medium">{server.ram}</span>
</p>
{/* <p className="flex space-x-2">
<span>GPU: </span>
<span className="flex space-x-4">
{server.gpu?.map((item: any, index: number) => (
<span key={index}>
{item} {index === 2 && "°C"}
</span>
))}
</span>
</p> */}
</div>
</div>
))
) : (
<p className="opacity-50">Нет запущенных серверов</p>
)}
</div>
))
) : (
<p className="opacity-50">Нет запущенных сессий</p>
)}
</div>
</div>
<div className="mt-4 flex flex-col gap-2 text-sm">
<p>Запущенные сессии:</p>
{sessions.length > 0 ? (
sessions.map((session: any) => (
<div
key={session.id}
className="flex flex-wrap gap-4 justify-between items-center p-4 bg-[#22222A] rounded"
>
<div className="flex items-center gap-4">
<div className="">
<QRCode
bgColor="#22222A"
fgColor="#F2F2F2"
size={128}
value={`https://stream.graff.tech/stream/?data=wss://${session.location}.sess.stream.graff.tech/${session.server}/${session.cirrusPort}/`}
viewBox={`0 0 128 128`}
/>
</div>
<div>
<p>
Локация:{" "}
<span className="font-medium">{session.location}</span>
</p>
<p>
Сервер:{" "}
<span className="font-medium">{session.server}</span>
</p>
<p>
Сборка:{" "}
<span className="font-medium">{session.title}</span>
</p>
<p>
Порт:{" "}
<span className="font-medium">
{session.cirrusPort}
</span>
</p>
<p>
Пользователи:{" "}
<span className="font-medium">
{session.connectedPlayersCount || 0}
</span>
</p>
<p>
Дата и время запуска:{" "}
<span className="font-medium">
{new Date(session.createdAt).toLocaleString()}
</span>
</p>
</div>
</div>
<div className="grid lg:grid-cols-1 grid-cols-2 gap-4 lg:w-auto w-full">
<a
href={`https://stream.graff.tech/stream/${session.id}`}
target="_blank"
className="inline-block text-center px-4 py-2 bg-blue-600 hover:bg-blue-500 transition-colors rounded"
>
Открыть в новом окне
</a>
<button
onClick={() =>
endActiveSession(
session.location,
session.server,
session.uePort,
session.cirrusPort
)
}
className="px-4 py-2 bg-red-600 hover:bg-red-500 transition-colors rounded"
>
Завершить сессию
</button>
</div>
</div>
))
) : (
<p className="opacity-50">Нет запущенных сессий</p>
)}
</div>
</>
) : (
<input
autoFocus
type="password"
value={pin}
onChange={(e) => setPin(e.target.value)}
className="w-fit rounded outline-none px-3 py-2"
/>
)}
</div>
);
}
+118 -74
View File
@@ -1,16 +1,14 @@
/* eslint-disable no-irregular-whitespace */
/* eslint-disable react-hooks/exhaustive-deps */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useParams } from "react-router-dom";
import { FullScreen, useFullScreenHandle } from "react-full-screen";
import { useEffect, useState } from "react";
import ky from "ky";
import { io } from "socket.io-client";
import { Socket, io } from "socket.io-client";
import userAgentParser from "ua-parser-js";
import { Player } from "./components/Player";
import { Trans, useTranslation } from "react-i18next";
// import { useScreen } from "usehooks-ts";
import ShareIcon from "./components/icons/ShareIcon";
import ModalContainer from "./components/ModalContainer";
import useModalStore from "./stores/useModalStore";
@@ -25,76 +23,47 @@ import UsersManagementModal from "./components/modals/UsersManagementModal";
import useStreamUserStore from "./stores/useStreamUserStore";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import UserIcon from "./components/icons/UserIcon";
import HandOnIcon from "./components/icons/HandOnIcon";
// import UserIcon from "./components/icons/UserIcon";
// import HandOnIcon from "./components/icons/HandOnIcon";
import AlertIcon from "./components/icons/AlertIcon";
import useSocketStore from "./stores/useSocketStore";
import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react";
import ToggleMic from "./components/ToggleMic";
function StreamPage() {
const { t } = useTranslation();
const params = useParams();
const handleFullScreen = useFullScreenHandle();
const [streamUrl, setStreamUrl] = useState<string>("");
const [isStreamEnded, setIsStreamEnded] = useState<boolean>(false);
const [isStreamLoaded, setStreamLoaded] = useState<boolean>(false);
const [socket, setSocket] = useState<any>(null);
const [users, setUsers] = useStreamUserStore((state) => [
state.users,
state.setUsers,
]);
const [me, setMe] = useState<any>({});
// const screen = useScreen();
const [modal, setModal] = useModalStore((state) => [
state.modal,
state.setModal,
]);
const params = useParams();
const roomId = params.id;
const serverUrl = "wss://livekit.stream.graff.tech";
const [token, setToken] = useState<string>();
const [socket, setSocket] = useSocketStore((state) => [
state.socket,
state.setSocket,
]);
const [users, setUsers] = useStreamUserStore((state) => [
state.users,
state.setUsers,
]);
// useEffect(() => {
// if (screen?.orientation.type.includes("portrait")) {
// alert("portrait");
// }
// }, [screen]);
async function getToken() {
if (!socket) return;
function toastWarn(text: string) {
toast.warn(text, {
position: "top-center",
autoClose: 2000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
theme: "dark",
icon: <AlertIcon />,
closeButton: false,
});
}
const { token }: any = await ky
.get(
`https://coord.graff.tech/getToken?roomName=${roomId}&participantName=${socket.id}`
)
.json();
function toastUser(text: string) {
toast.info(text, {
position: "top-center",
autoClose: 2000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
theme: "dark",
icon: <UserIcon />,
closeButton: false,
});
}
function toastHandOn(text: string) {
toast.info(text, {
position: "top-center",
autoClose: 2000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
theme: "dark",
icon: <HandOnIcon />,
closeButton: false,
});
setToken(token);
}
async function connect() {
@@ -113,35 +82,89 @@ function StreamPage() {
}
}
function update(socketId: string, params: object) {
socket.emit("update", socketId, params);
function update(socketId: string, data: { [key: string]: any }) {
if (!socket) return;
socket.emit("update", socketId, data);
console.log("Users: ", users);
}
function kick(socketId: string) {
if (!socket) return;
socket.emit("kick", socketId);
console.log("User kick: ", socketId);
}
function toastWarn(text: string) {
toast.warn(text, {
position: "top-center",
autoClose: 2000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
theme: "dark",
icon: <AlertIcon />,
closeButton: false,
});
}
// function toastUser(text: string) {
// toast.info(text, {
// position: "top-center",
// autoClose: 2000,
// hideProgressBar: true,
// closeOnClick: true,
// pauseOnHover: true,
// draggable: true,
// theme: "dark",
// icon: <UserIcon />,
// closeButton: false,
// });
// }
// function toastHandOn(text: string) {
// toast.info(text, {
// position: "top-center",
// autoClose: 2000,
// hideProgressBar: true,
// closeOnClick: true,
// pauseOnHover: true,
// draggable: true,
// theme: "dark",
// icon: <HandOnIcon />,
// closeButton: false,
// });
// }
useEffect(() => {
connect();
const socket = io(import.meta.env.VITE_COORD_URL, {
query: { roomId: params.id },
const socket: Socket = io(import.meta.env.VITE_COORD_URL, {
query: {
roomId,
},
});
socket.on("connect", () => {
console.log("Socket: ", socket.id);
setSocket(socket);
// console.log("connect: ", socket.id);
});
socket.on("join", (_socketId, room) => {
setUsers(room.users);
// console.log("join: ", _socketId, room.users);
toastUser(t("notification.newMember"));
socket.on("join", (socketId, sockets) => {
console.log("User connected: ", socketId, sockets);
setUsers(sockets);
});
socket.on("update", (_socketId, room) => {
setUsers(room.users);
// console.log("update: ", _socketId, room.users);
socket.on("update", (socketId, data, sockets) => {
console.log("Update users: ", socketId, data, sockets);
setUsers(sockets);
});
socket.on("leave", (socketId, sockets) => {
console.log("User disconnected: ", socketId);
setUsers(sockets);
});
socket.on("kick", () => {
@@ -162,12 +185,20 @@ function StreamPage() {
}, []);
useEffect(() => {
setMe(users.find((user: any) => user.id === socket.id));
if (!socket) return;
getToken();
}, [socket]);
useEffect(() => {
if (socket) {
setMe(users.find((user: any) => user.id === socket.id));
}
}, [users]);
useEffect(() => {
if (me && me.allowControl && !me.admin) {
toastHandOn(t("notification.controlReceived"));
// toastHandOn(t("notification.controlReceived"));
}
}, [me]);
@@ -206,7 +237,7 @@ function StreamPage() {
)}
<div className="absolute top-0 left-0 min-h-screen flex flex-col justify-center transition-all">
<div className="flex flex-col gap-2 lg:p-4 p-2">
<div className="flex flex-col gap-4 lg:p-4 p-2">
{new userAgentParser().getDevice().model !== "iPhone" && (
<>
{!handleFullScreen.active ? (
@@ -260,8 +291,8 @@ function StreamPage() {
setModal(
<UsersManagementModal
me={me}
handleUpdate={(socketId, params) => update(socketId, params)}
handleKick={(socketId) => kick(socketId)}
handleUpdate={update}
handleKick={kick}
/>
)
}
@@ -271,7 +302,20 @@ function StreamPage() {
<span className="absolute left-12 top-[50%] -translate-y-[50%] invisible group-hover:visible opacity-0 group-hover:opacity-100 text-xs transition-all px-2 py-1 bg-[#131313] rounded">
<Trans i18nKey={"members"}>Участники</Trans>
</span>
<div className="absolute px-2 py-1 text-xs flex flex-col justify-center items-center rounded-full -top-2 -right-2 bg-[#131313] -z-10">
{users.length}
</div>
</button>
<LiveKitRoom
video={false}
audio={true}
token={token}
serverUrl={serverUrl}
>
<RoomAudioRenderer />
<ToggleMic socket={socket} handleUpdate={update} />
</LiveKitRoom>
</div>
</div>
+65
View File
@@ -0,0 +1,65 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable react-hooks/exhaustive-deps */
import { useRoomContext } from "@livekit/components-react";
import { RoomEvent, Track } from "livekit-client";
import { useEffect, useState } from "react";
import MicroOnIcon from "./icons/MicroOnIcon";
import MicroOffIcon from "./icons/MicroOffIcon";
function ToggleMic({ socket, handleUpdate }: any) {
const room = useRoomContext();
const [muted, setMuted] = useState(false);
function toggleMic(value: boolean) {
const audioTrack = room.localParticipant.getTrack(Track.Source.Microphone);
if (!audioTrack) return;
if (value) {
audioTrack.mute();
} else {
audioTrack.unmute();
}
setMuted(!audioTrack.isMuted);
}
useEffect(() => {
if (!socket) return;
socket.on("update", (socketId: string, data: { [key: string]: any }) => {
if (
socket.id === socketId &&
Object.prototype.hasOwnProperty.call(data, "muted")
) {
toggleMic(data.muted);
}
});
}, [socket]);
useEffect(() => {
room.on(RoomEvent.TrackMuted, (_, participant) => {
console.log(participant);
});
room.on(RoomEvent.TrackUnmuted, (_, participant) => {
console.log(participant);
});
}, []);
return (
<button
onClick={() =>
handleUpdate(room.localParticipant.identity, {
muted: !room.localParticipant.getTrack(Track.Source.Microphone)
?.isMuted,
})
}
className="relative group outline-none bg-[#131313] rounded-full shadow-lg shadow-[#131313] p-2 opacity-90"
>
{!muted ? <MicroOnIcon /> : <MicroOffIcon />}
</button>
);
}
export default ToggleMic;
+33
View File
@@ -0,0 +1,33 @@
function MicroOffIcon() {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g id="Icon/MicroOff">
<path
id="Subtract"
fillRule="evenodd"
clipRule="evenodd"
d="M9 11.9387V12.4C9 13.2837 9.71634 14 10.6 14H11.0613L9 11.9387ZM13.0613 16H10C8.34315 16 7 14.6569 7 13V12C7 11.4477 6.55228 11 6 11C5.44772 11 5 11.4477 5 12V13C5 15.7614 7.23858 18 10 18H11V19.9998H9.6C9.04772 19.9998 8.6 20.4475 8.6 20.9998C8.6 21.5521 9.04772 21.9998 9.6 21.9998H14.4C14.9523 21.9998 15.4 21.5521 15.4 20.9998C15.4 20.4475 14.9523 19.9998 14.4 19.9998H13V18H14C14.3311 18 14.6547 17.9678 14.9678 17.9064L13.0613 16ZM18.37 15.4315L16.8568 13.9184C16.9498 13.6289 17 13.3203 17 13V12C17 11.4477 17.4477 11 18 11C18.5523 11 19 11.4477 19 12V13C19 13.8826 18.7713 14.7118 18.37 15.4315ZM15 12.0615L9 6.06152V4.1C9 3.21634 9.71634 2.5 10.6 2.5H13.4C14.2837 2.5 15 3.21634 15 4.1V12.0615Z"
fill="#F2F2F2"
/>
<rect
id="Rectangle 1100"
x="3.41406"
y="2"
width="26.2811"
height="2"
rx="1"
transform="rotate(45 3.41406 2)"
fill="#E94444"
/>
</g>
</svg>
);
}
export default MicroOffIcon;
+26
View File
@@ -0,0 +1,26 @@
function MicroOnIcon() {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g id="Icon/MicroOn">
<g id="Vector">
<path
d="M10.6 2.5H13.4C14.2837 2.5 15 3.21634 15 4.1V12.4C15 13.2837 14.2837 14 13.4 14H10.6C9.71634 14 9 13.2837 9 12.4V4.1C9 3.21634 9.71634 2.5 10.6 2.5Z"
fill="#F2F2F2"
/>
<path
d="M10 16C8.34315 16 7 14.6569 7 13V12C7 11.4477 6.55228 11 6 11C5.44772 11 5 11.4477 5 12V13C5 15.7614 7.23858 18 10 18H11V19.9998H9.6C9.04772 19.9998 8.6 20.4475 8.6 20.9998C8.6 21.5521 9.04772 21.9998 9.6 21.9998H14.4C14.9523 21.9998 15.4 21.5521 15.4 20.9998C15.4 20.4475 14.9523 19.9998 14.4 19.9998H13V18H14C16.7614 18 19 15.7614 19 13V12C19 11.4477 18.5523 11 18 11C17.4477 11 17 11.4477 17 12V13C17 14.6569 15.6569 16 14 16H10Z"
fill="#F2F2F2"
/>
</g>
</g>
</svg>
);
}
export default MicroOnIcon;
+39 -15
View File
@@ -10,6 +10,8 @@ import UserIcon from "../icons/UserIcon";
import CloseIcon from "../icons/CloseIcon";
import useStreamUserStore from "../../stores/useStreamUserStore";
import { Trans } from "react-i18next";
import MicroOffIcon from "../icons/MicroOffIcon";
import MicroOnIcon from "../icons/MicroOnIcon";
/* eslint-disable @typescript-eslint/no-explicit-any */
interface UsersManagementModalProps {
@@ -48,26 +50,46 @@ function UsersManagementModal({
<span className="text-sm">{user.city}</span>
{!user.admin && (
{me && me.admin && (
<>
{!user.allowControl ? (
<button
onClick={() =>
handleUpdate(user.id, { allowControl: true })
}
{!user.admin && (
<>
{!user.allowControl ? (
<button
onClick={() =>
handleUpdate(user.id, { allowControl: true })
}
className="outline-none"
>
<HandOffIcon />
</button>
) : (
<button
onClick={() =>
handleUpdate(user.id, { allowControl: false })
}
className="outline-none"
>
<HandOnIcon />
</button>
)}
</>
)}
{!user.muted ? (
<p
onClick={() => handleUpdate(user.id, { muted: true })}
className="outline-none"
>
<HandOffIcon />
</button>
<MicroOnIcon />
</p>
) : (
<button
onClick={() =>
handleUpdate(user.id, { allowControl: false })
}
<p
onClick={() => handleUpdate(user.id, { muted: false })}
className="outline-none"
>
<HandOnIcon />
</button>
<MicroOffIcon />
</p>
)}
</>
)}
@@ -83,7 +105,9 @@ function UsersManagementModal({
<div>
{me && me.id === user.id && (
<span className="text-[#73788C] text-xs"><Trans i18nKey={"you"}>Вы</Trans></span>
<span className="text-[#73788C] text-xs">
<Trans i18nKey={"you"}>Вы</Trans>
</span>
)}
</div>
</div>
+5
View File
@@ -42,3 +42,8 @@ input {
align-items: center;
border-radius: 9999px;
}
.lk-button {
background: #131313 !important;
@apply w-10 h-10 relative outline-none rounded-full shadow-lg shadow-[#131313] text-white opacity-90 flex flex-col justify-center items-center;
}
-2
View File
@@ -68,8 +68,6 @@ export function ProtectedRoute({ children }: { children: JSX.Element }) {
const accessToken = useAuthStore((state) => state.accessToken);
if (accessToken) {
// console.log("location.pathname", location.pathname);
if (location.pathname === "/personal-area/login") {
return <Navigate to="/personal-area/dashboard" replace />;
} else {
+17
View File
@@ -0,0 +1,17 @@
import { Socket } from "socket.io-client";
import { create } from "zustand";
import { devtools } from "zustand/middleware";
interface SocketState {
socket: Socket | null;
setSocket: (socket: Socket | null) => void;
}
const useSocketStore = create<SocketState>()(
devtools((set) => ({
socket: null,
setSocket: (socket) => set({ socket }),
}))
);
export default useSocketStore;
+143 -1
View File
@@ -28,6 +28,11 @@
dependencies:
regenerator-runtime "^0.13.11"
"@bufbuild/protobuf@^1.3.0":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-1.3.1.tgz#c4de66bacbe7ac97fe054e68314aeba6f45177f9"
integrity sha512-BUyJWutgP2S8K/1NphOJokuwDckXS4qI2T1pGZAlkFdZchWae3jm6fCdkcGbLlM1QLOcNFFePd+7Feo4BYGrJQ==
"@emotion/is-prop-valid@^0.7.3":
version "0.7.3"
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz#a6bf4fa5387cbba59d44e698a4680f481a8da6cc"
@@ -189,6 +194,26 @@
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz"
integrity sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==
"@floating-ui/core@^1.4.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.4.1.tgz#0d633f4b76052668afb932492ac452f7ebe97f17"
integrity sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==
dependencies:
"@floating-ui/utils" "^0.1.1"
"@floating-ui/dom@^1.1.0":
version "1.5.1"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.1.tgz#88b70defd002fe851f17b4a25efb2d3c04d7a8d7"
integrity sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==
dependencies:
"@floating-ui/core" "^1.4.1"
"@floating-ui/utils" "^0.1.1"
"@floating-ui/utils@^0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.1.tgz#1a5b1959a528e374e8037c4396c3e825d6cf4a83"
integrity sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==
"@humanwhocodes/config-array@^0.11.8":
version "0.11.8"
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz"
@@ -245,6 +270,32 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
"@livekit/components-core@0.6.15":
version "0.6.15"
resolved "https://registry.yarnpkg.com/@livekit/components-core/-/components-core-0.6.15.tgz#c64076b3176d51389d100b418c3335705f5a6808"
integrity sha512-M+xJT5pkEzldFHoybN6ttS/1tb+sVI1SBrjLTMBKjeNk7F3Y+XF0sbRY/PtoR1CYz+duPh0NV4DBPy8Fvx84sw==
dependencies:
"@floating-ui/dom" "^1.1.0"
email-regex "^5.0.0"
global-tld-list "^0.0.1139"
loglevel "^1.8.1"
rxjs "^7.8.0"
"@livekit/components-react@^1.1.6":
version "1.1.6"
resolved "https://registry.yarnpkg.com/@livekit/components-react/-/components-react-1.1.6.tgz#4e832570bd3fb0c15e36ca7513f736683de62fd4"
integrity sha512-wA5wKVsKM4cBskXkTbgQ8UhEWCq7hYn/ElOlm9IsGf0U3KpsXpyiW/h2hd/aDr/ufTbBWYjtPWa8RrvoBImnyg==
dependencies:
"@livekit/components-core" "0.6.15"
"@react-hook/latest" "^1.0.3"
clsx "^2.0.0"
usehooks-ts "^2.9.1"
"@livekit/components-styles@^1.0.6":
version "1.0.6"
resolved "https://registry.yarnpkg.com/@livekit/components-styles/-/components-styles-1.0.6.tgz#2649c61a631efff37eb2326e100e34a84e597d71"
integrity sha512-/toY2NFJCU0NdeP9AB+CWW9kPf8gdpndIoR0hYTKjDb8pHPdXDu5NE7XyO8qKIeV4biRFGsQ9+C3giPNgYm9TA==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
@@ -266,6 +317,11 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@react-hook/latest@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@react-hook/latest/-/latest-1.0.3.tgz#c2d1d0b0af8b69ec6e2b3a2412ba0768ac82db80"
integrity sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==
"@remix-run/router@1.6.2":
version "1.6.2"
resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.6.2.tgz"
@@ -520,6 +576,18 @@ acorn@^8.8.0:
resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz"
integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
agora-rtc-sdk-ng@^4.18.2:
version "4.18.2"
resolved "https://registry.yarnpkg.com/agora-rtc-sdk-ng/-/agora-rtc-sdk-ng-4.18.2.tgz#86b0b7fd6763aef776f1d6aaa13c487062ee11f7"
integrity sha512-tvyuXHVL15Eo0lK3wwM3rGPKGPeGYuarAwCs5oyveht1HZO9z3w6vra3d/q3eOCYpvQscCW7P0QNfUIAQTs4RQ==
dependencies:
agora-rte-extension "^1.2.3"
agora-rte-extension@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/agora-rte-extension/-/agora-rte-extension-1.2.3.tgz#979b96df0146300296f9f37212ffa67656c698ef"
integrity sha512-k3yNrYVyzJRoQJjaJUktKUI1XRtf8J1XsW8OzYKFqGlS8WQRMsES1+Phj2rfuEriiLObfuyuCimG6KHQCt5tiw==
ajv@^6.10.0, ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
@@ -665,6 +733,11 @@ clsx@^1.1.1, clsx@^1.2.1:
resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
clsx@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b"
integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz"
@@ -779,6 +852,11 @@ electron-to-chromium@^1.4.284:
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.411.tgz"
integrity sha512-5VXLW4Qw89vM2WTICHua/y8v7fKGDRVa2VPOtBB9IpLvW316B+xd8yD1wTmLPY2ot/00P/qt87xdolj4aG/Lzg==
email-regex@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/email-regex/-/email-regex-5.0.0.tgz#c8b1f4c7f251929b53586a7a3891da09c8dea26d"
integrity sha512-he76Cm8JFxb6OGQHabLBPdsiStgPmJeAEhctmw0uhonUh1pCBsHpI6/rB62s2GNzjBb0YlhIcF/1l9Lp5AfH0Q==
engine.io-client@~6.4.0:
version "6.4.0"
resolved "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz"
@@ -947,6 +1025,11 @@ esutils@^2.0.2:
resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
events@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
@@ -1086,6 +1169,11 @@ glob@^7.1.3:
once "^1.3.0"
path-is-absolute "^1.0.0"
global-tld-list@^0.0.1139:
version "0.0.1139"
resolved "https://registry.yarnpkg.com/global-tld-list/-/global-tld-list-0.0.1139.tgz#70400a3f3ccac1a19a8184274a1b117bc8a27969"
integrity sha512-TCWjAwHPzFV6zbQ5jnJvJTctesHGJr9BppxivRuIxTiIFUzaxy1F0674cxjoJecW5s8V32Q5i35dBFqvAy7eGQ==
globals@^13.19.0:
version "13.20.0"
resolved "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz"
@@ -1428,6 +1516,19 @@ lines-and-columns@^1.1.6:
resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
livekit-client@^1.13.2:
version "1.13.2"
resolved "https://registry.yarnpkg.com/livekit-client/-/livekit-client-1.13.2.tgz#3bc1e280cfc84ba07cf7eb44b388b97cfd96c435"
integrity sha512-NERBPqfinaYVg5rO3NdZTwVKavmgsRUi1SGrSJfrNcArExtNSQK7CtktmSZy/J2WjMMJ/iJrYkqJEFvN4CPr7Q==
dependencies:
"@bufbuild/protobuf" "^1.3.0"
events "^3.3.0"
loglevel "^1.8.0"
sdp-transform "^2.14.1"
ts-debounce "^4.0.0"
typed-emitter "^2.1.0"
webrtc-adapter "^8.1.1"
locate-path@^6.0.0:
version "6.0.0"
resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz"
@@ -1445,6 +1546,11 @@ lodash.merge@^4.6.2:
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
loglevel@^1.8.0, loglevel@^1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4"
integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
@@ -1889,6 +1995,13 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
rxjs@^7.5.2, rxjs@^7.8.0:
version "7.8.1"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
dependencies:
tslib "^2.1.0"
scheduler@^0.23.0:
version "0.23.0"
resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz"
@@ -1896,7 +2009,12 @@ scheduler@^0.23.0:
dependencies:
loose-envify "^1.1.0"
sdp@^3.1.0:
sdp-transform@^2.14.1:
version "2.14.1"
resolved "https://registry.yarnpkg.com/sdp-transform/-/sdp-transform-2.14.1.tgz#2bb443583d478dee217df4caa284c46b870d5827"
integrity sha512-RjZyX3nVwJyCuTo5tGPx+PZWkDMCg7oOLpSlhjDdZfwUoNqG1mM8nyj31IGHyaPWXhjbP7cdK3qZ2bmkJ1GzRw==
sdp@^3.1.0, sdp@^3.2.0:
version "3.2.0"
resolved "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz"
integrity sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==
@@ -2065,6 +2183,11 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
ts-debounce@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/ts-debounce/-/ts-debounce-4.0.0.tgz#33440ef64fab53793c3d546a8ca6ae539ec15841"
integrity sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg==
ts-interface-checker@^0.1.9:
version "0.1.13"
resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
@@ -2075,6 +2198,11 @@ tslib@^1.8.1:
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.1.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
tsutils@^3.21.0:
version "3.21.0"
resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz"
@@ -2094,6 +2222,13 @@ type-fest@^0.20.2:
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
typed-emitter@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/typed-emitter/-/typed-emitter-2.1.0.tgz#ca78e3d8ef1476f228f548d62e04e3d4d3fd77fb"
integrity sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==
optionalDependencies:
rxjs "^7.5.2"
typescript@^5.0.2:
version "5.0.4"
resolved "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz"
@@ -2164,6 +2299,13 @@ warning@^4.0.2:
dependencies:
loose-envify "^1.0.0"
webrtc-adapter@^8.1.1:
version "8.2.3"
resolved "https://registry.yarnpkg.com/webrtc-adapter/-/webrtc-adapter-8.2.3.tgz#85e5e52ea68e808be8d6db85e338aa5c95e80022"
integrity sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==
dependencies:
sdp "^3.2.0"
which@^2.0.1:
version "2.0.2"
resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz"