diff --git a/.env.development b/.env.development index 8e799b2..2542b99 100644 --- a/.env.development +++ b/.env.development @@ -4,4 +4,5 @@ VITE_COORD_URL=https://coord.graff.tech VITE_CRM_API_URL=https://crm.stream.graff.tech/api # VITE_API_URL=http://localhost:5002 VITE_API_URL=https://stream.graff.tech/api -VITE_SOCKET_URL=http://localhost:5003 \ No newline at end of file +# VITE_SOCKET_URL=http://localhost:5003 +VITE_SOCKET_URL=https://stream.graff.tech \ No newline at end of file diff --git a/package.json b/package.json index 80e47e2..17bf60f 100644 --- a/package.json +++ b/package.json @@ -10,17 +10,14 @@ "preview": "vite preview" }, "dependencies": { - "@epicgames-ps/lib-pixelstreamingfrontend-ue5.3": "^1.0.4", - "@epicgames-ps/lib-pixelstreamingfrontend-ue5.5": "^0.0.12", - "@livekit/components-react": "^2.0.3", - "@livekit/components-styles": "^1.0.10", + "@epicgames-ps/lib-pixelstreamingfrontend-ue5.3": "^1.0.6", + "@epicgames-ps/lib-pixelstreamingfrontend-ue5.5": "^0.1.4", "@uidotdev/usehooks": "^2.4.1", "ahooks": "^3.7.10", "date-fns": "^2.30.0", "i18next": "^23.8.2", "i18next-browser-languagedetector": "^7.2.0", "ky": "^1.1.3", - "livekit-client": "^1.13.2", "peerjs": "^1.5.4", "react": "^18.2.0", "react-calendar": "^4.3.0", diff --git a/src/App.tsx b/src/App.tsx index 5f4a3b4..7df5439 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -167,8 +167,8 @@ function App() {

-
-
+ {/*
-
+
*/}
{i18n.language === "ru" ? ( diff --git a/src/StreamPage.tsx b/src/StreamPage.tsx deleted file mode 100644 index a1735a6..0000000 --- a/src/StreamPage.tsx +++ /dev/null @@ -1,418 +0,0 @@ -/* eslint-disable no-irregular-whitespace */ -/* eslint-disable react-hooks/exhaustive-deps */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { useParams, useSearchParams } from "react-router-dom"; -import { FullScreen, useFullScreenHandle } from "react-full-screen"; -import { useEffect, useRef, useState } from "react"; -import ky from "ky"; -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 ShareIcon from "./components/icons/ShareIcon"; -import ModalContainer from "./components/ModalContainer"; -import useModalStore from "./stores/useModalStore"; -import ShareModal from "./components/modals/ShareModal"; -import { Transition } from "react-transition-group"; -import FullscreenIcon from "./components/icons/FullscreenIcon"; -import WindowedModeIcon from "./components/icons/WindowedModeIcon"; -import QRIcon from "./components/icons/QRIcon"; -import QRCodeModal from "./components/modals/QRCodeModal"; -import PersonsIcon from "./components/icons/PersonsIcon"; -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 AlertIcon from "./components/icons/AlertIcon"; -import useSocketStore from "./stores/useSocketStore"; -import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react"; -import Chat from "./components/Chat"; -// import AFKTimerModal from "./components/modals/AFKTimerModal"; -import { differenceInMilliseconds, format, parseISO } from "date-fns"; -import HandOnIcon from "./components/icons/HandOnIcon"; -import { useMobileOrientation } from "react-device-detect"; -import RotateDeviceIcon from "./components/icons/RotateDeviceIcon"; - -function StreamPage() { - const { t } = useTranslation(); - const handleFullScreen = useFullScreenHandle(); - const [streamUrl, setStreamUrl] = useState(""); - const [isStreamEnded, setIsStreamEnded] = useState(false); - const [isStreamLoaded, setStreamLoaded] = useState(false); - const [me, setMe] = useState({}); - const meRef = useRef({}); - meRef.current = me; - const [modal, setModal] = useModalStore((state) => [ - state.modal, - state.setModal, - ]); - const params = useParams(); - const [searchParams] = useSearchParams(); - const roomId = params.id; - const livekitServerUrl = "wss://livekit.stream.graff.tech"; - const [token, setToken] = useState(); - const [socket, setSocket] = useSocketStore((state) => [ - state.socket, - state.setSocket, - ]); - const [users, setUsers] = useStreamUserStore((state) => [ - state.users, - state.setUsers, - ]); - const [isShowChat, setIsShowChat] = useState(false); - const [stopwatch, setStopwatch] = useState(); - const { orientation } = useMobileOrientation(); - - async function getToken() { - if (!socket) return; - - const { token }: any = await ky - .get( - `https://coord.graff.tech/getToken?roomName=${roomId}&participantName=${socket.id}` - ) - .json(); - - setToken(token); - } - - function updateStartDate(date: Date) { - const diffMs = differenceInMilliseconds(new Date(), date); - setStopwatch(format(diffMs, "mm:ss")); - - setTimeout(() => { - updateStartDate(date); - }, 1000); - } - - async function connect() { - const activeSession: any = await ky - .get(`${import.meta.env.VITE_COORD_URL}/active_sessions/${params.id}`) - .json(); - - if (activeSession) { - setStreamUrl( - `wss://${activeSession.location}.sess.stream.graff.tech/${activeSession.server}/${activeSession.cirrusPort}/` - ); - - updateStartDate(parseISO(activeSession.createdAt)); - - setStreamLoaded(true); - } else { - setIsStreamEnded(true); - } - } - - function update(socketId: string, data: { [key: string]: any }) { - if (!socket) return; - - socket.emit("update", socketId, data); - } - - function kick(socketId: string) { - if (!socket) return; - - socket.emit("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: , - 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: , - // 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: , - closeButton: false, - }); - } - - async function checkIsActiveSession() { - const activeSession: any = await ky - .get(`${import.meta.env.VITE_COORD_URL}/active_sessions/${params.id}`) - .json(); - - if (!activeSession) { - setIsStreamEnded(true); - return; - } - - if (!isStreamEnded) { - setTimeout(() => { - checkIsActiveSession(); - }, 1000); - } - } - - useEffect(() => { - connect(); - - const socket: Socket = io(import.meta.env.VITE_COORD_URL, { - query: { - roomId, - admin: searchParams.has("admin", "true"), - }, - }); - - socket.on("connect", () => { - setSocket(socket); - }); - - socket.on("join", (_socketId, sockets) => { - setUsers(sockets); - toastHandOn(t("notification.newMember")); - }); - - socket.on("update", (_socketId, _data, sockets) => { - setUsers(sockets); - }); - - socket.on("kick", () => { - socket.close(); - window.close(); - }); - - socket.on("leave", (socketId) => { - setUsers( - useStreamUserStore - .getState() - .users.filter((user: any) => user.id !== socketId) - ); - }); - - socket.on("notification", (type) => { - if (type === "afk-timer") { - // setModal(); - } - }); - - checkIsActiveSession(); - - return () => { - socket.off("connect"); - socket.off("join"); - socket.off("update"); - socket.off("kick"); - socket.off("leave"); - socket.close(); - }; - }, []); - - useEffect(() => { - 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")); - } - }, [me]); - - return ( - - {!isStreamEnded ? ( - isStreamLoaded ? ( - <> - {orientation === "landscape" ? ( - <>{streamUrl && } - ) : ( -
- - Поверните устройство -
- )} - - ) : ( -
-

- - Ожидание потока -

-
- ) - ) : ( -
-

- Трансляция была завершена -

-
- )} - - {me && !me.allowControl && ( - <> - {new userAgentParser(me.ua).getDevice().type === "mobile" ? ( -
toastWarn(t("notification.getAccess"))} - >
- ) : ( -
toastWarn(t("notification.getAccess"))} - >
- )} - - )} - -
-
- {new userAgentParser().getDevice().model !== "iPhone" && ( - <> - {!handleFullScreen.active ? ( - - ) : ( - - )} - - )} - - - - - - - - {/* */} - - - - - -
-

{stopwatch}

-
-
-
- - - {(state) => ( -
- setIsShowChat(false)} /> -
- )} -
- - - {(state) => } - - - -
- ); -} - -export default StreamPage; diff --git a/src/components/Chat2.tsx b/src/components/Chat2.tsx new file mode 100644 index 0000000..54d51b9 --- /dev/null +++ b/src/components/Chat2.tsx @@ -0,0 +1,86 @@ +import { format } from "date-fns"; +import { t } from "i18next"; +import { Trans } from "react-i18next"; +// import useSocketStore from "../stores/useSocketStore"; +import CloseIcon from "./icons/CloseIcon"; +import SendChatIcon from "./icons/SendChatIcon"; +import Button from "./ui/Button"; +import useStreamUserStore from "../stores/useStreamUserStore"; +import { FormEvent, useRef, useState } from "react"; +import IMessage from "../types/IMessage"; +import useStateRef from "react-usestateref"; + +interface Props { + onClose: () => void; +} + +function Chat2({ onClose }: Props) { +// const { socket } = useSocketStore(); + const { me, users } = useStreamUserStore(); + const [messages] = useStateRef([]); + const messagesRef = useRef(null); + const [messageText, setMessageText] = useState(""); + const messageTextRef = useRef(null); + + function sendMessage(e: FormEvent) { + e.preventDefault(); + } + + return ( +
+
+

+ Чат +

