/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable react-hooks/exhaustive-deps */ import { useEffect, useRef, useState } from "react"; import { PixelStreamingWrapper2 } from "../components/PixelStreamingWrapper2"; import api from "../utils/api"; import { useParams, useSearchParams } from "react-router-dom"; import useStateRef from "react-usestateref"; import Peer from "peerjs"; import useIsAudioActive from "use-is-audio-active"; import { v4 as uuidv4 } from "uuid"; import { io } from "socket.io-client"; import IRemoteStream from "../types/IRemoteStream"; import Video from "../components/Video"; import ModalContainer2 from "../components/ModalContainer2"; import IUser from "../types/IUser"; import useModalStore from "../stores/useModalStore"; import SetNameModal from "../components/modals/stream/SetNameModal"; import useStreamStore from "../stores/useStreamStore"; import Button from "../components/ui/Button"; import HandOnIcon from "../components/icons/HandOnIcon"; import HandOffIcon from "../components/icons/HandOffIcon"; import MicroOnIcon from "../components/icons/MicroOnIcon"; import MicroOffIcon from "../components/icons/MicroOffIcon"; import CameraOnIcon from "../components/icons/CameraOnIcon"; import CameraOffIcon from "../components/icons/CameraOffIcon"; import { Trans, useTranslation } from "react-i18next"; import { isIOS, isMobile, useMobileOrientation } from "react-device-detect"; import WindowIcon from "../components/icons/WindowIcon"; import FullscreenIcon from "../components/icons/FullscreenIcon"; import ShareIcon from "../components/icons/ShareIcon"; import { useFullscreen } from "ahooks"; import InviteModal from "../components/modals/stream/InviteModal"; import Tooltip from "../components/Tooltip"; import { toast, ToastContainer } from "react-toastify"; import LoadingModal from "../components/modals/stream/LoadingModal"; import useSocketStore from "../stores/useSocketStore"; import useStreamUserStore from "../stores/useStreamUserStore"; import Chat2 from "../components/Chat2"; import Rotate64Icon from "../components/icons/Rotate64Icon"; import Draggable from "react-draggable"; import DesktopIcon from "../components/icons/DesktopIcon"; import MobileIcon from "../components/icons/MobileIcon"; import ChatIcon from "../components/icons/ChatIcon"; import useChatStore from "../stores/useChatStore"; import UsersIcon from "../components/icons/UsersIcon"; import Users from "../components/Users"; import SpeedtestModal from "../components/modals/stream/SpeedtestModal"; import InternetSpeedHighIcon from "../components/icons/InternetSpeedHighIcon"; import InternetSpeedLowIcon from "../components/icons/InternetSpeedLowIcon"; import InternetSpeedMediumIcon from "../components/icons/InternetSpeedMediumIcon"; // import ChatIcon from "../components/icons/ChatIcon"; // import MoreIcon from "../components/icons/MoreIcon"; const userId = uuidv4(); function StreamPage() { const { t } = useTranslation(); const params = useParams(); const [searchParams] = useSearchParams(); const [WSUrl, setWSUrl] = useState(""); const localVideoRef = useRef(null); const [localStream, setLocalStream] = useState( new MediaStream() ); const [remoteStreams, setRemoteStreams, remoteStreamsRef] = useStateRef< IRemoteStream[] >([]); const [peerId, setPeerId] = useState(""); const [peerInstance, setPeerInstance] = useState(); const [permission, setPermission] = useState(); const isSpeaking = useIsAudioActive({ source: localStream.getTracks().length ? localStream : null, }); const { me, users, setMe, setUsers } = useStreamUserStore(); const isCallInit = useRef(false); const [roomId] = useState(params.id!); const { socket, setSocket } = useSocketStore(); const { setModal } = useModalStore(); const { name } = useStreamStore(); const [isMicEnabled, setIsMicEnabled] = useState(true); const [isCameraEnabled, setIsCameraEnabled] = useState(true); const [isEnded, setIsEnded] = useState(); const [, setEndAt] = useState(); const fullscreenRef = useRef(null); const [isFullscreen, { toggleFullscreen }] = useFullscreen(fullscreenRef); const [isVideoInitialized, setIsVideoInitialized] = useState(false); const [step, setStep] = useState(1); const [isShowChat, setIsShowChat] = useState(false); const [isShowUsers, setIsShowUsers] = useState(false); const { isPortrait } = useMobileOrientation(); const { setMessages } = useChatStore(); const [activeSession, setActiveSession, activeSessionRef] = useStateRef(); const [downloadSpeed, setDownloadSpeed] = useState(0); async function startCall(remotePeerId: string) { if (!peerInstance) return; console.log("startCall", remotePeerId); const options = { constraints: { offerToReceiveVideo: true, offerToReceiveAudio: true, }, }; const call = peerInstance.call(remotePeerId, localStream, options as any); let accept = true; call.on("stream", (remoteStream) => { if (!accept) return; console.log("setRemoteStreams", remoteStream); setRemoteStreams((prev) => [ ...prev, { peerId: remotePeerId, mediaStream: remoteStream }, ]); accept = false; }); } async function getUserMedia() { try { const mediaStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true, }); if (!localVideoRef.current) return; localVideoRef.current.srcObject = mediaStream; localVideoRef.current.onloadedmetadata = () => { localVideoRef.current?.play(); }; setLocalStream(mediaStream); setPermission(true); console.log("setLocalStream mediaStream", mediaStream); } catch (error) { setPermission(false); console.log("ERROR: ", error); } } function initPeer() { console.log("initPeer"); const peer = new Peer({ host: "stream.graff.tech", config: { iceServers: [ { urls: "turn:185.173.176.83:3478", // Replace with your TURN server URL username: "username1", // Replace with your TURN username credential: "password1", // Replace with your TURN credential }, ], }, }); peer.on("open", (id) => { setPeerId(id); }); peer.on("call", (call) => { call.answer(localStream || undefined); let accept = true; call.on("stream", (remoteStream) => { if (!accept) return; setRemoteStreams((prev) => [ ...prev, { peerId: call.peer, mediaStream: remoteStream }, ]); accept = false; }); }); setPeerInstance(peer); } function initSocket() { const superAdmin = searchParams.has("admin", true); console.log("superAdmin", superAdmin); const socket = io(import.meta.env.VITE_SOCKET_URL, { transports: ["websocket"], auth: { roomId, user: { id: userId, name, peerId, superAdmin, isVideoInitialized, downloadSpeed, }, }, }); socket.on("update", async (users: IUser[]) => { console.log("isCallInit", isCallInit.current); if (!isCallInit.current) { for (const user of users) { if (userId === user.id) continue; await startCall(user.peerId); } isCallInit.current = true; } setMe(users.find((user) => user.id === userId)); setUsers(users); }); socket.on("request-control", (userId) => { const user = users.find((user) => user.id === userId); if (user?.id === me?.id || !me?.isAdmin) return; toast.info(`${user?.name} ${t("toasts.requestPermission")}`); }); socket.on("transfer-control", (userId) => { if (me?.id !== userId) return; toast.info(t("toasts.receivedPermission")); }); socket.on("kick", (userId) => { if (useStreamUserStore.getState().me?.id !== userId) return; window.close(); socket.disconnect(); setPeerInstance(undefined); setWSUrl(""); setUsers([]); setRemoteStreams([]); }); socket.on("message", ({ userId, text }) => { setMessages([...useChatStore.getState().messages, { userId, text }]); }); socket.on("connect", () => { setSocket(socket); }); } function toggleMic() { localStream.getAudioTracks().forEach((track) => { track.enabled = !track.enabled; if (!permission) return; setIsMicEnabled(track.enabled); socket?.emit("mic-enabled", track.enabled); }); } function toggleCamera() { localStream.getVideoTracks().forEach((track) => { track.enabled = !track.enabled; if (!permission) return; setIsCameraEnabled(track.enabled); }); } useEffect(() => { if (!socket) return; console.log("socket", socket); }, [socket?.connected]); function updateRemoteStreams() { setTimeout(() => { console.log("users", users); const newRemoteStreams = remoteStreamsRef.current.filter((remoteStream) => users.some((user) => user.peerId === remoteStream.peerId) ); setRemoteStreams(newRemoteStreams); }, 500); } function handleSetName() { setStep(2); getUserMedia(); } async function getWSUrl() { console.log("activeSession", activeSession); setWSUrl( `wss://${activeSessionRef.current.location}.sess.stream.graff.tech/server/${activeSessionRef.current.localIP}:${activeSessionRef.current.cirrusPort}` ); setModal(); checkSessionStatus(); } function transferControl(userId: string) { socket?.emit("transfer-control", userId); } function requestControl(userId: string) { socket?.emit("request-control", userId); } function kick(userId: string) { socket?.emit("kick", userId); } function videoInitialized() { socket?.emit("video-initialized", userId); } async function getActiveSession() { const activeSession: any = await api .get(`activeSessions/${params.id}`) .json(); if (activeSession?.endAt) { setEndAt(activeSession.endAt); } return activeSession; } async function checkSessionStatus() { const activeSession = await getActiveSession(); if (!activeSession || activeSession.status === "error") { setIsEnded(true); return; } setTimeout(async () => { await checkSessionStatus(); }, 1000); } async function init() { const activeSession = await getActiveSession(); console.log("activeSession init", activeSession); if (!activeSession || activeSession.status === "error") { setIsEnded(true); return; } setActiveSession(activeSession); setIsEnded(false); setModal( { setDownloadSpeed(Math.round(downloadSpeed)); getWSUrl(); }} /> ); } useEffect(() => { init(); // getWSUrl(); // initSocket(); }, []); useEffect(() => { console.log("permission", permission); if (permission === undefined) return; initPeer(); }, [permission]); useEffect(() => { if (step !== 2) return; if (!isVideoInitialized) { setModal(); } else { videoInitialized(); setModal(null); } }, [step, isVideoInitialized]); useEffect(() => { if (!peerId) return; initSocket(); }, [peerId]); useEffect(() => { if (!users.length) return; updateRemoteStreams(); }, [users.length]); useEffect(() => { toggleCamera(); toggleMic(); }, [localStream]); return (
{isEnded === false && ( <>
{/*

{name[0]?.toUpperCase()}

{me?.isControlAllowed && (
)}
*/} {me && (
{(me.downloadSpeed < 10 && ) || (me.downloadSpeed < 20 && ( )) || (me.downloadSpeed >= 20 && )}
)}

{name}

{isMobile ? : }
{permission && ( <>
)}
{/* {me && users.map((user) => { if (user.id !== userId) { return ( transferControl(user.id) } handleKick={() => kick(user.id)} /> ); } })} */}
{!isIOS && (
)}
{WSUrl && ( setIsVideoInitialized(true)} /> )} {step !== 1 && !users.find((user) => user.id === userId)?.isControlAllowed && (
toast.warn(t("toasts.needPermission"))} >
)}

{name}

{remoteStreams.map(({ peerId, mediaStream }) => (
{isShowChat && setIsShowChat(false)} />} {isShowUsers && ( setIsShowUsers(false)} transferControl={(userId) => transferControl(userId)} kick={(userId) => kick(userId)} /> )} {isPortrait && (

Поверните устройство

)} )} {isEnded === true && (

Данная демонстрация была завершена

)}
); } export default StreamPage;