upd
This commit is contained in:
@@ -30,6 +30,7 @@
|
|||||||
"react-qr-code": "^2.0.11",
|
"react-qr-code": "^2.0.11",
|
||||||
"react-router-dom": "^6.11.2",
|
"react-router-dom": "^6.11.2",
|
||||||
"react-timeit": "^1.2.12",
|
"react-timeit": "^1.2.12",
|
||||||
|
"react-timer-hook": "^3.0.7",
|
||||||
"react-toastify": "^9.1.3",
|
"react-toastify": "^9.1.3",
|
||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
"socket.io-client": "^4.6.2",
|
"socket.io-client": "^4.6.2",
|
||||||
|
|||||||
+62
-34
@@ -31,6 +31,19 @@ import { LiveKitRoom, RoomAudioRenderer } from "@livekit/components-react";
|
|||||||
import ToggleMic from "./components/ToggleMic";
|
import ToggleMic from "./components/ToggleMic";
|
||||||
import Chat from "./components/Chat";
|
import Chat from "./components/Chat";
|
||||||
import ChatIcon from "./components/icons/ChatIcon";
|
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() {
|
function StreamPage() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -39,6 +52,8 @@ function StreamPage() {
|
|||||||
const [isStreamEnded, setIsStreamEnded] = useState<boolean>(false);
|
const [isStreamEnded, setIsStreamEnded] = useState<boolean>(false);
|
||||||
const [isStreamLoaded, setStreamLoaded] = useState<boolean>(false);
|
const [isStreamLoaded, setStreamLoaded] = useState<boolean>(false);
|
||||||
const [me, setMe] = useState<any>({});
|
const [me, setMe] = useState<any>({});
|
||||||
|
const meRef = useRef<any>({});
|
||||||
|
meRef.current = me;
|
||||||
const [modal, setModal] = useModalStore((state) => [
|
const [modal, setModal] = useModalStore((state) => [
|
||||||
state.modal,
|
state.modal,
|
||||||
state.setModal,
|
state.setModal,
|
||||||
@@ -59,6 +74,7 @@ function StreamPage() {
|
|||||||
const [lastActivity, setLastActivity] = useState<Date>(new Date());
|
const [lastActivity, setLastActivity] = useState<Date>(new Date());
|
||||||
const lastActivityRef = useRef<Date>();
|
const lastActivityRef = useRef<Date>();
|
||||||
lastActivityRef.current = lastActivity;
|
lastActivityRef.current = lastActivity;
|
||||||
|
const [stopwatch, setStopwatch] = useState<string>();
|
||||||
|
|
||||||
async function getToken() {
|
async function getToken() {
|
||||||
if (!socket) return;
|
if (!socket) return;
|
||||||
@@ -72,6 +88,17 @@ function StreamPage() {
|
|||||||
setToken(token);
|
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() {
|
async function connect() {
|
||||||
const activeSession: any = await ky
|
const activeSession: any = await ky
|
||||||
.get(`${import.meta.env.VITE_COORD_URL}/active_sessions/${params.id}`)
|
.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}/`
|
`wss://${activeSession.location}.sess.stream.graff.tech/${activeSession.server}/${activeSession.cirrusPort}/`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
updateStartDate(parseISO(activeSession.createdAt));
|
||||||
|
|
||||||
setStreamLoaded(true);
|
setStreamLoaded(true);
|
||||||
} else {
|
} else {
|
||||||
setIsStreamEnded(true);
|
setIsStreamEnded(true);
|
||||||
@@ -130,47 +159,35 @@ function StreamPage() {
|
|||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// function toastHandOn(text: string) {
|
function toastHandOn(text: string) {
|
||||||
// toast.info(text, {
|
toast.info(text, {
|
||||||
// position: "top-center",
|
position: "top-center",
|
||||||
// autoClose: 2000,
|
autoClose: 2000,
|
||||||
// hideProgressBar: true,
|
hideProgressBar: true,
|
||||||
// closeOnClick: true,
|
closeOnClick: true,
|
||||||
// pauseOnHover: true,
|
pauseOnHover: true,
|
||||||
// draggable: true,
|
draggable: true,
|
||||||
// theme: "dark",
|
theme: "dark",
|
||||||
// icon: <HandOnIcon />,
|
icon: <HandOnIcon />,
|
||||||
// closeButton: false,
|
closeButton: false,
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
|
|
||||||
const handleAction = () => {
|
const handleAction = () => {
|
||||||
setLastActivity(new Date());
|
setLastActivity(new Date());
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateLastActivity = async () => {
|
function updateLastActivity() {
|
||||||
console.log("lastActivity: ", lastActivityRef.current);
|
if (!socket) return;
|
||||||
|
|
||||||
try {
|
if (meRef.current && meRef.current.admin) {
|
||||||
const result = await ky
|
socket.emit("last-activity", socket.id, lastActivityRef.current);
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
updateLastActivity();
|
updateLastActivity();
|
||||||
}, 2000);
|
}, 5000);
|
||||||
};
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
connect();
|
connect();
|
||||||
@@ -189,6 +206,7 @@ function StreamPage() {
|
|||||||
socket.on("join", (socketId, sockets) => {
|
socket.on("join", (socketId, sockets) => {
|
||||||
console.log("User connected: ", socketId, sockets);
|
console.log("User connected: ", socketId, sockets);
|
||||||
setUsers(sockets);
|
setUsers(sockets);
|
||||||
|
toastHandOn(t("notification.newMember"));
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("update", (socketId, data, sockets) => {
|
socket.on("update", (socketId, data, sockets) => {
|
||||||
@@ -197,6 +215,7 @@ function StreamPage() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
socket.on("kick", () => {
|
socket.on("kick", () => {
|
||||||
|
socket.close();
|
||||||
window.close();
|
window.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -208,11 +227,15 @@ function StreamPage() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("notification", (type) => {
|
||||||
|
if (type === "afk-timer") {
|
||||||
|
setModal(<AFKTimerModal />);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
document.addEventListener("mousemove", handleAction);
|
document.addEventListener("mousemove", handleAction);
|
||||||
document.addEventListener("touchstart", handleAction);
|
document.addEventListener("touchstart", handleAction);
|
||||||
|
|
||||||
updateLastActivity();
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
socket.off("connect");
|
socket.off("connect");
|
||||||
socket.off("join");
|
socket.off("join");
|
||||||
@@ -230,6 +253,7 @@ function StreamPage() {
|
|||||||
if (!socket) return;
|
if (!socket) return;
|
||||||
|
|
||||||
getToken();
|
getToken();
|
||||||
|
updateLastActivity();
|
||||||
}, [socket]);
|
}, [socket]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -240,7 +264,7 @@ function StreamPage() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (me && me.allowControl && !me.admin) {
|
if (me && me.allowControl && !me.admin) {
|
||||||
// toastHandOn(t("notification.controlReceived"));
|
toastHandOn(t("notification.controlReceived"));
|
||||||
}
|
}
|
||||||
}, [me]);
|
}, [me]);
|
||||||
|
|
||||||
@@ -368,6 +392,10 @@ function StreamPage() {
|
|||||||
<RoomAudioRenderer />
|
<RoomAudioRenderer />
|
||||||
<ToggleMic socket={socket} handleUpdate={update} />
|
<ToggleMic socket={socket} handleUpdate={update} />
|
||||||
</LiveKitRoom>
|
</LiveKitRoom>
|
||||||
|
|
||||||
|
<div className="relative group outline-none w-10 h-10 bg-[#131313] rounded-full shadow-lg shadow-[#131313] p-2 opacity-90 flex justify-center items-center">
|
||||||
|
<p className="text-xs">{stopwatch}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ function Chat({ className, handleClose }: ChatProps) {
|
|||||||
].join(" ")}
|
].join(" ")}
|
||||||
>
|
>
|
||||||
<div className="border-b pb-3 border-y-neutral-800 flex justify-between">
|
<div className="border-b pb-3 border-y-neutral-800 flex justify-between">
|
||||||
<p className="text-2xl ">Chat</p>
|
<p className="text-2xl font-gilroy">Чат</p>
|
||||||
<button onClick={handleClose}>
|
<button onClick={handleClose}>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</button>
|
</button>
|
||||||
@@ -109,7 +109,7 @@ function Chat({ className, handleClose }: ChatProps) {
|
|||||||
>
|
>
|
||||||
{messages.map((data, index) => (
|
{messages.map((data, index) => (
|
||||||
<p key={index} className="break-words">
|
<p key={index} className="break-words">
|
||||||
<b>{data.id}:</b> <span>{data.message}</span>
|
<b>{data.city && data.city + ":"}</b> <span>{data.message}</span>
|
||||||
</p>
|
</p>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ function Chat({ className, handleClose }: ChatProps) {
|
|||||||
<input
|
<input
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Message"
|
placeholder="Написать сообщение..."
|
||||||
autoFocus
|
autoFocus
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
value={message}
|
value={message}
|
||||||
@@ -152,7 +152,7 @@ function Chat({ className, handleClose }: ChatProps) {
|
|||||||
type="submit"
|
type="submit"
|
||||||
className="p-2 bg-blue-700 hover:bg-blue-800 transition-colors outline-none rounded"
|
className="p-2 bg-blue-700 hover:bg-blue-800 transition-colors outline-none rounded"
|
||||||
>
|
>
|
||||||
Send
|
Отправить
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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<number>(60);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setAfkTimer((prev) => prev - 1);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearInterval(interval);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative lg:p-10 p-6 bg-[#131317] rounded shadow-lg w-[320px]">
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<p className="text-2xl">AFK Check</p>
|
||||||
|
<p>Session will end in {afkTimer} seconds</p>
|
||||||
|
<button
|
||||||
|
onClick={() => setModal(null)}
|
||||||
|
className="px-3 py-2 bg-blue-600 hover:bg-blue-700 transition-colors rounded"
|
||||||
|
>
|
||||||
|
I'm here
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AFKTimerModal;
|
||||||
@@ -1912,6 +1912,11 @@ react-timeit@^1.2.12:
|
|||||||
dependencies:
|
dependencies:
|
||||||
react-jss "^10.9.0"
|
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:
|
react-toastify@^9.1.3:
|
||||||
version "9.1.3"
|
version "9.1.3"
|
||||||
resolved "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz"
|
resolved "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz"
|
||||||
|
|||||||
Reference in New Issue
Block a user