diff --git a/package.json b/package.json index b8b9605..98a81a1 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "react-qr-code": "^2.0.11", "react-router-dom": "^6.11.2", "react-timeit": "^1.2.12", + "react-timer-hook": "^3.0.7", "react-toastify": "^9.1.3", "react-transition-group": "^4.4.5", "socket.io-client": "^4.6.2", diff --git a/src/StreamPage.tsx b/src/StreamPage.tsx index 66ea19f..35ee369 100644 --- a/src/StreamPage.tsx +++ b/src/StreamPage.tsx @@ -31,6 +31,19 @@ import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react"; import ToggleMic from "./components/ToggleMic"; import Chat from "./components/Chat"; import ChatIcon from "./components/icons/ChatIcon"; +import AFKTimerModal from "./components/modals/AFKTimerModal"; +import { + differenceInMilliseconds, + differenceInSeconds, + format, + formatDuration, + getMinutes, + getSeconds, + intervalToDuration, + parseISO, +} from "date-fns"; +import { ru } from "date-fns/locale"; +import HandOnIcon from "./components/icons/HandOnIcon"; function StreamPage() { const { t } = useTranslation(); @@ -39,6 +52,8 @@ function StreamPage() { 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, @@ -59,6 +74,7 @@ function StreamPage() { const [lastActivity, setLastActivity] = useState(new Date()); const lastActivityRef = useRef(); lastActivityRef.current = lastActivity; + const [stopwatch, setStopwatch] = useState(); async function getToken() { if (!socket) return; @@ -72,6 +88,17 @@ function StreamPage() { setToken(token); } + function updateStartDate(date: Date) { + const diffMs = differenceInMilliseconds(new Date(), date); + setStopwatch(format(diffMs, "mm:ss")); + + console.log(); + + setTimeout(() => { + updateStartDate(date); + }, 1000); + } + async function connect() { const activeSession: any = await ky .get(`${import.meta.env.VITE_COORD_URL}/active_sessions/${params.id}`) @@ -82,6 +109,8 @@ function StreamPage() { `wss://${activeSession.location}.sess.stream.graff.tech/${activeSession.server}/${activeSession.cirrusPort}/` ); + updateStartDate(parseISO(activeSession.createdAt)); + setStreamLoaded(true); } else { setIsStreamEnded(true); @@ -130,47 +159,35 @@ function StreamPage() { // }); // } - // 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, - // }); - // } + 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, + }); + } const handleAction = () => { setLastActivity(new Date()); }; - const updateLastActivity = async () => { - console.log("lastActivity: ", lastActivityRef.current); + function updateLastActivity() { + if (!socket) return; - try { - const result = await ky - .put(`${import.meta.env.VITE_COORD_URL}/active_sessions/${params.id}`, { - json: { - lastActivityAt: lastActivityRef.current, - }, - }) - .json(); - - console.log(result); - } catch (error) { - if (error instanceof Error) { - console.log(error.message); - } + if (meRef.current && meRef.current.admin) { + socket.emit("last-activity", socket.id, lastActivityRef.current); } setTimeout(() => { updateLastActivity(); - }, 2000); - }; + }, 5000); + } useEffect(() => { connect(); @@ -189,6 +206,7 @@ function StreamPage() { socket.on("join", (socketId, sockets) => { console.log("User connected: ", socketId, sockets); setUsers(sockets); + toastHandOn(t("notification.newMember")); }); socket.on("update", (socketId, data, sockets) => { @@ -197,6 +215,7 @@ function StreamPage() { }); socket.on("kick", () => { + socket.close(); window.close(); }); @@ -208,11 +227,15 @@ function StreamPage() { ); }); + socket.on("notification", (type) => { + if (type === "afk-timer") { + setModal(); + } + }); + document.addEventListener("mousemove", handleAction); document.addEventListener("touchstart", handleAction); - updateLastActivity(); - return () => { socket.off("connect"); socket.off("join"); @@ -230,6 +253,7 @@ function StreamPage() { if (!socket) return; getToken(); + updateLastActivity(); }, [socket]); useEffect(() => { @@ -240,7 +264,7 @@ function StreamPage() { useEffect(() => { if (me && me.allowControl && !me.admin) { - // toastHandOn(t("notification.controlReceived")); + toastHandOn(t("notification.controlReceived")); } }, [me]); @@ -368,6 +392,10 @@ function StreamPage() { + +
+

{stopwatch}

+
diff --git a/src/components/Chat.tsx b/src/components/Chat.tsx index 5fdfa7b..dfc3a36 100644 --- a/src/components/Chat.tsx +++ b/src/components/Chat.tsx @@ -98,7 +98,7 @@ function Chat({ className, handleClose }: ChatProps) { ].join(" ")} >
-

Chat

+

Чат

@@ -109,7 +109,7 @@ function Chat({ className, handleClose }: ChatProps) { > {messages.map((data, index) => (

- {data.id}: {data.message} + {data.city && data.city + ":"} {data.message}

))} @@ -141,7 +141,7 @@ function Chat({ className, handleClose }: ChatProps) { - Send + Отправить
diff --git a/src/components/modals/AFKTimerModal.tsx b/src/components/modals/AFKTimerModal.tsx new file mode 100644 index 0000000..888b181 --- /dev/null +++ b/src/components/modals/AFKTimerModal.tsx @@ -0,0 +1,34 @@ +import { useEffect, useState } from "react"; +import useModalStore from "../../stores/useModalStore"; + +function AFKTimerModal() { + const setModal = useModalStore((state) => state.setModal); + const [afkTimer, setAfkTimer] = useState(60); + + useEffect(() => { + const interval = setInterval(() => { + setAfkTimer((prev) => prev - 1); + }, 1000); + + return () => { + clearInterval(interval); + }; + }, []); + + return ( +
+
+

AFK Check

+

Session will end in {afkTimer} seconds

+ +
+
+ ); +} + +export default AFKTimerModal; diff --git a/yarn.lock b/yarn.lock index 7def897..8d955ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1912,6 +1912,11 @@ react-timeit@^1.2.12: dependencies: react-jss "^10.9.0" +react-timer-hook@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/react-timer-hook/-/react-timer-hook-3.0.7.tgz#ac42c43d0034b873cbf97b44eb34ccb2b11fe5e0" + integrity sha512-ATpNcU+PQRxxfNBPVqce2+REtjGAlwmfoNQfcEBMZFxPj0r3GYdKhyPHdStvqrejejEi0QvqaJZjy2lBlFvAsA== + react-toastify@^9.1.3: version "9.1.3" resolved "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz"