From 65bc4a011a9a8be63d29f16eafbc51636c26d544 Mon Sep 17 00:00:00 2001 From: DmitriyB Date: Wed, 22 Mar 2023 13:54:05 +0500 Subject: [PATCH] refactoring --- public/assets/locales/en/translation.json | 4 +- src/App.css | 45 +- src/App.tsx | 34 +- .../ConnectPage/LoadingPopup/LoadingPopup.tsx | 4 +- .../PopupComponent/PopupComponent.tsx | 10 +- .../ConnectPage/PopupConnect/PopupConnect.tsx | 19 +- .../PlayerComponent/PlayerComponent.tsx | 17 +- .../pages/Stream/Sidebar/Sidebar.tsx | 8 +- .../Stream/SidebarDesktop/SidebarDesktop.tsx | 4 +- .../Stream/SidebarMobile/SidebarMobile.tsx | 12 +- .../WideSidebarButton/WideSidebarButton.tsx | 4 +- src/hooks/useWebRTC.js | 401 ------------------ src/store/reducers/ActionCreator.ts | 2 +- src/utils/app.js | 273 ++++++------ 14 files changed, 226 insertions(+), 611 deletions(-) delete mode 100644 src/hooks/useWebRTC.js diff --git a/public/assets/locales/en/translation.json b/public/assets/locales/en/translation.json index 6cc5227..919dbca 100644 --- a/public/assets/locales/en/translation.json +++ b/public/assets/locales/en/translation.json @@ -24,5 +24,7 @@ "exit-control-btn": "Exit", "popup-control-exit-title": "Are you sure you want to end the demo?", "popup-control-yes": "Finish", - "popup-control-no": "Stay" + "popup-control-no": "Stay", + "popup-loading": "Please, wait", + "sidebar-hide": "Hide menu" } diff --git a/src/App.css b/src/App.css index 899a28f..f11b433 100644 --- a/src/App.css +++ b/src/App.css @@ -25,6 +25,14 @@ z-index: 99; } +.card-title-container { + margin-top: 136px; + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 64px; +} + .card-container { display: flex; @@ -35,14 +43,19 @@ } .card-title { - margin: 28px 0 40px 0; + margin: 0; + width: 496px; + text-align: left; font-style: normal; - font-weight: 400; - font-size: 38px; + font-weight: 300; + font-size: 56px; line-height: 100%; - /* identical to box height, or 38px */ + background: linear-gradient(180deg, #BC75FF 0%, #798FFF 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + text-fill-color: transparent; - color: #ffffff; } .demos_container { @@ -58,9 +71,7 @@ @media screen and (max-width: 1440px) { - .card-title { - margin: 22px 0 40px 0; - } + .card-container { gap: 24px; @@ -69,14 +80,15 @@ } @media screen and (max-width: 1152px) { + .card-title-container { + margin-top: 96px; + } .card-container { gap: 20px; } - .card-title { - margin: 42px 0 40px 0; - } + } @media screen and (max-width: 1024px) { @@ -88,12 +100,15 @@ } @media screen and (max-width: 920px) { - .card-title { - margin: 36px 0 32px 0; + + .card-title-container { + margin-bottom: 40px; + margin-top: 50px; } .card-title { - font-size: 28px; + font-size: 40px; + width: 100%; } .card-container { @@ -106,4 +121,4 @@ width: 100%; border: none; } -} +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 90a4912..496e6a3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,8 @@ import { useEffect } from "react"; import { Redirect, Route, Switch, useHistory } from "react-router-dom"; import { useTranslation } from "react-i18next"; +import useQuery from "hooks/useQuery"; +import cookies from "js-cookie"; import { Header } from "components/shared/Header/Header"; import { Card } from "components/pages/Main/Card/Card"; @@ -15,12 +17,15 @@ import { PlanComponent } from "components/pages/Plan/PlanComponent"; import { useAppDispatch, useAppSelector } from "hooks/redux"; import { fetchCards } from "store/reducers/ActionCreator"; import { cardSlice } from "store/reducers/cardSlice"; +import { sessionSlice } from "store/reducers/sessionSlice"; import { languageSlice } from "store/reducers/languageSlice"; +import { createSession } from "store/reducers/ActionCreator"; + import { ICards } from "models/ICards"; -import useQuery from "hooks/useQuery"; +import { closeStream, load as loadStream } from "utils/app"; + -import cookies from "js-cookie"; @@ -29,9 +34,12 @@ const App: React.FC = () => { const history = useHistory(); const { handleCurrentCard } = cardSlice.actions; const { handleChangeLanguage } = languageSlice.actions; + const { cleanErrors } = sessionSlice.actions; + const { cards, currentCard, error } = useAppSelector((state) => state.cardReducer); const { currentLang } = useAppSelector((state) => state.languageReducer); + const { isLoading } = useAppSelector((state) => state.sessionReducer); const query = useQuery() const langQuery = query.get('lang') @@ -64,7 +72,14 @@ const App: React.FC = () => { }, []) - + const handleConnect = (title: string) => { + dispatch(createSession(title)).unwrap().then((res) => { + history.push(`/stream/${res.session_id}`); + }).catch((res) => { + alert(res); + history.push("/"); + }) + }; useEffect(() => { @@ -80,6 +95,11 @@ const App: React.FC = () => { history.push("/connect-page"); }; + const handleDisconnect = () => { + closeStream() + history.push('/') + } + const { t } = useTranslation(); return ( @@ -87,7 +107,9 @@ const App: React.FC = () => {
-

{error ? error : t("demo-title")}

+
+

{error ? error : t("demo-title")}

+
{cards.map((i: ICards) => ( handleCards(i)} key={i._id} item={i}> @@ -100,7 +122,7 @@ const App: React.FC = () => {
- +
@@ -109,7 +131,7 @@ const App: React.FC = () => { )} - +
diff --git a/src/components/pages/ConnectPage/LoadingPopup/LoadingPopup.tsx b/src/components/pages/ConnectPage/LoadingPopup/LoadingPopup.tsx index bf1b331..62777c7 100644 --- a/src/components/pages/ConnectPage/LoadingPopup/LoadingPopup.tsx +++ b/src/components/pages/ConnectPage/LoadingPopup/LoadingPopup.tsx @@ -2,8 +2,6 @@ import "./LoadingPopup.css"; import { useHistory } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { useAppDispatch, useAppSelector } from "hooks/redux"; - export const LoadingPopup: React.FC = ({ logo }) => { @@ -17,7 +15,7 @@ export const LoadingPopup: React.FC = ({ logo }) => {
лого
- Пожалуйста подождите + {t('popup-loading')}
diff --git a/src/components/pages/ConnectPage/PopupComponent/PopupComponent.tsx b/src/components/pages/ConnectPage/PopupComponent/PopupComponent.tsx index 106d964..7f3017f 100644 --- a/src/components/pages/ConnectPage/PopupComponent/PopupComponent.tsx +++ b/src/components/pages/ConnectPage/PopupComponent/PopupComponent.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { popupAnimation } from "utils/animationProps"; @@ -6,16 +6,13 @@ import { popupAnimation } from "utils/animationProps"; import { PopupConnect } from "components/pages/ConnectPage/PopupConnect/PopupConnect"; import { LoadingPopup } from "components/pages/ConnectPage/LoadingPopup/LoadingPopup"; -import { useAppDispatch, useAppSelector } from "hooks/redux"; +import { useAppDispatch } from "hooks/redux"; import { sessionSlice } from "store/reducers/sessionSlice"; -export const PopupComponent: React.FC = () => { +export const PopupComponent: React.FC = ({isLoading, handleConnect, currentCard, cleanErrors}) => { const dispatch = useAppDispatch(); - const { currentCard } = useAppSelector((state) => state.cardReducer); - const { cleanErrors } = sessionSlice.actions; - const { isLoading } = useAppSelector((state) => state.sessionReducer); @@ -39,6 +36,7 @@ export const PopupComponent: React.FC = () => { title={currentCard.app_title} isLoading={isLoading} logo={currentCard.logo} + handleConnect={handleConnect} > )} diff --git a/src/components/pages/ConnectPage/PopupConnect/PopupConnect.tsx b/src/components/pages/ConnectPage/PopupConnect/PopupConnect.tsx index 4bd028b..7e0531d 100644 --- a/src/components/pages/ConnectPage/PopupConnect/PopupConnect.tsx +++ b/src/components/pages/ConnectPage/PopupConnect/PopupConnect.tsx @@ -7,24 +7,9 @@ import { useAppDispatch } from "hooks/redux"; import { useTranslation } from "react-i18next"; -export const PopupConnect: React.FC = ({ onConnect, logo, isLoading, title }) => { +export const PopupConnect: React.FC = ({ logo, isLoading, title, handleConnect }) => { const history = useHistory(); const { t } = useTranslation(); - const dispatch = useAppDispatch(); - - - const handleConnect = () => { - dispatch(createSession(title)).unwrap().then((res) => { - history.push(`/stream/${res.payload.session_id}`); - }).catch((res) => { - alert(res); - history.push("/"); - }) - }; - - - - console.log(isLoading) return (
@@ -32,7 +17,7 @@ export const PopupConnect: React.FC = ({ onConnect, logo, isLoading, title лого
-
diff --git a/src/components/pages/Stream/PlayerComponent/PlayerComponent.tsx b/src/components/pages/Stream/PlayerComponent/PlayerComponent.tsx index 7a3d207..af338f2 100644 --- a/src/components/pages/Stream/PlayerComponent/PlayerComponent.tsx +++ b/src/components/pages/Stream/PlayerComponent/PlayerComponent.tsx @@ -1,12 +1,10 @@ import "./PlayerStyles.css"; import React, { useEffect, useState } from "react"; -import { useParams, useHistory } from "react-router-dom"; +import { useHistory, useParams } from "react-router-dom"; import { connectSession } from "store/reducers/ActionCreator"; import { useAppDispatch, useAppSelector } from "hooks/redux"; -import { sessionSlice } from "store/reducers/sessionSlice"; import useWindowDimensions from "hooks/useWindowDimensions"; -import { load as loadStream, usersArray } from "utils/app"; import useMobile from "hooks/useMobile"; import { Sidebar } from "components/pages/Stream/Sidebar/Sidebar"; @@ -18,7 +16,7 @@ type link = { -export const PlayerComponent: React.FC = ({ }) => { +export const PlayerComponent: React.FC = ({ cleanErrors, handleDisconnect, loadStream }) => { const { isMobile } = useMobile(); const windowDimensions = useWindowDimensions(); const width = windowDimensions.width; @@ -32,18 +30,19 @@ export const PlayerComponent: React.FC = ({ }) => { const { id } = useParams(); const [click, setClick] = useState(false); const dispatch = useAppDispatch(); - const { cleanErrors } = sessionSlice.actions; + const history = useHistory() useEffect(() => { dispatch(connectSession(id)).unwrap().then(() => { + loadStream() }).catch((res) => { alert(res); - }).finally(() => { - loadStream() - }); + history.push('/') + }) return () => { dispatch(cleanErrors()); + handleDisconnect() window.removeEventListener("change ", (event: any) => { setPopup(false); }); @@ -65,7 +64,6 @@ export const PlayerComponent: React.FC = ({ }) => { return ( <> - {playerCount} {popup && (

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

@@ -73,6 +71,7 @@ export const PlayerComponent: React.FC = ({ }) => { )} = ({ exitPopup, isMobile, heightDevice, players }) => { +export const Sidebar: React.FC = ({ exitPopup, isMobile, heightDevice, players, handleDisconnect }) => { const [isMuted, setMuted] = useState(true); const [isControl, setControl] = useState(false); - const history = useHistory() const { playerCount } = useAppSelector((state) => state.sessionReducer); @@ -22,8 +21,7 @@ export const Sidebar: React.FC = ({ exitPopup, isMobile, heightDevice, play const handleCloseStream = () => { - closeStream() - history.push('/') + handleDisconnect() } const handleMuteClick = () => { @@ -46,11 +44,11 @@ export const Sidebar: React.FC = ({ exitPopup, isMobile, heightDevice, play handleMuteClick={handleMuteClick} handleControlClick={handleControlClick} closeStream={handleCloseStream} + handleOpenExitPopup={handleCloseStream} > ) : ( = ({ return ( <> closeSideBar()} + onHoverStart={() => setWideSidebar(true)} + onHoverEnd={() => setWideSidebar(false)} initial={false} animate={open ? "open" : "closed"} variants={wideSidebar ? setAnimation() : sidebarVariants} @@ -110,7 +111,6 @@ export const SidebarDesktop: React.FC = ({ >
setWideSidebar(true)} className="toolbar-field-part" > = ({ height, isMobile }) => { +export const SidebarMobile: React.FC = ({ height, isMobile, handleOpenExitPopup }) => { const [open, setOpen] = useState(false); const [userList, setUserList] = useState(false); - const [popupAdditional, setPopupAdditipnal] = useState(false); + const [popupAdditional, setPopupAdditipnal] = useState(false); //should be insted of exit button, but popup doesn't ready yet console.log(userList, 'popup') return ( <> = ({ height, isMobile }) => {
- setPopupAdditipnal(true)} - > +
{!open && ( diff --git a/src/components/pages/Stream/WideSidebarButton/WideSidebarButton.tsx b/src/components/pages/Stream/WideSidebarButton/WideSidebarButton.tsx index fd18d42..1de8e18 100644 --- a/src/components/pages/Stream/WideSidebarButton/WideSidebarButton.tsx +++ b/src/components/pages/Stream/WideSidebarButton/WideSidebarButton.tsx @@ -10,8 +10,8 @@ export const WideSidebarButton: React.FC = ({ close, isSidebarWide }) => { const [active, setActive] = useState(false); const [button, setButton] = useState({ icon: wideButton, - inactive: "Скрыть меню", - active: "Скрыть меню", + inactive: "sidebar-hide", + active: "sidebar-hide", type: "fullscreen", noHover: true, }); diff --git a/src/hooks/useWebRTC.js b/src/hooks/useWebRTC.js deleted file mode 100644 index 728b38b..0000000 --- a/src/hooks/useWebRTC.js +++ /dev/null @@ -1,401 +0,0 @@ -import { useEffect, useState, useRef, useCallback } from "react"; -import { ACTIONS } from "../socket/actions"; -import socketInit from "../socket"; -import freeice from "freeice"; -import { useStateWithCallback } from "./useStateWithCallback"; -import { useHistory } from "react-router-dom"; - - -export const useWebRTC = (roomId, user) => { - const history = useHistory(); - const [clients, setClients] = useStateWithCallback([]); - const audioElements = useRef({}); - const [message, setMessages] = useState(""); - const [control, setControl] = useState('') - const [warning, setWarning] = useState(""); - const connections = useRef({}); - const socket = useRef(null); - const localMediaStream = useRef(null); - const clientsRef = useRef(null); - - const addNewClient = useCallback( - (newClient, cb) => { - const lookingFor = clients.find((client) => client.id === newClient.id); - - if (lookingFor === undefined) { - setClients((existingClients) => [...existingClients, newClient], cb); - } - }, - [clients, setClients] - ); - - useEffect(() => { - clientsRef.current = clients; - }, [clients]); - - useEffect(() => { - const initChat = async () => { - socket.current = socketInit(); - console.log(localMediaStream.current) - await captureMedia(); - addNewClient( - { - ...user, - muted: true, - admin: false, - control: false, - }, - () => { - const localElement = audioElements.current[user.id]; - if (localElement) { - localElement.volume = 0; - localElement.srcObject = localMediaStream.current; - } - } - ); - socket.current.on(ACTIONS.MUTE_INFO, ({ userId, isMute, isAdmin }) => { - handleSetMute(isMute, userId); - }); - - socket.current.on(ACTIONS.ADD_PEER, handleNewPeer); - socket.current.on(ACTIONS.REMOVE_PEER, handleRemovePeer); - socket.current.on(ACTIONS.ICE_CANDIDATE, handleIceCandidate); - socket.current.on(ACTIONS.SESSION_DESCRIPTION, setRemoteMedia); - socket.current.on(ACTIONS.MUTE, ({ peerId, userId }) => { - handleSetMute(true, userId); - }); - - socket.current.on("ADMIN-LEFT", ({ isLeft }) => { - setWarning(isLeft); - }); - - socket.current.on("ADMIN-SET", ({ admin }) => { - handleSetAdmin(true, admin); - setControl(admin) - }); - - socket.current.on(ACTIONS.UNMUTE, ({ peerId, userId }) => { - handleSetMute(false, userId); - }); - socket.current.emit(ACTIONS.JOIN, { - roomId, - user, - }); - - socket.current.on("CONTROL-SET", ({ userId }) => { - handleSetControl(userId); - setControl(userId) - }); - - async function captureMedia() { - // Start capturing local audio stream. - localMediaStream.current = await navigator.mediaDevices.getUserMedia({ - audio: true, - }).catch(() => { - alert('Подключите микрофон!') - }); - } - - socket.current.on("REQUEST-CONTROL", ({ userID }) => { - setMessages(userID); - }); - - async function handleNewPeer({ peerId, createOffer, user: remoteUser }) { - if (peerId in connections.current) { - return console.warn( - `You are already connected with ${peerId} (${user.name})` - ); - } - - // Store it to connections - connections.current[peerId] = new RTCPeerConnection({ - iceServers: freeice(), - }); - - // Handle new ice candidate on this peer connection - connections.current[peerId].onicecandidate = (event) => { - socket.current.emit(ACTIONS.RELAY_ICE, { - peerId, - icecandidate: event.candidate, - }); - }; - - // Handle on track event on this connection - connections.current[peerId].ontrack = ({ streams: [remoteStream] }) => { - addNewClient( - { - ...remoteUser, - muted: true, - admin: false, - control: false, - }, - () => { - // get current users mute info - const currentUser = clientsRef.current.find( - (client) => client.id === user.id - ); - if (currentUser) { - socket.current.emit(ACTIONS.MUTE_INFO, { - userId: user.id, - roomId, - isMute: currentUser.muted, - isAdmin: currentUser.admin, - isControl: currentUser.control, - }); - } - if (audioElements.current[remoteUser.id]) { - audioElements.current[remoteUser.id].srcObject = remoteStream; - } else { - let settled = false; - const interval = setInterval(() => { - if (audioElements.current[remoteUser.id]) { - audioElements.current[remoteUser.id].srcObject = - remoteStream; - settled = true; - } - - if (settled) { - clearInterval(interval); - } - }, 300); - } - } - ); - }; - - // Add connection to peer connections track - localMediaStream.current.getTracks().forEach((track) => { - connections.current[peerId].addTrack(track, localMediaStream.current); - }); - - // Create an offer if required - if (createOffer) { - const offer = await connections.current[peerId].createOffer(); - - // Set as local description - await connections.current[peerId].setLocalDescription(offer); - - // send offer to the server - socket.current.emit(ACTIONS.RELAY_SDP, { - peerId, - sessionDescription: offer, - }); - } - } - async function handleRemovePeer({ peerId, userId }) { - console.log(userId); - // Correction: peerID to peerId - if (connections.current[peerId]) { - connections.current[peerId].close(); - } - - delete connections.current[peerId]; - delete audioElements.current[peerId]; - setClients((list) => handleLogout(list, userId)); - } - async function handleIceCandidate({ peerId, icecandidate }) { - if (icecandidate) { - connections.current[peerId].addIceCandidate(icecandidate); - } - } - async function setRemoteMedia({ - peerId, - sessionDescription: remoteSessionDescription, - }) { - connections.current[peerId].setRemoteDescription( - new RTCSessionDescription(remoteSessionDescription) - ); - - // If session descrition is offer then create an answer - if (remoteSessionDescription.type === "offer") { - const connection = connections.current[peerId]; - - const answer = await connection.createAnswer(); - connection.setLocalDescription(answer); - - socket.current.emit(ACTIONS.RELAY_SDP, { - peerId, - sessionDescription: answer, - }); - } - } - async function handleSetMute(mute, userId) { - console.log(clientsRef.current); - const clientIdx = clientsRef.current - .map((client) => client.id) - .indexOf(userId); - const allConnectedClients = JSON.parse( - JSON.stringify(clientsRef.current) - ); - - if (clientIdx > -1) { - allConnectedClients[clientIdx].muted = mute; - setClients(allConnectedClients); - } - } - - async function handleSetControl(userId) { - const clientIdx = clientsRef.current - .map((client) => client.id) - .indexOf(userId); - const allConnectedClients = JSON.parse( - JSON.stringify(clientsRef.current) - ); - for (let i = 0; i < allConnectedClients.length; i++) { - allConnectedClients[i].control = false; - } - - if (clientIdx > -1) { - allConnectedClients[clientIdx].control = true; - setClients(allConnectedClients); - } - } - - async function handleSetAdmin(admin, userId) { - const clientIdx = clientsRef.current - .map((client) => client.id) - .indexOf(userId); - const allConnectedClients = JSON.parse( - JSON.stringify(clientsRef.current) - ); - if (clientIdx > -1) { - allConnectedClients[clientIdx].admin = admin; - allConnectedClients[clientIdx].control = true; - setClients(allConnectedClients); - } - } - }; - - initChat(); - return () => { - if (localMediaStream.current) { - localMediaStream.current.getTracks().forEach((track) => track.stop()); - } - socket.current.emit(ACTIONS.LEAVE, { roomId }); - for (let peerId in connections.current) { - connections.current[peerId].close(); - delete connections.current[peerId]; - delete audioElements.current[peerId]; - } - socket.current.off(ACTIONS.ADD_PEER); - socket.current.off(ACTIONS.REMOVE_PEER); - socket.current.off(ACTIONS.ICE_CANDIDATE); - socket.current.off(ACTIONS.SESSION_DESCRIPTION); - socket.current.off(ACTIONS.MUTE); - socket.current.off(ACTIONS.UNMUTE); - }; - }, []); - - const provideRef = (instance, userId) => { - audioElements.current[userId] = instance; - }; - - const handleAdmin = () => { - let settled = false; - let interval = setInterval(() => { - socket.current.emit("ADMIN-SET", { - roomId, - }); - - settled = true; - if (settled) { - clearInterval(interval); - } - }, 200); - }; - - const handleMute = (isMute, userId) => { - let settled = false; - - if (userId === user.id) { - let interval = setInterval(() => { - if (localMediaStream.current) { - localMediaStream.current.getTracks()[0].enabled = !isMute; - if (isMute) { - socket.current.emit(ACTIONS.MUTE, { - roomId, - userId: user.id, - }); - } else { - socket.current.emit(ACTIONS.UNMUTE, { - roomId, - userId: user.id, - }); - } - settled = true; - } - if (settled) { - clearInterval(interval); - } - }, 200); - } - }; - - const handleControl = (userId) => { - let settled = false; - let interval = setInterval(() => { - socket.current.emit("CONTROL-SET", { - roomId, - userId: userId, - }); - - settled = true; - if (settled) { - clearInterval(interval); - } - }, 200); - }; - - const handleLogout = (list, userId) => { - const isAdmin = list.some((i) => i.admin === true && i.id === userId); - const isControl = list.some((i) => i.control === true && i.id === userId); - console.log(isControl); - if (isAdmin) { - socket.current.emit("ADMIN-LEFT", { - isLeft: true, - }); - } else if (isControl) { - handleChangeControl(list); - } - - return list.filter((c) => c.id !== userId); - }; - - const handleChangeControl = (list) => { - const admin = list.find((i) => i.admin === true); - if (admin) { - handleControl(admin.id); - } else { - return; - } - }; - - const handleReturnControl = () => { - const admin = clients.find((i) => i.admin === true); - if (admin) { - handleControl(admin.id); - } else { - return; - } - }; - - const sendRequset = (user) => { - socket.current.emit("REQUEST-CONTROL", { - userID: user.id, - roomId, - }); - }; - - return { - clients, - provideRef, - warning, - handleMute, - handleControl, - sendRequset, - message, - control, - handleAdmin, - handleReturnControl, - }; -}; diff --git a/src/store/reducers/ActionCreator.ts b/src/store/reducers/ActionCreator.ts index 76e0d7c..47e8b27 100644 --- a/src/store/reducers/ActionCreator.ts +++ b/src/store/reducers/ActionCreator.ts @@ -2,7 +2,7 @@ import { createAsyncThunk } from "@reduxjs/toolkit"; import axios from 'axios'; const instance = axios.create({ - baseURL: 'https://a1.test.coord.graff.tech', + baseURL: 'https://a1.coord.graff.tech', }); instance.defaults.headers.post['Content-Type'] = 'application/json'; diff --git a/src/utils/app.js b/src/utils/app.js index 7efa027..d8df69a 100644 --- a/src/utils/app.js +++ b/src/utils/app.js @@ -998,10 +998,10 @@ function showConnectOverlay() { startText.id = 'container' let title = document.createElement('h2') title.id = "title" - title.innerHTML = 'Демонстрация начата' + title.innerHTML = store.getState().languageReducer.currentLang === 'ru' ? 'Демонстрация начата' : 'Demonstration is started' let caption = document.createElement('p') caption.id = 'caption' - caption.innerHTML = 'Нажмите, чтобы продолжить' + caption.innerHTML = store.getState().languageReducer.currentLang === 'ru' ? 'Нажмите, чтобы продолжить' : "Click to continue" let headerContainer = document.createElement('div') headerContainer.appendChild(title) headerContainer.appendChild(caption) @@ -1012,7 +1012,7 @@ function showConnectOverlay() { startText.appendChild(playButton) let buttonBack = document.createElement('a') let captionBack = document.createElement('div') - captionBack.innerHTML = 'Выбор жилого комплекса' + captionBack.innerHTML = store.getState().languageReducer.currentLang === 'ru' ? 'Выбор жилого комплекса' : 'Back to selection' buttonBack.appendChild(captionBack) buttonBack.id = 'link' buttonBack.href = 'https://stream.graff.tech/' @@ -1373,6 +1373,7 @@ function setupWebRtcPlayer(htmlElement, config) { } }; + webRtcPlayerObj.onNewVideoTrack = function (streams) { if (webRtcPlayerObj.video && webRtcPlayerObj.video.srcObject && webRtcPlayerObj.onVideoInitialised) { webRtcPlayerObj.onVideoInitialised(); @@ -1405,148 +1406,150 @@ function setupWebRtcPlayer(htmlElement, config) { } function setupStats() { - webRtcPlayerObj.aggregateStats(1 * 1000 /*Check every 1 second*/); + if (webRtcPlayerObj) { + webRtcPlayerObj.aggregateStats(1 * 1000 /*Check every 1 second*/); - let printInterval = 5 * 60 * 1000; /*Print every 5 minutes*/ - let nextPrintDuration = printInterval; + let printInterval = 5 * 60 * 1000; /*Print every 5 minutes*/ + let nextPrintDuration = printInterval; - webRtcPlayerObj.onAggregatedStats = (aggregatedStats) => { - let numberFormat = new Intl.NumberFormat(window.navigator.language, { - maximumFractionDigits: 0 - }); - let timeFormat = new Intl.NumberFormat(window.navigator.language, { - maximumFractionDigits: 0, - minimumIntegerDigits: 2 - }); + webRtcPlayerObj.onAggregatedStats = (aggregatedStats) => { + let numberFormat = new Intl.NumberFormat(window.navigator.language, { + maximumFractionDigits: 0 + }); + let timeFormat = new Intl.NumberFormat(window.navigator.language, { + maximumFractionDigits: 0, + minimumIntegerDigits: 2 + }); - // Calculate duration of run - let runTime = (aggregatedStats.timestamp - aggregatedStats.timestampStart) / 1000; - let timeValues = []; - let timeDurations = [60, 60]; - for (let timeIndex = 0; timeIndex < timeDurations.length; timeIndex++) { - timeValues.push(runTime % timeDurations[timeIndex]); - runTime = runTime / timeDurations[timeIndex]; - } - timeValues.push(runTime); + // Calculate duration of run + let runTime = (aggregatedStats.timestamp - aggregatedStats.timestampStart) / 1000; + let timeValues = []; + let timeDurations = [60, 60]; + for (let timeIndex = 0; timeIndex < timeDurations.length; timeIndex++) { + timeValues.push(runTime % timeDurations[timeIndex]); + runTime = runTime / timeDurations[timeIndex]; + } + timeValues.push(runTime); - let runTimeSeconds = timeValues[0]; - let runTimeMinutes = Math.floor(timeValues[1]); - let runTimeHours = Math.floor([timeValues[2]]); + let runTimeSeconds = timeValues[0]; + let runTimeMinutes = Math.floor(timeValues[1]); + let runTimeHours = Math.floor([timeValues[2]]); - let receivedBytesMeasurement = 'B'; - let receivedBytes = aggregatedStats.hasOwnProperty('bytesReceived') ? aggregatedStats.bytesReceived : 0; - let dataMeasurements = ['kB', 'MB', 'GB']; - for (let index = 0; index < dataMeasurements.length; index++) { - if (receivedBytes < 100 * 1000) - break; - receivedBytes = receivedBytes / 1000; - receivedBytesMeasurement = dataMeasurements[index]; - } + let receivedBytesMeasurement = 'B'; + let receivedBytes = aggregatedStats.hasOwnProperty('bytesReceived') ? aggregatedStats.bytesReceived : 0; + let dataMeasurements = ['kB', 'MB', 'GB']; + for (let index = 0; index < dataMeasurements.length; index++) { + if (receivedBytes < 100 * 1000) + break; + receivedBytes = receivedBytes / 1000; + receivedBytesMeasurement = dataMeasurements[index]; + } - let qualityStatus = document.getElementById("connectionStrength"); - // "blinks" quality status element for 1 sec by making it transparent, speed = number of blinks - let blinkQualityStatus = function (speed) { - let iter = speed; - let opacity = 1; // [0..1] - let tickId = setInterval( - function () { - opacity -= 0.1; - // map `opacity` to [-0.5..0.5] range, decrement by 0.2 per step and take `abs` to make it blink: 1 -> 0 -> 1 - qualityStatus.style.opacity = `${Math.abs((opacity - 0.5) * 2)}`; - if (opacity <= 0.1) { - if (--iter == 0) { - clearInterval(tickId); - } else { // next blink - opacity = 1; + let qualityStatus = document.getElementById("connectionStrength"); + // "blinks" quality status element for 1 sec by making it transparent, speed = number of blinks + let blinkQualityStatus = function (speed) { + let iter = speed; + let opacity = 1; // [0..1] + let tickId = setInterval( + function () { + opacity -= 0.1; + // map `opacity` to [-0.5..0.5] range, decrement by 0.2 per step and take `abs` to make it blink: 1 -> 0 -> 1 + qualityStatus.style.opacity = `${Math.abs((opacity - 0.5) * 2)}`; + if (opacity <= 0.1) { + if (--iter == 0) { + clearInterval(tickId); + } else { // next blink + opacity = 1; + } } + }, + 100 / speed // msecs + ); + }; + + const orangeQP = 26; + const redQP = 35; + + let statsText = ''; + let color; + + // Wifi strength elements + + if (VideoEncoderQP > redQP) { + + + } else if (VideoEncoderQP > orangeQP) { + + } else { + } + statsText += `
Duration: ${timeFormat.format(runTimeHours)}:${timeFormat.format(runTimeMinutes)}:${timeFormat.format(runTimeSeconds)}
`; + statsText += `
Controls stream input: ${inputController === null ? "Not sent yet" : (inputController ? "true" : "false")}
`; + statsText += `
Audio codec: ${aggregatedStats.hasOwnProperty('audioCodec') ? aggregatedStats.audioCodec : "Not set"}
`; + statsText += `
Video codec: ${aggregatedStats.hasOwnProperty('videoCodec') ? aggregatedStats.videoCodec : "Not set"}
`; + statsText += `
Video Resolution: ${aggregatedStats.hasOwnProperty('frameWidth') && aggregatedStats.frameWidth && aggregatedStats.hasOwnProperty('frameHeight') && aggregatedStats.frameHeight ? + aggregatedStats.frameWidth + 'x' + aggregatedStats.frameHeight : 'Chrome only' + }
`; + statsText += `
Received (${receivedBytesMeasurement}): ${numberFormat.format(receivedBytes)}
`; + statsText += `
Frames Decoded: ${aggregatedStats.hasOwnProperty('framesDecoded') ? numberFormat.format(aggregatedStats.framesDecoded) : 'Chrome only'}
`; + statsText += `
Packets Lost: ${aggregatedStats.hasOwnProperty('packetsLost') ? numberFormat.format(aggregatedStats.packetsLost) : 'Chrome only'}
`; + statsText += `
Framerate: ${aggregatedStats.hasOwnProperty('framerate') ? numberFormat.format(aggregatedStats.framerate) : 'Chrome only'}
`; + statsText += `
Frames dropped: ${aggregatedStats.hasOwnProperty('framesDropped') ? numberFormat.format(aggregatedStats.framesDropped) : 'Chrome only'}
`; + statsText += `
Net RTT (ms): ${aggregatedStats.hasOwnProperty('currentRoundTripTime') ? numberFormat.format(aggregatedStats.currentRoundTripTime * 1000) : 'Can\'t calculate'}
`; + statsText += `
Browser receive to composite (ms): ${aggregatedStats.hasOwnProperty('receiveToCompositeMs') ? numberFormat.format(aggregatedStats.receiveToCompositeMs) : 'Chrome only'}
`; + statsText += `
Audio Bitrate (kbps): ${aggregatedStats.hasOwnProperty('audioBitrate') ? numberFormat.format(aggregatedStats.audioBitrate) : 'Chrome only'}
`; + statsText += `
Video Bitrate (kbps): ${aggregatedStats.hasOwnProperty('bitrate') ? numberFormat.format(aggregatedStats.bitrate) : 'Chrome only'}
`; + statsText += `
Video Quantization Parameter: ${VideoEncoderQP}
`; + + let statsDiv = document.getElementById("stats"); + statsDiv.innerHTML = statsText; + + if (print_stats) { + if (aggregatedStats.timestampStart) { + if ((aggregatedStats.timestamp - aggregatedStats.timestampStart) > nextPrintDuration) { + if (ws && ws.readyState === WS_OPEN_STATE) { + console.log(`-> SS: stats\n${JSON.stringify(aggregatedStats)}`); + ws.send(JSON.stringify({ + type: 'stats', + data: aggregatedStats + })); + } + nextPrintDuration += printInterval; } - }, - 100 / speed // msecs - ); - }; - - const orangeQP = 26; - const redQP = 35; - - let statsText = ''; - let color; - - // Wifi strength elements - - if (VideoEncoderQP > redQP) { - - - } else if (VideoEncoderQP > orangeQP) { - - } else { - } - statsText += `
Duration: ${timeFormat.format(runTimeHours)}:${timeFormat.format(runTimeMinutes)}:${timeFormat.format(runTimeSeconds)}
`; - statsText += `
Controls stream input: ${inputController === null ? "Not sent yet" : (inputController ? "true" : "false")}
`; - statsText += `
Audio codec: ${aggregatedStats.hasOwnProperty('audioCodec') ? aggregatedStats.audioCodec : "Not set"}
`; - statsText += `
Video codec: ${aggregatedStats.hasOwnProperty('videoCodec') ? aggregatedStats.videoCodec : "Not set"}
`; - statsText += `
Video Resolution: ${aggregatedStats.hasOwnProperty('frameWidth') && aggregatedStats.frameWidth && aggregatedStats.hasOwnProperty('frameHeight') && aggregatedStats.frameHeight ? - aggregatedStats.frameWidth + 'x' + aggregatedStats.frameHeight : 'Chrome only' - }
`; - statsText += `
Received (${receivedBytesMeasurement}): ${numberFormat.format(receivedBytes)}
`; - statsText += `
Frames Decoded: ${aggregatedStats.hasOwnProperty('framesDecoded') ? numberFormat.format(aggregatedStats.framesDecoded) : 'Chrome only'}
`; - statsText += `
Packets Lost: ${aggregatedStats.hasOwnProperty('packetsLost') ? numberFormat.format(aggregatedStats.packetsLost) : 'Chrome only'}
`; - statsText += `
Framerate: ${aggregatedStats.hasOwnProperty('framerate') ? numberFormat.format(aggregatedStats.framerate) : 'Chrome only'}
`; - statsText += `
Frames dropped: ${aggregatedStats.hasOwnProperty('framesDropped') ? numberFormat.format(aggregatedStats.framesDropped) : 'Chrome only'}
`; - statsText += `
Net RTT (ms): ${aggregatedStats.hasOwnProperty('currentRoundTripTime') ? numberFormat.format(aggregatedStats.currentRoundTripTime * 1000) : 'Can\'t calculate'}
`; - statsText += `
Browser receive to composite (ms): ${aggregatedStats.hasOwnProperty('receiveToCompositeMs') ? numberFormat.format(aggregatedStats.receiveToCompositeMs) : 'Chrome only'}
`; - statsText += `
Audio Bitrate (kbps): ${aggregatedStats.hasOwnProperty('audioBitrate') ? numberFormat.format(aggregatedStats.audioBitrate) : 'Chrome only'}
`; - statsText += `
Video Bitrate (kbps): ${aggregatedStats.hasOwnProperty('bitrate') ? numberFormat.format(aggregatedStats.bitrate) : 'Chrome only'}
`; - statsText += `
Video Quantization Parameter: ${VideoEncoderQP}
`; - - let statsDiv = document.getElementById("stats"); - statsDiv.innerHTML = statsText; - - if (print_stats) { - if (aggregatedStats.timestampStart) { - if ((aggregatedStats.timestamp - aggregatedStats.timestampStart) > nextPrintDuration) { - if (ws && ws.readyState === WS_OPEN_STATE) { - console.log(`-> SS: stats\n${JSON.stringify(aggregatedStats)}`); - ws.send(JSON.stringify({ - type: 'stats', - data: aggregatedStats - })); - } - nextPrintDuration += printInterval; } } + }; + + webRtcPlayerObj.latencyTestTimings.OnAllLatencyTimingsReady = function (timings) { + + if (!timings.BrowserReceiptTimeMs) { + return; + } + + let latencyExcludingDecode = timings.BrowserReceiptTimeMs - timings.TestStartTimeMs; + let encodeLatency = timings.UEEncodeMs; + let uePixelStreamLatency = timings.UECaptureToSendMs; + let ueTestDuration = timings.UETransmissionTimeMs - timings.UEReceiptTimeMs; + let networkLatency = latencyExcludingDecode - ueTestDuration; + + //these ones depend on FrameDisplayDeltaTimeMs + let endToEndLatency = null; + let browserSideLatency = null; + + if (timings.FrameDisplayDeltaTimeMs && timings.BrowserReceiptTimeMs) { + endToEndLatency = timings.FrameDisplayDeltaTimeMs + networkLatency + (typeof uePixelStreamLatency === "string" ? 0 : uePixelStreamLatency); + browserSideLatency = timings.FrameDisplayDeltaTimeMs + (latencyExcludingDecode - networkLatency - ueTestDuration); + } + + let latencyStatsInnerHTML = ''; + latencyStatsInnerHTML += `
Net latency RTT (ms): ${networkLatency.toFixed(2)}
`; + latencyStatsInnerHTML += `
UE Encode (ms): ${(typeof encodeLatency === "string" ? encodeLatency : encodeLatency.toFixed(2))}
`; + latencyStatsInnerHTML += `
UE Send to capture (ms): ${(typeof uePixelStreamLatency === "string" ? uePixelStreamLatency : uePixelStreamLatency.toFixed(2))}
`; + latencyStatsInnerHTML += `
UE probe duration (ms): ${ueTestDuration.toFixed(2)}
`; + latencyStatsInnerHTML += timings.FrameDisplayDeltaTimeMs && timings.BrowserReceiptTimeMs ? `
Browser composite latency (ms): ${timings.FrameDisplayDeltaTimeMs.toFixed(2)}
` : ""; + latencyStatsInnerHTML += browserSideLatency ? `
Total browser latency (ms): ${browserSideLatency.toFixed(2)}
` : ""; + latencyStatsInnerHTML += endToEndLatency ? `
Total latency (ms): ${endToEndLatency.toFixed(2)}
` : ""; + document.getElementById("LatencyStats").innerHTML = latencyStatsInnerHTML; } - }; - - webRtcPlayerObj.latencyTestTimings.OnAllLatencyTimingsReady = function (timings) { - - if (!timings.BrowserReceiptTimeMs) { - return; - } - - let latencyExcludingDecode = timings.BrowserReceiptTimeMs - timings.TestStartTimeMs; - let encodeLatency = timings.UEEncodeMs; - let uePixelStreamLatency = timings.UECaptureToSendMs; - let ueTestDuration = timings.UETransmissionTimeMs - timings.UEReceiptTimeMs; - let networkLatency = latencyExcludingDecode - ueTestDuration; - - //these ones depend on FrameDisplayDeltaTimeMs - let endToEndLatency = null; - let browserSideLatency = null; - - if (timings.FrameDisplayDeltaTimeMs && timings.BrowserReceiptTimeMs) { - endToEndLatency = timings.FrameDisplayDeltaTimeMs + networkLatency + (typeof uePixelStreamLatency === "string" ? 0 : uePixelStreamLatency); - browserSideLatency = timings.FrameDisplayDeltaTimeMs + (latencyExcludingDecode - networkLatency - ueTestDuration); - } - - let latencyStatsInnerHTML = ''; - latencyStatsInnerHTML += `
Net latency RTT (ms): ${networkLatency.toFixed(2)}
`; - latencyStatsInnerHTML += `
UE Encode (ms): ${(typeof encodeLatency === "string" ? encodeLatency : encodeLatency.toFixed(2))}
`; - latencyStatsInnerHTML += `
UE Send to capture (ms): ${(typeof uePixelStreamLatency === "string" ? uePixelStreamLatency : uePixelStreamLatency.toFixed(2))}
`; - latencyStatsInnerHTML += `
UE probe duration (ms): ${ueTestDuration.toFixed(2)}
`; - latencyStatsInnerHTML += timings.FrameDisplayDeltaTimeMs && timings.BrowserReceiptTimeMs ? `
Browser composite latency (ms): ${timings.FrameDisplayDeltaTimeMs.toFixed(2)}
` : ""; - latencyStatsInnerHTML += browserSideLatency ? `
Total browser latency (ms): ${browserSideLatency.toFixed(2)}
` : ""; - latencyStatsInnerHTML += endToEndLatency ? `
Total latency (ms): ${endToEndLatency.toFixed(2)}
` : ""; - document.getElementById("LatencyStats").innerHTML = latencyStatsInnerHTML; } } @@ -2700,7 +2703,7 @@ function connect() { } // Make a new websocket connection - let connectionUrl = 'wss://a2.sess.graff.tech/s2/14000/' + let connectionUrl = store.getState().sessionReducer.url console.log(`Creating a websocket connection to: ${connectionUrl}`); ws = new WebSocket(connectionUrl); ws.attemptStreamReconnection = true;