+
+
+ {messages.map((message, index) => ( +
user.id === me?.id)?.name === message.name + ? "bg-[#C4DDF5]" + : "bg-[#F0F1F2]" + }`} + > + {users.find((user) => user.id === me?.id)?.name !== + message.name && ( +

{message.name}

+ )} + +

{message.text}

+

+ {format(new Date(), "HH:mm")} +

+
+ ))} +
+
+
+ setMessageText(e.target.value)} + /> + +
+
+
+ ); +} + +export default Chat2; diff --git a/src/components/ModalContainer2.tsx b/src/components/ModalContainer2.tsx index 6fd0d20..d0d7c0c 100644 --- a/src/components/ModalContainer2.tsx +++ b/src/components/ModalContainer2.tsx @@ -3,9 +3,15 @@ import useModalStore from "../stores/useModalStore"; function ModalContainer2() { const { modal } = useModalStore(); - if (modal) { - return
{modal}
; - } + return ( +
+ {modal} +
+ ); } export default ModalContainer2; diff --git a/src/components/PixelStreamingWrapper2.tsx b/src/components/PixelStreamingWrapper2.tsx index 481b9da..c5c3bda 100644 --- a/src/components/PixelStreamingWrapper2.tsx +++ b/src/components/PixelStreamingWrapper2.tsx @@ -6,7 +6,7 @@ import { Config, AllSettings, PixelStreaming, -} from "@epicgames-ps/lib-pixelstreamingfrontend-ue5.5"; +} from "@epicgames-ps/lib-pixelstreamingfrontend-ue5.3"; export interface PixelStreamingWrapperProps { initialSettings?: Partial; diff --git a/src/components/Tooltip.tsx b/src/components/Tooltip.tsx index 0b52307..c9322fe 100644 --- a/src/components/Tooltip.tsx +++ b/src/components/Tooltip.tsx @@ -4,7 +4,7 @@ interface TooltipProps { function Tooltip({ text }: TooltipProps) { return ( -
+

diff --git a/src/components/modals/stream/SetNameModal.tsx b/src/components/modals/stream/SetNameModal.tsx index 789079f..54e8f6c 100644 --- a/src/components/modals/stream/SetNameModal.tsx +++ b/src/components/modals/stream/SetNameModal.tsx @@ -30,7 +30,7 @@ function SetNameModal({ onAction }: Props) { return (

-
+

Здравствуйте!

Представьтесь, пожалуйста

@@ -46,6 +46,7 @@ function SetNameModal({ onAction }: Props) { onChange={handleChangeName} autoFocus={!name} required + className="max-sm:w-full" />
@@ -54,10 +55,11 @@ function SetNameModal({ onAction }: Props) { type="submit" large onClick={handleClickNoName} + className="max-sm:w-full" > Не указывать -
diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx index 79ba4e2..7d73948 100644 --- a/src/components/ui/Input.tsx +++ b/src/components/ui/Input.tsx @@ -6,6 +6,7 @@ interface InputProps { autoFocus?: boolean; required?: boolean; value?: string; + className?: string; onChange?: (e: ChangeEvent) => void; } @@ -15,13 +16,14 @@ function Input({ autoFocus, required, value, + className, onChange, }: InputProps) { return ( ([]); - const [me, setMe, meRef] = useStateRef(); + const { me, users, setMe, setUsers } = useStreamUserStore(); const isCallInit = useRef(false); const [roomId] = useState(params.id!); - const [socket, setSocket] = useState(); + const { socket, setSocket } = useSocketStore(); const { setModal } = useModalStore(); const { name } = useStreamStore(); const [isMicEnabled, setIsMicEnabled] = useState(true); @@ -72,6 +77,8 @@ function StreamPage3() { const [isFullscreen, { toggleFullscreen }] = useFullscreen(fullscreenRef); const [isVideoInitialized, setIsVideoInitialized] = useState(false); const [step, setStep] = useState(1); + const [isShowChat, setIsShowChat] = useState(false); + const { isPortrait } = useMobileOrientation(); async function startCall(remotePeerId: string) { if (!peerInstance) return; @@ -187,20 +194,20 @@ function StreamPage3() { isCallInit.current = true; } - setUsers(users); setMe(users.find((user) => user.id === userId)); + setUsers(users); }); socket.on("request-control", (userId) => { - const user = usersRef.current.find((user) => user.id === userId); + const user = users.find((user) => user.id === userId); - if (user?.id === meRef.current?.id || !meRef.current?.isAdmin) return; + if (user?.id === me?.id || !me?.isAdmin) return; toast.info(`${user?.name} запрашивает разрешение на управление`); }); socket.on("transfer-control", (userId) => { - if (meRef.current?.id !== userId) return; + if (me?.id !== userId) return; toast.info(`Вы получили разрешение на управление`); }); @@ -336,16 +343,20 @@ function StreamPage3() { return (
- {isEnded === false ? ( + {isEnded === false && ( <> -
-
- +
+
+
-
+

{name[0]?.toUpperCase()} @@ -356,7 +367,7 @@ function StreamPage3() {

{name}

-
+
-
- {users.map((user) => { - if (user.id !== userId) { - return ( -
-
-

- {name[0]?.toUpperCase()} -

- {user?.isControlAllowed && ( -
- )} -
-

{user.name}

+
+
+ {users.map((user) => { + if (user.id !== userId) { + return ( +
+
+

+ {name[0]?.toUpperCase()} +

+ {user?.isControlAllowed && ( +
+ )} +
+

{user.name}

- {me?.isAdmin && me?.isControlAllowed && ( -
- {/*
+ {/*
*/}
- {/*
*/} -
- )} -
- ); - } - })} + )} +
+ ); + } + })} +
-
+
+ {/*
+
*/}