Compare commits

..

16 Commits

Author SHA1 Message Date
inmake ec701a7925 add privated fetch 2023-04-17 15:04:58 +05:00
DmitriyB 1a3e6f688d deleted unused types 2023-03-31 13:17:02 +05:00
DmitriyB 62be599d33 some filestructure fixes 2023-03-27 12:44:28 +05:00
DmitriyB 0adbf6f4e2 refactoring, mobile version functional 2023-03-24 17:05:56 +05:00
DmitriyB 65bc4a011a refactoring 2023-03-22 13:54:05 +05:00
DmitriyB 4d9bd5c98b refactoring 2023-03-22 13:53:43 +05:00
DmitriyB d494abf7ae added language change query 2023-03-20 18:46:15 +05:00
DmitriyB 61efa5611d added webRtcPlayer 2023-03-13 15:18:24 +05:00
DmitriyB 240f28935d ui update progress 2023-03-07 15:59:17 +05:00
DmitriyB 4107a85a4d ui upddate ... 2023-03-07 15:58:56 +05:00
VyacheslavShtyrlin 48e3061c09 Mobile: Inprogress 2023-03-03 00:37:22 +05:00
DmitriyB 13d725261f style fixes 2023-02-28 17:06:43 +05:00
DmitriyB a99883d093 ui update and axios 2023-02-28 16:30:16 +05:00
DmitriyB 031bd771f9 style fixes 2023-02-22 12:58:23 +05:00
VyacheslavShtyrlin 443532b484 added planning component 2023-02-22 09:18:03 +05:00
VyacheslavShtyrlin c34d3ae1fe ui update: test version 2023-02-20 23:16:07 +05:00
220 changed files with 8969 additions and 3816 deletions
+70 -66
View File
@@ -19,12 +19,13 @@
"@types/react-redux": "^7.1.24",
"@types/react-router": "^5.1.20",
"@types/socket.io": "^3.0.2",
"connected-react-router": "^6.9.3",
"axios": "^1.3.4",
"framer-motion": "^7.4.0",
"i18next": "^22.4.6",
"i18next-browser-languagedetector": "^7.0.1",
"i18next-http-backend": "^2.1.1",
"js-cookie": "^3.0.1",
"moment": "^2.29.4",
"peer": "^0.6.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -4938,6 +4939,29 @@
"node": ">=4"
}
},
"node_modules/axios": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"dependencies": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axios/node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/axobject-query": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
@@ -5777,26 +5801,6 @@
"node": ">=0.8"
}
},
"node_modules/connected-react-router": {
"version": "6.9.3",
"resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-6.9.3.tgz",
"integrity": "sha512-4ThxysOiv/R2Dc4Cke1eJwjKwH1Y51VDwlOrOfs1LjpdYOVvCNjNkZDayo7+sx42EeGJPQUNchWkjAIJdXGIOQ==",
"dependencies": {
"lodash.isequalwith": "^4.4.0",
"prop-types": "^15.7.2"
},
"optionalDependencies": {
"immutable": "^3.8.1 || ^4.0.0",
"seamless-immutable": "^7.1.3"
},
"peerDependencies": {
"history": "^4.7.2",
"react": "^16.4.0 || ^17.0.0",
"react-redux": "^6.0.0 || ^7.1.0",
"react-router": "^4.3.1 || ^5.0.0",
"redux": "^3.6.0 || ^4.0.0"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -8983,12 +8987,6 @@
"url": "https://opencollective.com/immer"
}
},
"node_modules/immutable": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz",
"integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==",
"optional": true
},
"node_modules/import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -11710,11 +11708,6 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"node_modules/lodash.isequalwith": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz",
"integrity": "sha512-dcZON0IalGBpRmJBmMkaoV7d3I80R2O+FrzsZyHdNSFrANq/cgDqKQNmAHE8UEj4+QYWwwhkQOVdLHiAopzlsQ=="
},
"node_modules/lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -12018,6 +12011,14 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -14169,6 +14170,11 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -15231,12 +15237,6 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/seamless-immutable": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/seamless-immutable/-/seamless-immutable-7.1.4.tgz",
"integrity": "sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A==",
"optional": true
},
"node_modules/select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -20971,6 +20971,28 @@
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.4.3.tgz",
"integrity": "sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w=="
},
"axios": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
"integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
"requires": {
"follow-redirects": "^1.15.0",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
},
"dependencies": {
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
}
},
"axobject-query": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
@@ -21618,17 +21640,6 @@
"resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz",
"integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA=="
},
"connected-react-router": {
"version": "6.9.3",
"resolved": "https://registry.npmjs.org/connected-react-router/-/connected-react-router-6.9.3.tgz",
"integrity": "sha512-4ThxysOiv/R2Dc4Cke1eJwjKwH1Y51VDwlOrOfs1LjpdYOVvCNjNkZDayo7+sx42EeGJPQUNchWkjAIJdXGIOQ==",
"requires": {
"immutable": "^3.8.1 || ^4.0.0",
"lodash.isequalwith": "^4.4.0",
"prop-types": "^15.7.2",
"seamless-immutable": "^7.1.3"
}
},
"content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -23912,12 +23923,6 @@
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.16.tgz",
"integrity": "sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ=="
},
"immutable": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz",
"integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==",
"optional": true
},
"import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@@ -25881,11 +25886,6 @@
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"lodash.isequalwith": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz",
"integrity": "sha512-dcZON0IalGBpRmJBmMkaoV7d3I80R2O+FrzsZyHdNSFrANq/cgDqKQNmAHE8UEj4+QYWwwhkQOVdLHiAopzlsQ=="
},
"lodash.memoize": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
@@ -26112,6 +26112,11 @@
"minimist": "^1.2.6"
}
},
"moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -27493,6 +27498,11 @@
}
}
},
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -28241,12 +28251,6 @@
"ajv-keywords": "^3.5.2"
}
},
"seamless-immutable": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/seamless-immutable/-/seamless-immutable-7.1.4.tgz",
"integrity": "sha512-XiUO1QP4ki4E2PHegiGAlu6r82o5A+6tRh7IkGGTVg/h+UoeX4nFBeCGPOhb4CYjvkqsfm/TUtvOMYC1xmV30A==",
"optional": true
},
"select-hose": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+2 -1
View File
@@ -14,12 +14,13 @@
"@types/react-redux": "^7.1.24",
"@types/react-router": "^5.1.20",
"@types/socket.io": "^3.0.2",
"connected-react-router": "^6.9.3",
"axios": "^1.3.4",
"framer-motion": "^7.4.0",
"i18next": "^22.4.6",
"i18next-browser-languagedetector": "^7.0.1",
"i18next-http-backend": "^2.1.1",
"js-cookie": "^3.0.1",
"moment": "^2.29.4",
"peer": "^0.6.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+17 -6
View File
@@ -8,8 +8,8 @@
"popup-connect-title": "Connection code",
"popup-connect-btn-mode": "Select demonstration mode",
"popup-connect-btn-continue": "Continue",
"fullscreen-control-btn": "Expand",
"fullscreen-control-btn-active": "Collapse",
"fullscreen-control-btn": "Windowed mode",
"fullscreen-control-btn-active": "Fullscreen mode",
"request-control-btn": "Request control",
"request-control-btn-disable": "Stop control",
"mute-control-btn": "Turn On Mic",
@@ -19,10 +19,21 @@
"popup-control-code": "Connection code",
"popup-control-link": "Link for connection",
"popup-control-btn": "Copy",
"popup-control-btn-active": "Copied!",
"language-control-btn": "Language",
"exit-control-btn": "Exit presintation",
"popup-control-btn-active": "Link Copied",
"language-control-btn": "English",
"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",
"language-button": "Language",
"popup-additional": "Additional",
"popup-userlist": "Spectrators",
"user": "User",
"popup-change-language": "Change Language"
}
+14 -8
View File
@@ -8,8 +8,8 @@
"popup-connect-title": "Код подключения к демонстрации",
"popup-connect-btn-mode": "Выбор способа демонстрации",
"popup-connect-btn-continue": "Продолжить",
"fullscreen-control-btn": "Развернуть",
"fullscreen-control-btn-active": "Свернуть",
"fullscreen-control-btn": "Полноэкранный режим",
"fullscreen-control-btn-active": "Оконный режим",
"request-control-btn": "Запрос управления",
"request-control-btn-disable": "Запрет управления",
"mute-control-btn": "Включить микрофон",
@@ -19,11 +19,17 @@
"popup-control-code": "Код подключения",
"popup-control-link": "Ссылка для подключения",
"popup-control-btn": "Скопировать",
"popup-control-btn-active": "Скопировано",
"language-control-btn": "Язык",
"exit-control-btn": "Завершить презентацию",
"popup-control-exit-title": "Вы уверены, что хотите закончить демонстрацию??",
"popup-control-btn-active": "Ссылка скопирована",
"language-control-btn": "Русский язык",
"exit-control-btn": "Выйти",
"popup-control-exit-title": "Вы уверены, что хотите закончить демонстрацию?",
"popup-control-yes": "Закончить",
"popup-control-no": "Остаться"
"popup-control-no": "Остаться",
"popup-loading": "Пожалуйста, подождите",
"sidebar-hide": "Скрыть меню",
"language-button": "Язык",
"popup-additional": "Дополнительно",
"popup-userlist": "Участники демонстрации",
"user": "Пользователь",
"popup-change-language": "Выбор языка"
}
+1 -1
View File
@@ -4,7 +4,7 @@
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="user-scalable=no, width=device-width, height=device-height" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
+36 -37
View File
@@ -6,40 +6,29 @@
.content__container {
height: 100vh;
width: 400px;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 50%;
left: 50%;
border-width: 0px 2px;
border-style: solid;
border-color: #23242A;
transform: translate(-50%, -50%);
/* This is a shorthand of */
}
.card-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 30px;
margin-bottom: 128px;
}
.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 {
@@ -49,48 +38,58 @@
@media screen and (max-width: 1440px) {
.card-title {
margin: 22px 0 40px 0;
}
@media screen and (max-width: 1440px) {
.card-container {
gap: 24px;
margin-bottom: 76px;
}
}
@media screen and (max-width: 1152px) {
.card-container {
gap: 20px;
.card-title-container {
margin-top: 96px;
}
.card-title {
margin: 42px 0 40px 0;
.card-container {
gap: 20px;
}
}
@media screen and (max-width: 1024px) {
.card-container {
gap: 10px;
margin-bottom: 10px;
}
}
@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 {
flex-direction: column;
gap: 10px;
margin-bottom: 115px;
}
.content__container {
width: 100%;
border: none;
}
}
+99 -26
View File
@@ -1,40 +1,101 @@
import "./App.css";
import "styles/styles.css";
import { useEffect } from "react";
import { Redirect, Route, Switch, useHistory } from "react-router-dom";
import { Header } from "./components/header/header";
import { Card } from "./components/demos/Card";
import { Main } from "./components/Main/Main";
import { PlayerComponent } from "./components/playerComponent/playerComponent";
import { useAppDispatch, useAppSelector } from "./hooks/redux";
import { fetchCards } from "./store/reducers/ActionCreator";
import { cardSlice } from "./store/reducers/cardSlice";
import { languageSlice } from "./store/reducers/languageSlice";
import { ICards } from "./models/ICards";
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";
import { SessionStartComponent } from "components/pages/ConnectPage/SessionStartComponent/SessionStartComponent";
import { PlayerComponent } from "components/pages/Stream/components/PlayerComponent/PlayerComponent";
import { PlanComponent } from "components/pages/Plan/PlanComponent";
import { useAppDispatch, useAppSelector } from "hooks/redux";
import { fetchCards, fetchPrivateCards } 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 { closeStream, load as loadStream } from "utils/app";
const App: React.FC = () => {
const dispatch = useAppDispatch();
const history = useHistory();
const { handleCurrentCard } = cardSlice.actions;
const { handleChangeLanguage } = languageSlice.actions;
const { cards, currentCard } = useAppSelector((state) => state.cardReducer);
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");
const langArray = ["en", "ru"];
const browserLanguage = window.navigator.language;
const handleBrowserLanguage = () => {
return langArray.includes(browserLanguage);
};
const handleCookiesLanguage = () => {
const language = cookies.get("i18next");
return language;
};
useEffect(() => {
if (langArray.includes(langQuery as string)) {
dispatch(handleChangeLanguage(langQuery as string));
return;
} else if (handleCookiesLanguage()) {
const languageCookies = handleCookiesLanguage();
dispatch(handleChangeLanguage(languageCookies as string));
return;
}
let isSupported = handleBrowserLanguage();
dispatch(handleChangeLanguage(isSupported ? browserLanguage : "en"));
}, []);
const handleConnect = (title: string) => {
dispatch(createSession(title))
.unwrap()
.then((res) => {
history.push(`/stream/${res.session_id}`);
})
.catch((res) => {
alert(res);
history.push("/");
});
};
useEffect(() => {
dispatch(fetchCards());
dispatch(handleChangeLanguage(cookies.get("i18next")));
}, []);
if (currentLang) {
const key = new URLSearchParams(history.location.search).get("key");
if (key) {
dispatch(fetchPrivateCards(key));
} else {
dispatch(fetchCards(currentLang));
}
}
}, [currentLang]);
const handleCards = (card: ICards) => {
dispatch(handleCurrentCard(card));
history.push("/connect-page");
};
const closeStream = () => {
const handleDisconnect = () => {
closeStream();
history.push("/");
};
@@ -45,14 +106,12 @@ const App: React.FC = () => {
<Route exact path="/">
<Header></Header>
<div className="main">
<h3 className="card-title">{t("demo-title")}</h3>
<div className="card-title-container">
<h2 className="card-title">{error ? error : t("demo-title")}</h2>
</div>
<div className="card-container">
{cards.map((i: ICards) => (
<Card
onClick={() => handleCards(i)}
key={i._id}
item={i}
></Card>
<Card onClick={() => handleCards(i)} key={i._id} item={i}></Card>
))}
</div>
</div>
@@ -60,9 +119,15 @@ const App: React.FC = () => {
<Route path="/connect-page">
{currentCard ? (
<div className="background">
<Header></Header>
<div className="popup-container">
<div className="content__container">
<Main></Main>
<SessionStartComponent
cleanErrors={cleanErrors}
handleConnect={handleConnect}
isLoading={isLoading}
currentCard={currentCard}
></SessionStartComponent>
</div>
</div>
</div>
) : (
@@ -71,9 +136,17 @@ const App: React.FC = () => {
</Route>
<Route exact path="/stream/:id">
<PlayerComponent
closeStream={closeStream}
handleDisconnect={handleDisconnect}
loadStream={loadStream}
cleanErrors={cleanErrors}
></PlayerComponent>
</Route>
<Route path="/plan">
<Header></Header>
<div className="popup-container">
<PlanComponent></PlanComponent>
</div>
</Route>
</Switch>
);
};
@@ -1,28 +0,0 @@
import { ControlButton } from "../ui/ControlButton/ControlButton";
import { MicroButton } from "../ui/MicroButton/MicroButton";
import { ShareButton } from "../ui/ShareButton/ShareButton";
import { LanguageButton } from "../ui/LanguageButton/LanguageButton";
import { ExitButton } from "../ui/ExitButton/ExitButton";
import React, { useState, useEffect } from "react";
export const ControlPanel: React.FC<any> = ({
handleOpenSharePopup,
handleOpenExitPopup,
handleMuteClick,
isMuted,
isSidebarWide
}) => {
return (
<div className="toolbar-field-part">
<div className="toolbar-button-container-border-line"></div>
<ControlButton isSidebarWide={isSidebarWide} onClick={() => console.log("click!")}></ControlButton>
<MicroButton isSidebarWide={isSidebarWide} isMuted={isMuted} onClick={handleMuteClick}></MicroButton>
<div className="toolbar-button-container-border-line"></div>
<ShareButton isSidebarWide={isSidebarWide} onClick={handleOpenSharePopup}></ShareButton>
<LanguageButton isSidebarWide={isSidebarWide} onClick={() => console.log("click!")}></LanguageButton>
<div className="toolbar-button-container-border-line"></div>
<ExitButton isSidebarWide={isSidebarWide} onClick={handleOpenExitPopup}></ExitButton>
</div>
);
};
-60
View File
@@ -1,60 +0,0 @@
.exit-popup-container {
position: absolute;
display: flex;
flex-direction: column;
width: 494px;
height: 330px;
box-sizing: border-box;
border: 1px solid #4F4F4F;
border-radius: 32px;
top: calc(50% - 469px / 2);
left: calc(50% - 494px / 2);
background-color: #333333;
color: #FFFFFF;
padding: 48px;
/* gap: 24px; */
justify-content: space-between;
}
.exit-popup-button-container {
display: flex;
flex-direction: column;
gap: 24px;
}
.exit-popup-button {
border-radius: 12px;
border: none;
color: white;
height: 60px;
font-style: normal;
font-weight: 600;
font-size: 16px;
line-height: 20px;
cursor: pointer;
}
.exit-popup-button_confirm {
background: #567ECE;
opacity: 1;
}
.exit-popup-button_finish {
background: #404040;
}
.exit-popup-button-title {
width: 366px;
}
.exit-popup-button_confirm:hover {
opacity: 0.7;
transition: .3s;
}
.exit-popup-header {
align-items: normal;
}
-36
View File
@@ -1,36 +0,0 @@
import "../PopupShare/sharePopup.css";
import "./ExitPopup.css";
import { TSidebarPopup } from "../../utils/types";
import { useTranslation } from "react-i18next";
export const ExitPopup: React.FC<TSidebarPopup> = ({ setClose, onExit }) => {
const { t } = useTranslation();
return (
<div className="exit-popup-container">
<div className="mobile-users-part-header exit-popup-header">
<span className="mobile-users-part-header-title exit-popup-button-title">
{t("popup-control-exit-title")}
</span>
<button
onClick={() => setClose()}
className="mobile-users-part-header-close-button"
></button>
</div>
<div className="exit-popup-button-container">
<button
onClick={() => setClose()}
className="exit-popup-button exit-popup-button_confirm"
>
{t("popup-control-no")}
</button>
<button
onClick={() => onExit?.()}
className="exit-popup-button exit-popup-button_finish"
>
{t("popup-control-yes")}
</button>
</div>
</div>
);
};
@@ -1,38 +0,0 @@
import "../sidebar/toolbar.css";
import { languageSlice } from "../../store/reducers/languageSlice";
import { useAppDispatch, useAppSelector } from "../../hooks/redux";
export const LanguagePopup: React.FC<any> = ({ setOpen }) => {
const { handleChangeLanguage } = languageSlice.actions;
const buttons = [{ value: "ru" }, { value: "en" }];
const dispatch = useAppDispatch();
const onChange = (value: string) => {
dispatch(handleChangeLanguage(value));
setOpen(false);
};
const { currentLang } = useAppSelector((state) => state.languageReducer);
return (
<div className="toolbar-language-popup">
{buttons.map((i) => (
<div className="toolbar-button-area">
<button
key={i.value}
value={i.value}
onClick={(e: any) => onChange(e.target.value)}
className={
currentLang === i.value
? "toolbar-button toolbar-button-active"
: "toolbar-button"
}
>
{i.value.toUpperCase()}
</button>
</div>
))}
</div>
);
};
@@ -1,20 +0,0 @@
import "../../styles/styles.css";
import "./LoadingPopup.css";
import loader from "./loader.svg";
import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";
export const LoadingPopup: React.FC<any> = ({ logo }) => {
const history = useHistory();
const { t } = useTranslation();
return (
<div className="loader-container">
<div className="loading-logo-container">
<img className="popup-logo" src={logo} alt="лого" />
<span className="loading-caption">Пожалуйста подождите</span>
</div>
<span className="loader"></span>
</div>
);
};
-9
View File
@@ -1,9 +0,0 @@
<svg width="106" height="106" viewBox="0 0 106 106" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M103.35 53C104.814 53 106.007 51.8126 105.934 50.3509C105.354 38.7527 100.977 27.6378 93.4384 18.7402C85.328 9.16712 74.0844 2.78179 61.7081 0.720288C49.3319 -1.34121 36.6256 1.05484 25.85 7.48211C15.0745 13.9094 6.92854 23.951 2.86158 35.8204C-1.20539 47.6898 -0.929606 60.6171 3.63985 72.3022C8.2093 83.9873 16.7761 93.6725 27.8159 99.6343C38.8558 105.596 51.6527 107.448 63.9298 104.861C75.3406 102.456 85.6145 96.361 93.1857 87.5559C94.1399 86.4462 93.9256 84.7764 92.7696 83.8789V83.8789C91.6136 82.9813 89.9541 83.1964 88.9938 84.3008C82.1966 92.1174 73.0197 97.5286 62.8368 99.6747C51.7874 102.003 40.2702 100.337 30.3343 94.9709C20.3985 89.6052 12.6884 80.8886 8.57587 70.372C4.46336 59.8554 4.21516 48.2208 7.87542 37.5384C11.5357 26.8559 18.867 17.8184 28.565 12.0339C38.263 6.24936 49.6987 4.09292 60.8373 5.94826C71.976 7.80361 82.0952 13.5504 89.3946 22.1662C96.1215 30.1063 100.051 40.0084 100.626 50.3511C100.708 51.8124 101.886 53 103.35 53V53Z" fill="url(#paint0_angular_2745_13429)"/>
<defs>
<radialGradient id="paint0_angular_2745_13429" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(53 53) rotate(45) scale(52.4673)">
<stop offset="0.874517" stop-color="white"/>
<stop offset="0.982613" stop-color="white" stop-opacity="0"/>
</radialGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

-73
View File
@@ -1,73 +0,0 @@
import "./main.css";
import { useEffect, useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { PopupConnect } from "../popupConnect/popupConnect";
import { LoadingPopup } from "../LoadingPopup/LoadingPopup";
import { popupAnimation } from "../../utils/animationProps";
import { useAppDispatch, useAppSelector } from "../../hooks/redux";
import { createSession } from "../../store/reducers/ActionCreator";
import { sessionSlice } from "../../store/reducers/sessionSlice";
export const Main: React.FC = () => {
const dispatch = useAppDispatch();
const { currentCard } = useAppSelector((state) => state.cardReducer);
const [visible, setVisible] = useState({
connectPopup: true,
loadingPopup: false,
});
const { cleanErrors } = sessionSlice.actions;
const { isLoading, error } = useAppSelector((state) => state.sessionReducer);
const { connectPopup, loadingPopup } = visible;
useEffect(() => {
if (isLoading) {
setVisible({ connectPopup: false, loadingPopup: true });
return;
} else if (error) {
setVisible({ connectPopup: false, loadingPopup: false });
return;
}
}, [isLoading, error]);
useEffect(() => {
return () => {
dispatch(cleanErrors());
};
}, []);
return (
<AnimatePresence mode="wait">
{connectPopup && (
<motion.div
key={1}
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<PopupConnect
isLoading={true}
logo={currentCard.image.logo}
onConnect={() => dispatch(createSession(currentCard.title))}
></PopupConnect>
</motion.div>
)}
{loadingPopup && (
<motion.div
key={2}
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<LoadingPopup logo={currentCard.image.logo}
></LoadingPopup>
</motion.div>
)}
</AnimatePresence>
);
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 B

-3
View File
@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 12L16.9996 17M12 12L17 7M12 12L7 17M12 12L7.00002 7" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 233 B

-112
View File
@@ -1,112 +0,0 @@
.navigation {
display: flex;
flex-direction: column;
color: #E3DDE5;
padding: 8px 10px;
box-sizing: border-box;
position: fixed;
top: 66px;
right: 0;
bottom: 0;
background: #0E0E0E;
width: 100%;
z-index: 3;
overflow-y: scroll;
}
.nav__close {
cursor: pointer;
object-fit: cover;
width: 24px;
}
.button__container {
}
.nav__header {
display: flex;
flex-direction: column;
gap: 42px;
}
.logo {
width: 31px;
height: 50px;
}
.button__nav_container {
margin-top: 28px;
padding: 1px;
display: flex;
gap: 20px;
align-items: center;
}
.nav__button {
appearance: none;
background-color: transparent;
color: #ebebeb;
margin: 0;
text-align: left;
border: none;
height: 13px;
-webkit-appearance: none;
padding: 0;
cursor: pointer;
}
.language__container {
border-bottom: 1px solid transparent;
}
.language__container_active {
box-sizing: border-box;
height: 20px;
border-bottom: 1px solid #EBEBEB;
}
.nav__button_active {
border-bottom: 1px solid #EBEBEB;
}
.nav__title {
font-style: normal;
font-weight: 500;
font-size: 24px;
line-height: 100%;
/* identical to box height, or 24px */
color: #FFFFFF;
margin: 0;
text-decoration: none;
}
.nav__line {
background-color: #454545;
width: 100%;
height: 1px;
margin-bottom: 40px;
}
.nav__title_container {
display: flex;
gap: 24px;
}
.icon_chevron {
width: 20px;
}
.nav__buttons {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 50px;
}
-185
View File
@@ -1,185 +0,0 @@
import './navigation.css'
import '../header/header.css'
import "../../styles/styles.css"
import write from '../footer/send.svg'
import phone from '../footer/phone.svg'
import iconButton from "../../styles/iconButton.svg"
import { AnimatePresence, motion } from "framer-motion";
import chevron from '../header/chevronIcon.svg'
type THeader = {
menuOpen?: boolean,
language: string,
setOpen?: (value: boolean) => void
changeLanguage: (language: string) => void;
setOpacity?: (value: boolean) => void;
text?: any;
}
const container = {
hidden: {
translateX: '100%',
transition: { duration: 0.2, ease: 'easeOut' }
},
show: {
translateX: '0%',
transition: { duration: 0.2, ease: 'easeIn' },
},
exit: {
translateX: '0%',
transition: { duration: 0.2, ease: 'easeOut' }
}
}
const staggeredVariants = {
hidden: {
transition: {
}
},
show: {
transition: {
staggerChildren: 0.35,
staggerDirection: -1,
}
},
exit: {
transition: {
}
}
}
const items = {
hidden: {
opacity: 0,
},
show: {
opacity: 1,
transition: { ease: 'easeIn' },
},
exit: {
opacity: 1,
}
}
export const Nav: React.FC<THeader> = ({ menuOpen, language, changeLanguage, text }) => {
const userLanguage = language === 'RU';
const { cardLarge, cardLarge1, cardSmall, cardSmall1 } = text
const langArray = [{
value: 'ru-RU',
name: 'RU'
},
{
value: 'en-EN',
name: 'EN'
}]
function langActive(lang: string, language: string) {
if (lang === language) {
return 'language__container_active'
}
else {
return 'language__container'
}
}
return (
<AnimatePresence>
{menuOpen && (
<motion.nav
variants={container}
initial={'hidden'}
animate={'show'}
exit={'hidden'}
className={`navigation`}>
<motion.div
variants={staggeredVariants}
initial={'hidden'}
animate={'show'}
exit={'exit'}
className='nav__buttons'>
<motion.div
variants={items}
className='nav__header'>
<div className='button__nav_container'>
{langArray.map((lang, i) => (
<div className={langActive(lang.name, language)}>
<button value={lang.value} key={i} onClick={() => changeLanguage(lang.name)} className='nav__button'>{lang.name}</button>
</div>
))}
</div>
<div className='nav__title_container'>
<a target='_blank' rel="noreferrer" href={userLanguage ? 'https://graff.tech/' : 'http://en.graff.tech/'} className='nav__title'>{text.captionCompain}</a>
<img alt='icon' className='icon_chevron' src={chevron}></img>
</div>
<span className='nav__line'></span>
</motion.div>
<motion.a
href={userLanguage ? 'mailto:info@graff.tech' : 'mailto:waseem@graff.tech'}
variants={items}
className='footer__card_2'>
<div className='ellipse__container'>
<div className="main-block__icon_container">
<img alt='icon' src={iconButton} className="main-block__icon"></img>
</div>
</div>
<div className={'card__write'}>
<img alt='icon' src={write} className='card__icon'></img>
<p className='card__caption'>{cardSmall.caption}</p>
</div>
</motion.a>
<motion.a
href={userLanguage ? 'tel:+7-800-770-00-76' : 'tel:+971-50-938-8902'}
variants={items}
className='footer__card_2'>
<div className='ellipse__container'>
<div className="main-block__icon_container">
<img alt='icon' src={iconButton} className="main-block__icon"></img>
</div>
</div>
<div className='card__write'>
<img alt='icon' src={phone} className='card__icon'></img>
<p className='card__caption'>{cardSmall1.caption}</p>
</div>
</motion.a>
<motion.div
variants={items}
className={'footer__card_l'}>
<div className='card__contacts'>
<p className='card__email'>info@graff.tech</p>
<p className='card__tel'>+7 800 770 00 76</p>
</div>
<div className='card__adress'>
<p className='card__street'>{cardLarge.address}</p>
<p className='card_city'>{cardLarge.city}</p>
</div>
<p className='card_country'>{cardLarge.country}</p>
</motion.div>
<motion.div
variants={items}
className='footer__card_l'>
<div className='card__contacts'>
<p className='card__email'>waseem@graff.tech</p>
<p className='card__tel'>+971 50 938 8902</p>
</div>
<div className='card__adress card__adress_uae'>
<p className='card_city'>{cardLarge1.address}</p>
</div>
<p className='card_country'>{cardLarge1.country}</p>
</motion.div>
</motion.div>
</motion.nav>
)}
</AnimatePresence>
)
}
-53
View File
@@ -1,53 +0,0 @@
import "./sharePopup.css";
import { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
export const PopupShare: React.FC<any> = ({ setClose }) => {
const [copy, setCopy] = useState(false);
function copyLink() {
navigator.clipboard.writeText(window.location.href);
setCopy((prev) => !prev);
}
function closePopup() {
setClose();
setCopy(false);
}
useEffect(() => () => setCopy(false), []);
const { t } = useTranslation();
return (
<div className="share-popup-container">
<div className="mobile-users-part-header share-header-popup">
<span className="mobile-users-part-header-title">
{t("popup-control-invite-title")}
</span>
<button
onClick={closePopup}
className="mobile-users-part-header-close-button"
></button>
</div>
<div className="share-popup-data-container">
<span className="share-popup-data-title">
{t("popup-control-link")}
</span>
<input
className="share-popup-data-input href"
value={window.location.href}
readOnly
></input>
</div>
<div className="share-popup-data-container">
<button className="share-popup-copy-button ">
<span className="share-popup-copy-button-icon"></span>
<span onClick={copyLink} className="share-popup-copy-button-title">
{copy ? t("popup-control-btn-active") : t("popup-control-btn")}
</span>
</button>
</div>
</div>
);
};
-141
View File
@@ -1,141 +0,0 @@
.share-popup-container {
position: absolute;
display: flex;
flex-direction: column;
width: 494px;
height: 469px;
box-sizing: border-box;
border: 1px solid #4F4F4F;
border-radius: 32px;
top: calc(50% - 469px / 2);
left: calc(50% - 494px / 2);
background-color: #333333;
color: #FFFFFF;
padding: 48px;
gap: 24px;
}
.share-header-popup {
padding-bottom: 0px;
}
.border-line {
width: 80%;
height: 1px;
background-color: #404040;
}
.share-popup-data-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.share-popup-data-title {
font-weight: 600;
font-size: 18px;
line-height: 22px;
user-select: none;
}
.share-popup-data-input {
background-color: #4F4F4F;
outline: none;
width: 100%;
border: none;
border-radius: 12px;
color: #FFFFFF;
box-sizing: border-box;
}
.mobile-users-part-header-title {
display: flex;
gap: 12px;
align-items: center;
font-weight: 400;
font-size: 22px;
line-height: 130%;
-webkit-user-select: none;
user-select: none;
}
.mobile-users-part-header-close-button {
width: 15px;
height: 15px;
border: none;
background-color: transparent;
background: url(../../images/icons/close.svg) 50% 50% no-repeat;
background-size: 100% 100%;
cursor: pointer;
}
.share-popup-data-input.code {
height: 76px;
font-weight: 300;
font-size: 38px;
line-height: 46px;
letter-spacing: 0.3em;
text-align: center;
padding: 15px 0;
}
.share-popup-data-input.href {
height: 48px;
padding: 14.5px 16px;
font-weight: 400;
font-size: 16px;
line-height: 19px;
}
.share-popup-copy-button {
display: flex;
box-sizing: border-box;
width: 176px;
height: 48px;
align-items: center;
justify-content: center;
border-radius: 12px;
background-color: #567ECE;
border: none;
color: #FFFFFF;
font-weight: 500;
font-size: 16px;
line-height: 20px;
gap: 4px;
cursor: pointer;
transition: .3s;
}
.share-popup-copy-button:hover {
opacity: .7;
}
.share-popup-copy-button.copied {
background-color: #219653;
transition: .3s;
}
.share-popup-copy-button-icon {
width: 24px;
height: 24px;
background: url('copyIcon.svg') 50% 50% no-repeat;
background-size: 100% 100%;
}
.show-share-popup-enter {
opacity: 0;
}
.show-share-popup-enter-active {
opacity: 1;
transition: .3s;
}
.show-share-popup-exit {
opacity: 1;
}
.show-share-popup-exit-active {
opacity: 0;
transition: .3s;
}
@@ -1,10 +0,0 @@
import { Route, Redirect } from "react-router-dom";
export const ProtectedComponent: React.FC<any> = ({ children, ...props }) => {
console.log(props)
return (
<Route>
{() => (props.currentCard ? children : <Redirect to="/" />)}
</Route>
)
}
-4
View File
@@ -1,4 +0,0 @@
export const UserList: React.FC<any> = ({ selected, setSelected }) => {
return <></>;
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

@@ -1,33 +0,0 @@
import "../../styles/styles.css"
import './demonstration.css'
import building from './building.png'
import iconButton from "./iconButton.svg"
export const Demostration: React.FC<any> = ({text}) => {
const { card } = text;
return (
<div className="block">
<a href='https://stream.graff.tech/' target='_blank' rel="noreferrer" className='demos_container demos__route'>
<div className="main-block__icon_container demo__icon">
<img alt="icon" src={iconButton} className="main-block__icon"></img>
</div>
<div className='demo'>
<img alt='buildingImg' src={building} className='demo_image'></img>
<div className='demo_info'>
<div className='title__block'>
<h4 className='title__demo'>{card.title}</h4>
<p className='caption'>{card.subTitle}</p>
</div>
<div className='body__block'>
<p className='caption1'>{card.caption}</p>
<p className='caption2'>{card.caption1}</p>
</div>
</div>
</div>
</a>
</div>
)
}
@@ -1,152 +0,0 @@
.demonstration__title {
margin: 0 0 44px 0;
font-style: normal;
font-weight: 400;
font-size: 40px;
line-height: 100%;
/* or 40px */
color: #FFFFFF;
}
.demos-card__container {
display: flex;
flex-direction: row;
gap: 24px;
}
.demo__icon {
right: 36px;
top: 36px;
}
.demo-card__header {
position: relative;
}
.demos-card {
background: #1E1E1E;
border-radius: 24px;
width: 384px;
}
.block {
margin-top: 80px;
display: flex;
flex-direction: column;
gap: 20px;
}
.demo-card__image {
width: 100%;
height: 300px;
border-radius: 24px;
}
.demo-card__info {
padding: 32px;
display: flex;
flex-direction: column;
gap: 20px;
}
.demo-card__icon {
position: absolute;
top: 0;
right: 0;
width: 16px;
}
.demo-card__title {
font-weight: 400;
font-size: 22px;
line-height: 130%;
color: #EBEBEB;
margin: 0 0 5px 0;
}
.demo-card__subtitle {
font-weight: 400;
font-size: 14px;
line-height: 130%;
/* or 18px */
/* Landing/White */
color: #EBEBEB;
opacity: 0.5;
}
.demo-card__text {
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 130%;
/* or 21px */
/* Landing/White */
margin: 0 0 10px 0;
color: #EBEBEB;
}
.demo-card__subtext {
font-weight: 400;
font-size: 14px;
line-height: 130%;
/* or 18px */
/* Landing/White */
color: #EBEBEB;
opacity: 0.5;
}
.demos-card {
cursor: pointer;
}
.demos-card:hover>div>div>div>img {
transition: opacity ease-out 0.2s;
opacity: 1;
}
.demos__route {
width: 100%;
}
@media screen and (max-width: 1024px) {
.block {
padding: 0 20px 80px;
}
.demos-card__container {
flex-direction: column;
}
.demos-card {
display: flex;
flex-direction: row;
width: 100%;
}
.demo-card__image {
width: 200px;
height: auto;
}
}
@media screen and (max-width: 639px) {
.demo__icon {
right: 19px;
top: 186px;
}
}
@@ -1,3 +0,0 @@
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.35356 9.35355C5.1583 9.54881 4.84171 9.54882 4.64645 9.35356C4.45119 9.1583 4.45118 8.84171 4.64644 8.64645L5.35356 9.35355ZM9 4.99993L9.35355 4.64637C9.44732 4.74014 9.5 4.86731 9.5 4.99992C9.5 5.13253 9.44732 5.25971 9.35356 5.35348L9 4.99993ZM4.64645 1.35356C4.45119 1.1583 4.45118 0.841714 4.64644 0.64645C4.8417 0.451186 5.15829 0.451183 5.35355 0.646443L4.64645 1.35356ZM1 5.49993C0.723858 5.49993 0.5 5.27607 0.5 4.99993C0.5 4.72378 0.723858 4.49993 1 4.49993V5.49993ZM4.64644 8.64645L8.64644 4.64638L9.35356 5.35348L5.35356 9.35355L4.64644 8.64645ZM8.64645 5.35348L4.64645 1.35356L5.35355 0.646443L9.35355 4.64637L8.64645 5.35348ZM9 5.49993H1V4.49993H9V5.49993Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 801 B

-37
View File
@@ -1,37 +0,0 @@
import "./Card.css";
import "../../styles/styles.css";
import iconButton from "./iconButton.svg";
import { useAppSelector } from "../../hooks/redux";
export const Card: React.FC<any> = ({ item, onClick }) => {
const { currentLang } = useAppSelector((state) => state.languageReducer);
console.log(currentLang);
return (
<div onClick={() => onClick()} className="card">
<img className="card-image" src={item.image.preview} alt='building' />
<div className="card-body">
<div className="card-header">
<img src={iconButton} className="card-icon" alt="лого" />
<div className="card-name">
<p className="caption-name">{currentLang === "ru" ? item.title_full.ru : item.title_full.en}</p>
<p className="card-location">{currentLang === "ru" ? item.location.ru : item.location.en}</p>
</div>
</div>
<div className="card-description-block">
<p className="card-description"> {currentLang === "ru"
? item.description.ru
: item.description.en}
</p>
{item.description_second ? <p className="card-description-second">{currentLang === "ru"
? item.description_second.ru
: item.description_second.en}
</p> : <p className="card-description-empty"></p>}
</div>
</div>
</div>
);
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 602 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 KiB

-3
View File
@@ -1,3 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 12L16.9996 17M12 12L17 7M12 12L7 17M12 12L7.00002 7" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 233 B

-122
View File
@@ -1,122 +0,0 @@
import React, { CSSProperties, useState } from "react";
import logo from "./logoIcon.svg";
import chevron from "./chevronIcon.svg";
import "./header.css";
import "../../styles/styles.css";
import { useLocation } from "react-router-dom";
import { useAppSelector, useAppDispatch } from "../../hooks/redux";
import { languageSlice } from "../../store/reducers/languageSlice";
export type THeader = {
language: string;
changeLanguage: (language: string) => void;
};
export const Header: React.FC = ({ }) => {
const [open, setOpen] = useState<boolean>(false);
const [menuOpen, setMenuOpen] = useState(false);
const [opacity, setOpacity] = useState(false);
const location = useLocation();
const { handleChangeLanguage } = languageSlice.actions;
const { currentLang } = useAppSelector((state) => state.languageReducer);
const dispatch = useAppDispatch();
function handleUpdateLanguage(value: string) {
dispatch(handleChangeLanguage(value));
setOpen((prev) => !prev);
}
function handleOpen() {
setOpen((prev) => !prev);
}
function handleOpacity() {
setOpacity(true);
}
const style = {
background: "rgba(235, 235, 235, 0.2",
};
const iconTransform = {
transform: "rotate(-90deg)",
} as CSSProperties;
const setBackground = opacity && (!open as boolean);
function setOpacityAndMenu() {
setMenuOpen(true);
const targetElement = document.querySelector("body") as HTMLElement;
targetElement.style.overflow = "hidden";
}
function openMenu() {
setMenuOpen(false);
const targetElement = document.querySelector("body") as HTMLElement;
targetElement.style.overflow = "visible";
}
return (
<div
className={
location.pathname === "connect-page" ? "header" : "header-main"
}
>
<div className="header-container">
<a target='_blank' href="https://estate.graff.tech/" style={{cursor: "pointer"}}><img className="header-logo" alt="company-logo" src={logo} />
</a>
<div className="header-buttons">
<div
className={
open
? "header-lang-button header-lang-button_active"
: "header-lang-button header-lang-button_disabled"
}
style={setBackground ? style : { background: "transparent" }}
>
<img
alt="img"
src={chevron}
className="header-select__icon"
style={
open
? iconTransform
: {
transform: "rotate(0deg)",
}
}
></img>
<div className="wrapper__button">
<div
onMouseEnter={handleOpacity}
onMouseLeave={() => setOpacity(false)}
onClick={handleOpen}
className=" header-lang-button-picked"
>
{currentLang.toLocaleUpperCase()}
</div>
</div>
{currentLang === "ru" ? (
<button
value={"en"}
onClick={(e: any) => handleUpdateLanguage(e.target.value)}
className="header-lang-button-text"
>
EN
</button>
) : (
<button
onClick={(e: any) => handleUpdateLanguage(e.target.value)}
value={"ru"}
className="header-lang-button-text"
>
RU
</button>
)}
</div>
</div>
</div>
</div>
);
};
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

@@ -0,0 +1,79 @@
.content__container {
height: 100vh;
width: 400px;
display: flex;
justify-content: center;
align-items: center;
border-width: 0px 2px;
border-style: solid;
border-color: #23242a;
}
.popup-container {
display: flex;
justify-content: center;
}
.popup {
border-radius: 4px;
width: 100%;
background: transparent;
padding: 56px;
box-sizing: border-box;
z-index: 99;
}
.card-title-container {
margin-top: 136px;
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 64px;
}
.popup-img-container {
height: 94px;
width: 100%;
padding: 0 21px 16px 0;
margin-bottom: 32px;
box-sizing: border-box;
}
.popup-logo {
height: 78px;
width: 267px;
object-fit: contain;
}
.popup-button-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.line {
height: 1px;
background-color: #23242A;
width: 100%;
}
@media screen and (max-width: 1000px) {
.popup-img-container {
margin-bottom: 16px;
}
}
@media screen and (max-width: 640px) {
.popup-connect {
padding: 16px;
}
}
@@ -0,0 +1,28 @@
import "./ConnectComponent.css";
import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";
export const ConnectComponent: React.FC<any> = ({ logo, isLoading, title, handleConnect }) => {
const history = useHistory();
const { t } = useTranslation();
return (
<div className="popup">
<div className="popup-img-container">
<img className="popup-logo" src={logo} alt="лого" />
</div>
<div className="popup-button-container">
<button disabled={isLoading} onClick={() => handleConnect(title)} className="button button-primary">
{t("popup-main-btn-start")}
</button>
<div className="line"></div>
<button onClick={() => history.goBack()} disabled={isLoading} className="button button-type-small">
{t("popup-main-select")}
</button>
</div>
</div>
);
};
@@ -1,8 +1,4 @@
.loader-container {
width: 100%;
background: transparent;
padding: 56px;
box-sizing: border-box;
.popup-type-loader {
display: flex;
flex-direction: column;
gap: 58px;
@@ -14,6 +10,22 @@
flex-direction: column;
}
.popup-img-container {
height: 94px;
width: 100%;
padding: 0 21px 16px 0;
margin-bottom: 32px;
box-sizing: border-box;
}
.popup-logo {
height: 78px;
width: 267px;
object-fit: contain;
}
.loading-caption {
font-style: normal;
font-weight: 400;
@@ -0,0 +1,21 @@
import "./LoaderComponent.css";
import { useTranslation } from "react-i18next";
export const LoaderComponent: React.FC<any> = ({ logo }) => {
const { t } = useTranslation();
return (
<div className="popup popup-type-loader">
<div className="loading-logo-container">
<div className="popup-img-container">
<img className="popup-logo" src={logo} alt="лого" />
</div>
<span className="loading-caption">{t('popup-loading')}</span>
</div>
<span className="loader"></span>
</div>
);
};
@@ -0,0 +1,52 @@
import { useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { popupAnimation } from "utils/animationProps";
import { ConnectComponent } from "components/pages/ConnectPage/ConnectComponent/ConnectComponent";
import { LoaderComponent } from "components/pages/ConnectPage/LoaderComponent/LoaderComponent";
import { useAppDispatch } from "hooks/redux";
export const SessionStartComponent: React.FC<any> = ({isLoading, handleConnect, currentCard, cleanErrors}) => {
const dispatch = useAppDispatch();
useEffect(() => {
return () => {
dispatch(cleanErrors());
};
}, []);
return (
<AnimatePresence mode="wait">
{!isLoading && (
<motion.div
key={1}
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<ConnectComponent
title={currentCard.app_title}
isLoading={isLoading}
logo={currentCard.logo}
handleConnect={handleConnect}
></ConnectComponent>
</motion.div>
)}
{isLoading && (
<motion.div
key={2}
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<LoaderComponent logo={currentCard.logo}></LoaderComponent>
</motion.div>
)}
</AnimatePresence>
);
};
@@ -8,7 +8,7 @@
.card-image {
border-radius: 4px;
object-fit: cover;
width: 100%;
height: 364px;
min-height: 260px;
+30
View File
@@ -0,0 +1,30 @@
import "./Card.css";
export const Card: React.FC<any> = ({ item, onClick }) => {
return (
<div onClick={() => onClick()} className="card">
<img className="card-image" src={item.preview} alt='building' />
<div className="card-body">
<div className="card-header">
<img src={item.icon} className="card-icon" alt="лого" />
<div className="card-name">
<h2 className="caption-name">{item.title}</h2>
<h2 className="card-location">{item.location}</h2>
</div>
</div>
<div className="card-description-block">
<p className="card-description">
{item.description}
</p>
{item.feature_description ? <p className="card-description-second">
{item.feature_description}
</p> : <p className="card-description-empty"></p>}
</div>
</div>
</div>
);
};

Before

Width:  |  Height:  |  Size: 864 B

After

Width:  |  Height:  |  Size: 864 B

+18
View File
@@ -0,0 +1,18 @@
<svg width="158" height="46" viewBox="0 0 158 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.4185 28.2237L27.7756 25.8035C26.7887 26.0609 25.8072 26.3338 24.8311 26.6222C24.3508 25.0028 23.8303 23.3976 23.2697 21.8068C20.5345 14.1008 16.8199 6.77268 12.2156 0L10.1248 1.39958C14.6103 7.99685 18.2283 15.1359 20.8907 22.6434C21.4441 24.2056 21.955 25.7815 22.4232 27.3711C18.1602 28.7491 13.9995 30.4214 9.97168 32.376L16.8926 18.5948L14.6379 17.4794L6.17355 34.3207C4.07608 35.454 2.01823 36.6658 0 37.9564L1.36689 40.0602C2.28536 39.4727 3.21345 38.9013 4.15113 38.346L0.959882 44.6968L3.21644 45.814L8.07892 36.1457C12.8901 33.5957 17.9164 31.4671 23.1004 29.7842C24.4945 35.0348 25.4254 40.396 25.8828 45.8069L28.4041 45.5941C27.9318 40.0028 26.9677 34.4633 25.5226 29.0388C26.4831 28.7552 27.4532 28.4859 28.4329 28.2309" fill="#F2F2F2"/>
<path d="M125.712 28.2183H127.115V17.6616H125.712V28.2183ZM131.894 17.6616L127.318 22.7076L127.275 22.7487L127.322 22.7916L132.824 28.2129H134.612L129.017 22.6986L133.591 17.6562L131.894 17.6616Z" fill="#F2F2F2"/>
<path d="M115.989 22.9526C115.984 21.8684 116.412 20.8264 117.18 20.0556C117.948 19.2847 118.994 18.8481 120.086 18.8414C121.02 18.8353 121.943 19.0375 122.787 19.4331V17.9477C121.923 17.6277 121.008 17.4654 120.086 17.4687C119.354 17.4555 118.626 17.5873 117.946 17.8564C117.266 18.1254 116.646 18.5262 116.124 19.0355C115.601 19.5448 115.186 20.1523 114.903 20.8225C114.62 21.4928 114.474 22.2123 114.474 22.9392C114.474 23.666 114.62 24.3855 114.903 25.0558C115.186 25.726 115.601 26.3335 116.124 26.8428C116.646 27.3521 117.266 27.7529 117.946 28.0219C118.626 28.291 119.354 28.4228 120.086 28.4096C121.008 28.4129 121.923 28.2506 122.787 27.9306V26.4631C121.943 26.8601 121.02 27.063 120.086 27.0565C118.994 27.0499 117.948 26.6132 117.18 25.8424C116.412 25.0715 115.984 24.0296 115.989 22.9454" fill="#F2F2F2"/>
<path d="M44.3371 20.4106L46.201 24.2053H42.4713L44.3371 20.4106ZM39.0586 28.2181H40.4993L41.8572 25.4565H46.8169L48.173 28.2181H49.6138L44.3353 17.4756L39.0586 28.2181Z" fill="#F2F2F2"/>
<path d="M60.3495 17.4726L52.7965 25.1801V17.6621H51.3936V28.4082L58.9448 20.7008V28.2188H60.3495V17.4726ZM60.9042 13.1613L60.2541 12.0889C58.3865 13.2127 56.8372 14.7891 55.7518 16.6701L56.8485 17.2903C57.8277 15.5945 59.2251 14.1737 60.9096 13.1613" fill="#F2F2F2"/>
<path d="M101.841 22.9527C101.832 23.755 101.584 24.5367 101.129 25.1995C100.673 25.8624 100.03 26.3766 99.2809 26.6775C98.5315 26.9785 97.7091 27.0527 96.9174 26.8907C96.1256 26.7288 95.3998 26.3381 94.8313 25.7676C94.2628 25.1972 93.877 24.4726 93.7226 23.685C93.5681 22.8974 93.6518 22.0821 93.9632 21.3417C94.2746 20.6012 94.7997 19.9687 95.4725 19.5239C96.1452 19.079 96.9355 18.8416 97.7439 18.8415C98.8363 18.8482 99.8813 19.2848 100.65 20.0557C101.418 20.8265 101.846 21.8685 101.841 22.9527ZM103.255 22.9527C103.255 21.8704 102.932 20.8123 102.326 19.9123C101.721 19.0122 100.86 18.3107 99.8525 17.8963C98.8452 17.482 97.7366 17.3735 96.6671 17.5845C95.5976 17.7956 94.6152 18.3167 93.8441 19.082C93.073 19.8473 92.548 20.8224 92.3353 21.8839C92.1227 22.9454 92.232 24.0457 92.6495 25.0455C93.0669 26.0453 93.7738 26.8998 94.6806 27.5009C95.5874 28.1019 96.6535 28.4226 97.7439 28.4222C99.2055 28.4222 100.607 27.846 101.641 26.8202C102.674 25.7945 103.255 24.4033 103.255 22.9527Z" fill="#F2F2F2"/>
<path d="M89.9872 20.3855C89.9872 18.7768 88.5879 17.472 86.8608 17.472C85.9714 17.4638 85.0897 17.6353 84.2693 17.976V19.3952L84.3629 19.3363C85.1039 18.9357 85.9381 18.736 86.7815 18.7571C87.8027 18.7571 88.6311 19.5186 88.6311 20.457C88.6311 21.3954 87.8027 22.1551 86.7815 22.1551H85.7892V23.4063H87.23C88.3231 23.4063 89.211 24.2303 89.211 25.2438C89.211 26.2572 88.2511 27.0813 87.0679 27.0813C85.7316 27.0813 84.5664 26.7023 83.9541 26.1446V27.6247C84.9012 28.1548 85.9735 28.425 87.0607 28.4076C89.0039 28.4076 90.5797 27.0634 90.5797 25.4028C90.5797 24.0855 89.8593 23.0792 88.8166 22.6538C89.1748 22.3913 89.4666 22.0498 89.6688 21.6561C89.8711 21.2624 89.9782 20.8274 89.9818 20.3855" fill="#F2F2F2"/>
<path d="M69.5334 25.1866C69.5334 25.6591 69.3449 26.1124 69.0091 26.4474C68.6733 26.7823 68.2176 26.9717 67.7415 26.9741H64.9249V23.4135H67.7415C68.2176 23.4158 68.6733 23.6052 69.0091 23.9402C69.3449 24.2751 69.5334 24.7284 69.5334 25.2009V25.1866ZM68.379 20.5536C68.379 20.9835 68.2069 21.3959 67.9006 21.6999C67.5943 22.004 67.1788 22.1748 66.7456 22.1748H64.9249V18.9127H66.7456C67.1787 18.9132 67.5938 19.0841 67.9001 19.3881C68.2063 19.692 68.3785 20.1041 68.379 20.5339V20.5536ZM68.8905 22.5001C69.1686 22.2449 69.3908 21.9356 69.5432 21.5915C69.6956 21.2474 69.7751 20.8758 69.7765 20.4999C69.7765 18.9377 68.4186 17.6704 66.7456 17.6704H63.5273V28.2271H67.7469C69.4902 28.2074 70.8949 26.8865 70.8949 25.2581C70.8844 24.6513 70.6873 24.0621 70.3301 23.5695C69.9728 23.0769 69.4724 22.7044 68.8959 22.5019" fill="#F2F2F2"/>
<path d="M157.447 17.4722L149.894 25.1796V17.6616H148.491V28.4078L156.042 20.7003V28.2183H157.447V17.4722ZM158 12.7086L156.905 12.0884C155.926 13.7842 154.528 15.205 152.844 16.2174L153.492 17.2898C155.36 16.166 156.909 14.5896 157.995 12.7086" fill="#F2F2F2"/>
<path d="M77.2385 20.4106L79.1024 24.2053H75.3727L77.2385 20.4106ZM71.96 28.2181H73.4007L74.7568 25.4565H79.7165L81.0726 28.2181H82.5134L77.2349 17.4756L71.96 28.2181Z" fill="#F2F2F2"/>
<path d="M111.686 25.1866C111.686 25.6607 111.496 26.1153 111.158 26.4505C110.821 26.7857 110.363 26.9741 109.885 26.9741H107.068V23.4135H109.885C110.363 23.4135 110.821 23.6018 111.158 23.937C111.496 24.2722 111.686 24.7269 111.686 25.2009V25.1866ZM110.53 20.5536C110.53 20.9835 110.358 21.3959 110.051 21.6999C109.745 22.004 109.329 22.1748 108.896 22.1748H107.075V18.9127H108.896C109.329 18.9132 109.744 19.0841 110.051 19.3881C110.357 19.692 110.529 20.1041 110.53 20.5339V20.5536ZM111.041 22.5001C111.319 22.2449 111.541 21.9356 111.694 21.5915C111.846 21.2474 111.926 20.8758 111.927 20.4999C111.927 18.9377 110.571 17.6704 108.896 17.6704H105.669V28.2271H109.888C111.632 28.2074 113.038 26.8865 113.038 25.2581C113.027 24.6511 112.83 24.0619 112.472 23.5693C112.115 23.0767 111.614 22.7043 111.037 22.5019" fill="#F2F2F2"/>
<path d="M145.32 17.4722L137.769 25.1796V17.6616H136.364V28.4078L143.915 20.6985V28.2165H145.32V17.4722Z" fill="#F2F2F2"/>
<path d="M51.6 35.25H50.1953V45.7959H51.6V35.25Z" fill="#F2F2F2"/>
<path d="M61.9211 36.5012V35.25H54.0078V36.5012H57.2639V45.7959H58.6668V36.5012H61.9211Z" fill="#F2F2F2"/>
<path d="M71.2448 35.2412L68.1094 39.8546H68.0968L64.9614 35.2412H63.3027L67.4016 41.272V45.7961H68.8045V41.272L72.9034 35.2412H71.2448Z" fill="#F2F2F2"/>
<path d="M40.476 40.5234C40.4708 39.4392 40.8993 38.3972 41.6675 37.6264C42.4357 36.8555 43.4808 36.4189 44.5731 36.4122C45.5072 36.4053 46.4307 36.6082 47.2745 37.0057V35.5382C46.4106 35.218 45.4953 35.0557 44.5731 35.0591C43.1286 35.0851 41.752 35.6728 40.7397 36.6959C39.7274 37.719 39.1602 39.0956 39.1602 40.5296C39.1602 41.9636 39.7274 43.3402 40.7397 44.3633C41.752 45.3864 43.1286 45.9742 44.5731 46.0001C45.4953 46.0035 46.4106 45.8413 47.2745 45.5211V44.0446C46.4307 44.4421 45.5072 44.645 44.5731 44.6381C43.4809 44.631 42.4361 44.1941 41.668 43.4234C40.8999 42.6526 40.4713 41.611 40.476 40.5269" fill="#F2F2F2"/>
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

@@ -0,0 +1,57 @@
.calender-container {
width: 312px;
}
.calendar-title {
margin-top: 0;
margin-bottom: 32px;
}
.weekday-table {
user-select: none;
background: transparent;
cursor: auto;
padding: 3px 5px;
width: 100%;
height: 40px;
border-radius: 4px;
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 140%;
color: #f2f2f2;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
background-color: transparent;
}
.calendar {
height: auto;
}
.calendar-header {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
}
.calendar-nav {
border-radius: 8px;
cursor: pointer;
}
.calendar-cell_active {
cursor: pointer;
background: rgba(86, 126, 206, 0.2);
}
.calendar-nav:hover {
background: rgba(86, 126, 206, 0.2);
}
.calendar-nav-left {
transform: rotate(180deg);
}
@@ -0,0 +1,136 @@
import "./CalendarComponent.css";
import chevron from "images/icons/ChevronRight.svg";
import * as moment from "moment";
import "moment/locale/ru";
import { motion, useAnimationControls } from "framer-motion";
import { useState, useEffect } from "react";
import { planSlice } from "store/reducers/planSlice";
import { useAppDispatch, useAppSelector } from "hooks/redux";
import { variantsCalendar } from "utils/animationProps.js";
export const CalendarComponent: React.FC<any> = ({ time, pickedTime }) => {
const controls = useAnimationControls();
const dispatch = useAppDispatch();
const { handleNavigation, handleTimeValue } = planSlice.actions;
const [calendar, setCalendar] = useState([]);
const [value, setValue] = useState(time);
const currDate = time;
const startDay = value.clone().startOf("month").startOf("week");
const endDay = value.clone().endOf("month").endOf("week");
const weekDays = ["ПН", "ВT", "СР", "ЧТ", "ПТ", "СБ", "ВС"];
useEffect(() => {
controls.start({
opacity: 1,
});
}, [nextMonth, prevMonth]);
useEffect(() => {
const day = startDay.clone().subtract("1", "day");
const a = [];
while (day.isBefore(endDay, "day")) {
a.push(
Array(7)
.fill(0)
.map(() => day.add(1, "day").clone())
);
}
setCalendar(a);
}, [value]);
function prevMonth() {
return value.clone().subtract(1, "month");
}
function nextMonth() {
return value.clone().add(1, "month").startOf("month");
}
function thisMonth() {
return value.isSame(new Date(), "month");
}
function hide(day: any) {
if (value.isSame(day, "month")) {
if (currDate.isSame(day, "day")) {
const cellClass = "calendar-cell_active calendar-cell";
return cellClass;
}
return "calendar-cell calendar-cell_day";
} else {
return "calendar-cell-none";
}
}
const handleValue = (day: moment.Moment) => {
const temp = currDate.clone().subtract(1, "day");
if (day.isBefore(temp)) {
return;
} else {
dispatch(
handleNavigation({
isDone: false,
isCalendar: false,
isTimepicker: true,
isForm: false,
})
);
dispatch(handleTimeValue(day));
}
};
return (
<div className="calendar-container">
<h3 className="plan-title">Выберите дату.</h3>
<div className="calendar">
<div className="calendar-header">
<img
alt="prev-mouth"
onClick={() => !thisMonth() && setValue(prevMonth())}
className="calendar-nav calendar-nav-left"
src={chevron}
></img>
<span className="calendar-date">
{value.format("MMMM")}, {value.format("YYYY")}
</span>
<img
alt="next-mouth"
onClick={() => setValue(nextMonth())}
className="calendar-nav"
src={chevron}
></img>
</div>
<div className="weekday-table">
{weekDays.map((day, i) => (
<div key={i} className="calendar-cell">
{day}
</div>
))}
</div>
{calendar.map((week, i) => (
<motion.div
initial="hidden"
animate="visible"
variants={variantsCalendar}
key={Math.random()}
className="calendar-table"
>
{week.map((day: any, i: number) => (
<div
key={i}
onClick={() => handleValue(day)}
className={hide(day)}
>
{day.format("D")}
</div>
))}
</motion.div>
))}
</div>
</div>
);
};
@@ -0,0 +1,26 @@
.finish-container {
display: flex;
flex-direction: column;
gap: 32px;
}
.finish-title {
margin: 0;
font-style: normal;
font-weight: 400;
font-size: 38px;
line-height: 100%;
/* or 38px */
/* White */
color: #f2f2f2;
}
.finish-text {
margin: 0;
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 140%;
color: #c5c7ce;
}
@@ -0,0 +1,16 @@
import "./Finish.css";
export const Finish: React.FC<any> = () => {
return (
<div className="finish-container">
<h2 className="finish-title">Просмотр запланирован.</h2>
<p className="finish-text">
Ссылка для подключения и другая дополнительная информация будут
отправлены на ваш почтовый адрес.
</p>
<button style={{ marginTop: "16px" }} className="button button-primary">
На сайт жилого комплекса
</button>
</div>
);
};
+115
View File
@@ -0,0 +1,115 @@
.form {
display: flex;
flex-direction: column;
gap: 10px;
}
.form-input-container {
display: flex;
flex-direction: column;
gap: 4px;
}
.form-input {
width: 100%;
background: #23242a;
box-sizing: border-box;
outline: none;
border-radius: 2px;
padding: 12px 16px;
font-weight: 400;
font-size: 16px;
line-height: 135%;
color: #c5c7ce;
border: 10px solid transparent;
border-image-slice: 1;
border-width: 1px;
}
.form-input-caption {
font-weight: 400;
font-size: 14px;
line-height: 140%;
color: #c5c7ce;
}
.form-input:focus {
border: linear-gradient(180deg, #bc75ff 0%, #798fff 100%);
}
.form-border {
cursor: text;
position: relative;
border: 1px solid transparent;
}
.form-border::after {
content: "";
position: absolute;
inset: 0;
border-radius: 4px;
padding: 1px;
background: transparent;
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
.form-border-error {
cursor: text;
position: relative;
border: 1px solid transparent;
}
.form-border-error::after {
content: "";
position: absolute;
inset: 0;
border-radius: 4px;
padding: 1px;
background: transparent;
-webkit-mask:
linear-gradient(#fff 0 0) content-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
.form-border-error:focus-within::after {
background: linear-gradient(180deg, #E94444 100%, #E94444 100%);
}
.form-border:focus-within::after {
background: linear-gradient(180deg, #798fff -41.07%, #d375ff 100%);
}
input:-webkit-autofill,
input:-webkit-autofill:focus {
transition: background-color 600000s 0s, color 600000s 0s;
}
.error-caption {
font-style: normal;
font-weight: 400;
font-size: 10px;
line-height: 130%;
/* identical to box height, or 13px */
height: 26px;
display: none;
/* Error */
color: #E94444;
}
.error-caption-active {
display: inline-block;
}
+91
View File
@@ -0,0 +1,91 @@
import chevron from "images/icons/ChevronLeft.svg";
import "./Form.css";
import { planSlice } from "store/reducers/planSlice";
import useFormWithValidation from "hooks/useFormWithValidation";
import { useAppDispatch, useAppSelector } from "hooks/redux";
import { useEffect, useRef, useState } from "react";
export const Form: React.FC<any> = ({ time }) => {
const inputRef = useRef()
const [error, showError] = useState(false)
const { values, handleChangeState, resetForm, errors, isValid } =
useFormWithValidation();
const dispatch = useAppDispatch();
const { handleNavigation, handleTimeValue } = planSlice.actions;
useEffect(() => {
resetForm();
}, [resetForm]);
function handleSubmit(e: React.SyntheticEvent) {
e.preventDefault();
}
const handleReturn = () => {
dispatch(
handleNavigation({
isDone: false,
isCalendar: false,
isTimepicker: true,
isForm: false,
})
);
};
return (
<div className="calendar-container">
<h3 className="plan-title">
Расскажите о себе.
</h3>
<button
onClick={(e) => handleReturn()}
style={{ width: "141px", padding: "4px 16px 4px 4px", height: "32px" }}
className="button button-type-small button-plan"
>
<img src={chevron}></img>
Выбор времени
</button>
<div className="calendar" style={{ height: "auto" }}>
<div className="calendar-header">
<span className="calendar-date">{time.format("DD MMM, LT")}</span>
</div>
<form noValidate onSubmit={handleSubmit} className="form">
<div className="form-input-container">
<label className="form-input-caption">Имя</label>
<div onClick={(e: any) => console.log(e.target.closest('input')) } className={errors.name ? "form-border-error" : 'form-border'}>
<input ref={inputRef} required onChange={handleChangeState} name='name' value={values.name} minLength={2} maxLength={30} className="form-input" type="text"></input>
</div>
<span className={errors.name ? "error-caption error-caption-active" : "error-caption"}>{errors.name}</span>
</div>
<div className="form-input-container">
<label className="form-input-caption">E-mail</label>
<div className={errors.email ? "form-border-error" : 'form-border'}>
<input required onChange={handleChangeState} name='email' value={values.email} className="form-input" type="email"></input>
</div>
<span className={errors.email ? "error-caption error-caption-active" : "error-caption"}>{errors.email}</span>
</div>
<button
type="submit"
disabled={!isValid}
style={{ marginTop: "24px" }}
className="button button-primary"
>
Запланировать
</button>
</form>
</div>
</div>
);
};
+163
View File
@@ -0,0 +1,163 @@
.content-container-plan {
height: 100vh;
width: 428px;
justify-content: center;
padding: 24px 56px;
box-sizing: border-box;
border-width: 0px 2px;
border-style: solid;
border-color: #23242a;
}
.button-plan {
margin-top: 24px;
margin-bottom: 16px;
}
.calendar-position {
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.plan-logo {
width: 158px;
height: 46px;
}
.plan-title {
margin-top: 0;
margin-bottom: 32px;
font-style: normal;
font-weight: 400;
font-size: 38px;
line-height: 100%;
color: #f2f2f2;
width: 60%;
}
.calendar-date {
font-style: normal;
font-weight: 400;
font-size: 20px;
line-height: 120%;
color: #f2f2f2;
user-select: none;
}
.calendar-table {
display: grid;
grid-template-columns: repeat(7, 1fr);
justify-content: center;
background: transparent;
gap: 5px;
}
.calendar-table:nth-child(odd) {
background: #1c1d21;
}
.calendar-cell {
user-select: none;
background: transparent;
cursor: auto;
padding: 3px 5px;
width: 100%;
height: 40px;
border-radius: 4px;
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 140%;
color: #f2f2f2;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
}
.calendar-cell_day {
cursor: pointer;
}
.calendar-cell_day:hover {
background: #23242a;
}
.calendar-cell-none {
visibility: hidden;
}
.line-calendar {
margin-top: 32px;
margin-bottom: 16px;
}
.button-plan {
margin-top: 16px;
margin-bottom: 16px;
}
.plan-title-plan {
margin: 0;
}
.plan-logo-container {
display: flex;
align-items: center;
justify-content: center;
}
@media screen and (max-width: 640px) {
.plan-title {
width: 100%;
}
.plan-logo-container {
display: none;
}
.content-container-plan {
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
gap: 45px;
padding: 0 10px;
border: none;
height: auto;
}
.calendar-position {
position: static;
transform: none;
}
.calendar {
width: 100%;
}
.plan-logo {
display: none;
}
.plan-title {
margin-top: 24px;
margin-bottom: 24px;
font-style: normal;
font-weight: 400;
font-size: 24px;
line-height: 125%;
white-space: nowrap;
}
}
@@ -0,0 +1,86 @@
import "./PlanComponent.css";
import logo from "./Aivaz.svg";
import { useState } from "react";
import * as moment from "moment";
import "moment/locale/ru";
import { motion, AnimatePresence } from "framer-motion";
import { CalendarComponent } from "./CalendarComponent/CalendarComponent";
import { TimepickerComponent } from "./TimepickerComponent/TimepickerComponent";
import { Form } from "./Form/Form";
import { Finish } from "./Finish/Finish";
import { useAppSelector } from "hooks/redux";
import { popupAnimation } from "utils/animationProps";
export const PlanComponent: React.FC<any> = () => {
const { isDone, isCalendar, isTimepicker, isForm, time, currTime } = useAppSelector(
(state) => state.planReducer
);
return (
<div className="content-container-plan">
<div className="plan-logo-container">
<img style={{ width: "158px" }} src={logo}></img>
</div>
<div className="calendar-position">
<AnimatePresence mode="wait">
{isCalendar && (
<motion.div
key={1}
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<CalendarComponent time={time}></CalendarComponent>
</motion.div>
)}
{isTimepicker && (
<motion.div
key={2}
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<TimepickerComponent time={currTime}></TimepickerComponent>
</motion.div>
)}
{isForm && (
<motion.div
key={3}
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<Form time={currTime}></Form>
</motion.div>
)}
{isDone && (
<motion.div
key={4}
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<Finish></Finish>
</motion.div>
)}
</AnimatePresence>
{!isDone && (
<>
<div className="line line-calendar"></div>
<button style={{ height: '32px' }} className="button button-type-small">
На сайт жилого комплекса
</button>
</>
)}
</div>
</div>
);
};
@@ -0,0 +1,23 @@
.timepicker-cell {
padding: 9px 18px;
width: 72px;
cursor: pointer;
}
.timepicker-cell:hover {
background: #23242a;
}
.timepicker-table {
display: grid;
justify-content: center;
background: transparent;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
}
.timepicker-table:nth-child(even) {
background: #1c1d21;
}
@@ -0,0 +1,83 @@
import chevron from "images/icons/ChevronLeft.svg";
import "./TimepickerComponent.css";
import { planSlice } from "store/reducers/planSlice";
import { useAppDispatch, useAppSelector } from "hooks/redux";
import { useEffect, useState } from "react";
export const TimepickerComponent: React.FC<any> = ({ time }) => {
const dispatch = useAppDispatch();
const { handleNavigation, handleTimeValue } = planSlice.actions;
const [timePicker, settimePicker] = useState([]);
const startTime = time.clone().hours(8).minutes(0);
const endTime = time.clone().hours(19).minutes(0);
useEffect(() => {
const temp = startTime.clone().subtract("30", "minute");
const a = [];
while (temp.isBefore(endTime, "minute")) {
a.push(
Array(8)
.fill(0)
.map(() => temp.add(30, "minutes").clone())
);
}
settimePicker(a);
}, [time]);
const handleValue = (time: moment.Moment) => {
dispatch(
handleNavigation({
isDone: false,
isCalendar: false,
isTimepicker: false,
isForm: true,
})
);
dispatch(handleTimeValue(time));
};
const handleReturn = () => {
dispatch(
handleNavigation({
isDone: false,
isCalendar: true,
isTimepicker: false,
isForm: false,
})
);
};
return (
<div className="calendar-container">
<h3 className="plan-title">Выберите время.</h3>
<button
onClick={() => handleReturn()}
style={{ width: "120px", padding: "4px 16px 4px 4px", height: "32px" }}
className="button button-type-small button-plan"
>
<img src={chevron}></img>
Выбор даты
</button>
<div className="calendar">
<div className="calendar-header">
<span className="calendar-date">{time.format("DD MMMM")}</span>
</div>
{timePicker.map((day, i) => (
<div key={i} className="timepicker-table">
{day.map((time: any, i: number) => (
<div
onClick={() => handleValue(time)}
key={i}
className="calendar-cell timepicker-cell"
>
{time.format("LT")}
</div>
))}
</div>
))}
</div>
</div>
);
};
@@ -0,0 +1,26 @@
import { ControlButton } from "components/pages/Stream/components/buttons/ControlButton/ControlButton";
import { MicroButton } from "components/pages/Stream/components/buttons/MicroButton/MicroButton";
import { ShareButton } from "components/pages/Stream/components/buttons/ShareButton/ShareButton";
import { LanguageButton } from "components/pages/Stream/components/buttons/LanguageButton/LanguageButton";
import { ExitButton } from "components/pages/Stream/components/buttons/ExitButton/ExitButton";
export const ControlPanel: React.FC<any> = ({
handleOpenSharePopup,
handleOpenExitPopup,
handleMuteClick,
handleControlClick,
isMuted,
isControl,
isSidebarWide
}) => {
return (
<div className="toolbar-field-part">
<ShareButton isSidebarWide={isSidebarWide} onClick={handleOpenSharePopup}></ShareButton>
<LanguageButton isSidebarWide={isSidebarWide} onClick={() => console.log("click!")}></LanguageButton>
<div className="toolbar-button-container-border-line"></div>
<ExitButton isSidebarWide={isSidebarWide} onClick={handleOpenExitPopup}></ExitButton>
</div>
);
};
@@ -0,0 +1,63 @@
.popup-mobile-button-container {
display: flex;
flex-direction: row;
gap: 8px;
justify-content: space-between;
}
.menu-item-button {
justify-content: space-between;
width: 50%;
background: #23242A;
border-radius: 4px;
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 140%;
/* or 22px */
display: flex;
align-items: center;
/* White */
padding: 12px;
box-sizing: border-box;
color: #F2F2F2;
}
.menu-item-container {
height: 24px;
display: flex;
flex-direction: row;
gap: 8px;
}
.menu-item-language-caption {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 130%;
/* identical to box height, or 16px */
align-items: center;
text-align: right;
/* Button_3 */
color: #73788C;
}
.menu-item-language {
display: flex;
flex-direction: row;
gap: 4px;
align-items: center;
}
.menu-item-button-exit {
position: absolute;
width: 118px;
right: 0%;
bottom: 0%;
}
@@ -0,0 +1,45 @@
import './ContolPanelMobile.css'
import close from 'images/icons/close.svg'
import share from 'images/icons/Share.svg'
import right from 'images/icons/ChevronRight.svg'
import exit from 'images/icons/exit.svg'
import language from 'images/icons/Language.svg'
import { useTranslation } from 'react-i18next'
export const ControlPanelMobile: React.FC<any> = ({ closePopup, currentLang, setPopupExit, setPopupShare, setLanguagePopup }) => {
const { t } = useTranslation()
return (
<>
<div style={{ top: "-20px", right: "-20px" }} onClick={closePopup} className="icon-close-container">
<img src={close}></img>
</div>
<h2 style={{ marginBottom: "20px" }}>{t('popup-additional')}</h2>
<div className='popup-mobile-button-container'>
<button onClick={setPopupShare} className='menu-item-button'>
<div className='menu-item-container'>
<img src={share} className='menu-item-icon' />
<span className='menu-item-caption'>{t('share-contro-btn')}</span>
</div>
<img src={right} className='menu-item-arrow' />
</button>
<button onClick={setLanguagePopup} className='menu-item-button'>
<div className='menu-item-container'>
<img src={language} className='menu-item-icon' />
<span className='menu-item-caption'>{t('language-button')}</span>
</div>
<div className="menu-item-language">
<span className='menu-item-language-caption'>{currentLang === 'ru' ? 'Русский' : 'English'}</span>
<img src={right} className='menu-item-arrow' />
</div>
</button>
</div>
<button onClick={setPopupExit} className='menu-item-button-exit button button-teritary'>
<img src={exit} className='' />
<span>{t('exit-control-btn')}</span>
</button>
</>
)
}
@@ -0,0 +1,224 @@
import './player.css'
export const Player: React.FC = () => {
return (
<>
<div id="playerUI" className="noselect">
<div id="player"></div>
</div>
<div id="overlay">
<div id="controls">
<button className="tooltip" id="fullscreen-btn">
<span className="tooltiptext">Fullscreen</span>
</button>
<button className="tooltip" id="settingsBtn">
<span className="tooltiptext">Settings</span>
</button>
<button className="tooltip" id="statsBtn">
<span className="tooltiptext">Information</span>
</button>
</div>
<div id="unrealengine">
</div>
<div id="connection" className="tooltip">
<span className="tooltiptext" id="qualityText">Not connected</span>
</div>
<div className="panel-wrap" id="settings-panel">
<div className="panel">
<div id="heading">Settings</div>
<div id="close"></div>
<div id="content">
<div id="fillWindow" className="setting">
<div className="settings-text">Enlarge display to fill window</div>
<label className="tgl-switch">
<input type="checkbox" id="enlarge-display-to-fill-window-tgl" className="tgl tgl-flat" checked />
<div className="tgl-slider"></div>
</label>
</div>
<div id="qualityControlOwnership" className="setting">
<div className="settings-text">Is quality controller?</div>
<label className="tgl-switch">
<input type="checkbox" id="quality-control-ownership-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="matchViewportResolution" className="setting">
<div className="settings-text">Match viewport resolution</div>
<label className="tgl-switch">
<input type="checkbox" id="match-viewport-res-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="offerToReceive" className="setting">
<div className="settings-text">Offer To Receive</div>
<label className="tgl-switch">
<input type="checkbox" id="offer-receive-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="preferSFU" className="setting">
<div className="settings-text">Prefer SFU</div>
<label className="tgl-switch">
<input type="checkbox" id="prefer-sfu-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="useMic" className="setting">
<div className="settings-text">Use microphone</div>
<label className="tgl-switch">
<input type="checkbox" id="use-mic-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="forceMonoAudio" className="setting">
<div className="settings-text">Force mono audio</div>
<label className="tgl-switch">
<input type="checkbox" id="force-mono-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="forceTURN" className="setting">
<div className="settings-text">Force TURN</div>
<label className="tgl-switch">
<input type="checkbox" id="force-turn-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</div>
<div id="toggleControl" className="setting">
<div className="settings-text" id="control-scheme-text">Control Scheme</div>
<label className="btn-overlay">
<label className="tgl-switch">
<input type="checkbox" id="control-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</label>
</div>
<div id="toggleCursor" className="setting">
<div className="settings-text" id="cursor-text">Hide Browser Cursor</div>
<label className="btn-overlay">
<label className="tgl-switch">
<input type="checkbox" id="cursor-tgl" className="tgl tgl-flat" />
<div className="tgl-slider"></div>
</label>
</label>
</div>
<div id="showFPS" className="setting">
<div className="settings-text">Show FPS</div>
<label className="btn-overlay">
<input type="button" id="show-fps-button" className="overlay-button btn-flat" value="Toggle" />
</label>
</div>
<div id="keyframeRequest" className="setting">
<div className="settings-text">Request KeyFrame</div>
<label className="btn-overlay">
<input type="button" id="request-keyframe-button" className="overlay-button btn-flat" value="Request" />
</label>
</div>
<section id="encoderSettings">
<div id="encoderSettingsHeader" className="settings-text">
<div>Encoder Settings</div>
</div>
<div id="encoderParamsContainer" className="collapse">
<div className="form-group">
<label htmlFor="encoder-min-qp-text">Min QP</label>
<input type="number" className="form-control" id="encoder-min-qp-text" value="0" min="0"
max="51" />
<label htmlFor="encoder-max-qp-text">Max QP</label>
<input type="number" className="form-control" id="encoder-max-qp-text" value="51" min="0"
max="51" />
<br></br>
<input id="encoder-params-submit" className="overlay-button btn-flat" type="button"
value="Apply" />
</div>
</div>
</section>
<section id="webRTCSettings">
<div id="webRTCSettingsHeader" className="settings-text">
<div>WebRTC Settings</div>
</div>
<div id="webrtcParamsContainer" className="collapse">
<div className="form-group">
<label htmlFor="webrtc-fps-text">FPS</label>
<input type="number" className="form-control" id="webrtc-fps-text" value="60" min="1"
max="999" />
<label htmlFor="webrtc-min-bitrate-text">Min bitrate (kbps)</label>
<input type="number" className="form-control" id="webrtc-min-bitrate-text" value="0" min="0"
max="100000" />
<label htmlFor="webrtc-max-bitrate-text">Max bitrate (kbps)</label>
<input type="number" className="form-control" id="webrtc-max-bitrate-text" value="0" min="0"
max="100000" />
<br></br>
<input id="webrtc-params-submit" className="overlay-button btn-flat" type="button"
value="Apply" />
</div>
</div>
</section>
<section id="streamSettings">
<div id="streamSettingsHeader" className="settings-text">
<div>Stream Settings</div>
</div>
<div id="streamSettingsContainer" className="collapse">
<div className="form-group">
<div className="settings-text">Player stream</div>
<select className="form-control" id="stream-select"></select>
<div className="settings-text">Player track</div>
<select className="form-control" id="track-select"></select>
</div>
</div>
</section>
<br></br>
<section id="connectionSettings">
<div id="connectionHeader" className="settings-text">
<div>Stream Settings</div>
</div>
<div id="connectionContainer" className="collapse">
<div className="setting">
<div className="settings-text"></div>
<label className="btn-overlay">
<input type="button" id="restart-stream-button" className="overlay-button btn-flat" value="Restart stream" />
</label>
</div>
</div>
</section>
</div>
</div>
</div>
<div className="panel-wrap" id="stats-panel">
<div className="panel">
<div id="heading">Information</div>
<div id="close"></div>
<div id="content">
<section id="statsPanel">
<div className="setting settings-text">
<div>Session Stats</div>
</div>
<div id="statsContainer" className="statsContainer">
<div id="stats" className="stats"></div>
</div>
</section>
<br></br>
<section id="latencyTest">
<div className="setting">
<div className="settings-text">
<div>Latency Report</div>
</div>
<label className="btn-overlay">
<input type="button" id="test-latency-button" className="overlay-button btn-flat"
value="Get Report" />
</label>
</div>
<div id="latencyStatsContainer" className="statsContainer">
<div className="stats">No report yet</div>
</div>
</section>
</div>
</div>
</div>
</div>
</>
)
}
@@ -0,0 +1,709 @@
#loader {
width: 106px;
height: 106px;
border-radius: 50%;
display: inline-block;
position: relative;
background: conic-gradient(from 135deg at 50% 50%,
rgba(255, 255, 255, 0) -6.26deg,
#ffffff 314.83deg,
rgba(255, 255, 255, 0) 353.74deg,
#ffffff 674.83deg);
box-sizing: border-box;
animation: rotation 1s linear infinite;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
#loader::after {
content: "";
box-sizing: border-box;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 100px;
height: 100px;
border-radius: 50%;
background: #151619;
}
h2 {
font-family: "GilroyWebRegular";
}
#playerUI {
width: 100%;
height: 100%;
}
canvas {
image-rendering: crisp-edges;
position: absolute;
}
video {
position: absolute;
width: 100%;
height: 100%;
}
#player {
width: 100%;
height: 100%;
position: absolute;
}
#videoPlayOverlay {
position: absolute;
font-size: 1.8em;
width: 100%;
height: 100%;
color: var(--colour2)
}
/* State for element to be clickable */
.clickableState {
align-items: center;
justify-content: center;
display: flex;
}
/* State for element to show text, this is for informational use*/
.textDisplayState {
align-items: center;
justify-content: center;
display: flex;
cursor: pointer;
}
/* State to hide overlay, WebRTC communication is in progress and or is playing */
.hiddenState {
display: none;
}
#playButton {
display: block;
width: 88px;
height: 88px;
z-index: 30;
backdrop-filter: blur(10px);
border-radius: 112px;
cursor: pointer;
}
#container {
width: 400px;
height: 100%;
justify-content: center;
/* Background */
background: transparent;
/* Button_1 */
border-width: 0px 2px;
border-style: solid;
border-color: #23242A;
display: flex;
flex-direction: column;
align-items: center;
gap: 40px;
padding: 40px 56px;
box-sizing: border-box;
}
@media screen and (max-width: 500px) {
#container {
width: 100%;
border: none;
}
}
#playButton:hover {
background: linear-gradient(180deg, #BC75FF 0%, #798FFF 100%);
backdrop-filter: blur(10px)
}
#title {
font-style: normal;
font-weight: 400;
font-size: 38px;
line-height: 100%;
/* or 38px */
/* White */
color: #F2F2F2;
margin: 0 0 16px 0;
}
#caption {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 140%;
/* or 22px */
text-align: center;
/* Inactive */
color: #C5C7CE;
margin: 0;
text-align: left;
}
#link {
font-family: 'Inter';
cursor: pointer;
background: #1C1D21;
border-radius: 4px;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
text-decoration: none;
padding: 8px 16px;
box-sizing: border-box;
font-weight: 400;
font-size: 12px;
line-height: 130%;
/* identical to box height, or 16px */
/* Inactive */
color: #C5C7CE;
}
#link:hover {
background: #23242A;
}
#freezeFrameOverlay {
background-color: transparent;
}
.freezeframeBackground {
background-color: #000 !important;
}
#overlay {
width: 100%;
height: 100%;
z-index: 20;
position: absolute;
color: var(--colour2);
pointer-events: none;
overflow: hidden;
}
#overlay button {
background-color: var(--colour7);
border: 1px solid var(--colour7);
color: var(--colour2);
position: relative;
width: 3rem;
height: 3rem;
padding: 0.5rem;
text-align: center;
}
#fullscreen-btn {
padding: 0.6rem !important;
}
#overlay button:hover {
background-color: var(--colour3);
border: 3px solid var(--colour3);
transition: 0.25s ease;
padding-left: 0.55rem;
padding-top: 0.55rem;
}
#overlay button:active {
border: 3px solid var(--colour3);
background-color: var(--colour7);
padding-left: 0.55rem;
padding-top: 0.55rem;
}
#overlay img {
width: 100%;
height: 100%;
}
.tooltip .tooltiptext {
visibility: hidden;
width: auto;
color: var(--colour2);
text-align: center;
border-radius: 15px;
padding: 0px 10px;
font-family: 'Montserrat', sans-serif;
font-size: 0.75rem;
letter-spacing: 0.75px;
/* Position the tooltip */
position: absolute;
top: 0;
transform: translateY(25%);
left: 125%;
z-index: 20;
}
.tooltip:hover .tooltiptext {
visibility: visible;
background-color: var(--colour7);
}
#connection .tooltiptext {
top: 125%;
transform: translateX(-25%);
left: 0;
z-index: 20;
padding: 5px 10px;
}
#settings-panel .tooltiptext {
display: block;
top: 125%;
transform: translateX(-50%);
left: 0;
z-index: 20;
padding: 5px 10px;
border: 3px solid var(--colour5);
width: max-content;
}
#controls {
position: absolute;
top: 2%;
left: 1%;
font-family: 'Michroma', sans-serif;
pointer-events: all;
display: none;
}
#controls>* {
margin-bottom: 0.5rem;
border-radius: 50%;
display: block;
height: 2rem;
line-height: 1.75rem;
padding: 0.5rem;
}
#controls #additionalinfo {
text-align: center;
font-family: 'Montserrat', sans-serif;
}
#unrealengine {
position: absolute;
bottom: 5%;
right: 10%;
font-family: 'Michroma', sans-serif;
pointer-events: all;
visibility: hidden;
width: min-content;
}
#unrealengine p {
visibility: hidden;
width: 15rem;
}
#connection {
position: absolute;
bottom: 5%;
left: 10%;
font-family: 'Michroma', sans-serif;
height: 3rem;
width: 3rem;
pointer-events: none;
visibility: hidden;
}
.noselect {
-webkit-touch-callout: none;
/* iOS Safari */
-webkit-user-select: none;
/* Safari */
-khtml-user-select: none;
/* Konqueror HTML */
-moz-user-select: none;
/* Old versions of Firefox */
-ms-user-select: none;
/* Internet Explorer/Edge */
user-select: none;
/* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
}
.panel-wrap {
position: fixed;
top: 0;
bottom: 0;
right: 0;
height: 100%;
min-width: 20vw;
transform: translateX(100%);
transition: .3s ease-out;
pointer-events: all;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
overflow-y: auto;
overflow-x: hidden;
background-color: rgba(30, 29, 34, 0.5)
}
.panel-wrap-visible {
transform: translateX(0%);
}
.panel {
color: #eee;
overflow-y: auto;
padding: 1em;
}
#heading {
display: inline-block;
font-size: 2em;
margin-block-start: 0.67em;
margin-block-end: 0.67em;
margin-inline-start: 0px;
margin-inline-end: 0px;
position: relative;
padding: 0 0 0 2rem;
}
#close {
margin: 0.5rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
padding-right: 0.5rem;
font-size: 2em;
float: right;
}
#close:after {
padding-left: 0.5rem;
display: inline-block;
content: "\00d7";
/* This will render the 'X' */
}
#close:hover {
color: var(--colour3);
transition: ease 0.3s;
}
#content {
margin: 2rem;
}
.setting {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 0;
margin: 0.5rem 0;
}
.settings-text {
margin-right: 2rem;
display: flex;
}
/*** Toggle Switch styles ***/
.tgl-switch {
vertical-align: middle;
display: inline-block;
}
.tgl-switch .tgl {
display: none;
}
.tgl,
.tgl:after,
.tgl:before,
.tgl *,
.tgl *:after,
.tgl *:before,
.tgl+.tgl-slider {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.tgl::-moz-selection,
.tgl:after::-moz-selection,
.tgl:before::-moz-selection,
.tgl *::-moz-selection,
.tgl *:after::-moz-selection,
.tgl *:before::-moz-selection,
.tgl+.tgl-slider::-moz-selection {
background: none;
}
.tgl::selection,
.tgl:after::selection,
.tgl:before::selection,
.tgl *::selection,
.tgl *:after::selection,
.tgl *:before::selection,
.tgl+.tgl-slider::selection {
background: none;
}
.tgl+.tgl-slider {
outline: 0;
display: block;
width: 40px;
height: 18px;
position: relative;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.tgl+.tgl-slider:after,
.tgl+.tgl-slider:before {
position: relative;
display: block;
content: "";
width: 50%;
height: 100%;
}
.tgl+.tgl-slider:after {
left: 0;
}
.tgl+.tgl-slider:before {
display: none;
}
.tgl-flat+.tgl-slider {
padding: 2px;
-webkit-transition: all .2s ease;
transition: all .2s ease;
background: var(--colour6);
border: 3px solid var(--colour7);
border-radius: 2em;
}
.tgl-flat+.tgl-slider:after {
-webkit-transition: all .2s ease;
transition: all .2s ease;
background: var(--colour7);
content: "";
border-radius: 1em;
}
.tgl-flat:checked+.tgl-slider {
border: 3px solid var(--colour3);
}
.tgl-flat:checked+.tgl-slider:after {
left: 50%;
background: var(--colour3);
}
.subtitle-text {
margin: 0 0 0 1rem;
color: var(--colour5);
position: relative;
}
.form-group {
padding-top: 4px;
display: grid;
grid-template-columns: 50% 50%;
row-gap: 4px;
padding-right: 10px;
padding-left: 10px;
}
.form-group label {
color: var(--colour2);
vertical-align: middle;
font-weight: normal;
}
#stats {
margin-left: 1rem;
}
#LatencyStats {
margin-left: 1rem;
}
#hiddenInput {
position: absolute;
left: -10%;
/* Although invisible, push off-screen to prevent user interaction. */
width: 0px;
opacity: 0;
}
#editTextButton {
position: absolute;
height: 40px;
width: 40px;
}
.form-group label {
margin-right: 2rem;
min-width: 75%;
}
.warning {
box-sizing: border-box;
position: relative;
transform: scale(var(--ggs, 1));
width: 20px;
height: 20px;
border: 2px solid;
border-radius: 40px;
display: none;
}
.warning::after,
.warning::before {
content: "";
display: block;
box-sizing: border-box;
position: absolute;
border-radius: 3px;
width: 2px;
background: currentColor;
left: 7px
}
.warning::after {
top: 2px;
height: 8px
}
.warning::before {
height: 2px;
bottom: 2px
}
/* Flat buttons */
input[type="button"] {
background-color: transparent;
color: var(--colour2);
font-family: 'Montserrat';
border: 3px solid var(--colour3);
border-radius: 1rem;
font-size: 0.75rem;
padding-left: 0.5rem;
padding-right: 0.5rem;
}
input[type="button"]:hover {
background-color: var(--colour3);
transition: ease 0.3s;
}
input[type="button"]:active {
background-color: transparent;
}
#encoder-params-submit,
#webrtc-params-submit {
text-align: center;
}
select,
input[type="number"] {
background-color: var(--colour7);
color: var(--colour2);
border: 1px solid var(--colour6);
padding: 0.25rem;
font-family: 'Montserrat';
border-radius: 0.25rem;
}
input[type=number]::-webkit-inner-spin-button {
margin-left: 0.5rem;
}
input[type="number"]:disabled {
padding-right: 0.5rem;
-moz-appearance: textfield;
}
input[type=number]:disabled::-webkit-inner-spin-button {
display: none;
}
#settingsBtn,
#statsBtn {
cursor: pointer;
}
#streamingVideo {
pointer-events: all;
}
embed {
border: none;
width: 100%;
height: 100%;
}
g {
fill: var(--colour2);
}
object {
pointer-events: none;
}
#connectionStrength {
fill: var(--colour7);
}
#minimize {
display: none;
}
#afkOverlay {
z-index: 999;
background-color: rgba(30, 29, 34, 0.5);
display: inline-block;
height: 100vh;
width: 100vw;
line-height: 100vh;
text-align: center;
overflow: hidden;
}
#afkOverlay center {
display: inline-block;
line-height: 1.5;
height: 100vh;
}
@@ -0,0 +1,81 @@
import "./PlayerStyles.css";
import React, { useEffect, useState } from "react";
import { useHistory, useParams } from "react-router-dom";
import { connectSession } from "store/reducers/ActionCreator";
import { useAppDispatch, useAppSelector } from "hooks/redux";
import useWindowDimensions from "hooks/useWindowDimensions";
import useMobile from "hooks/useMobile";
import { Sidebar } from "components/pages/Stream/components/Sidebar/Sidebar";
import { Player } from "components/pages/Stream/components/Player/Player";
type link = {
id: string;
};
export const PlayerComponent: React.FC<any> = ({ cleanErrors, handleDisconnect, loadStream }) => {
const { isMobile } = useMobile();
const windowDimensions = useWindowDimensions();
const width = windowDimensions.width;
const height = windowDimensions.height;
const [popup, setPopup] = useState(false);
const { playerCount } = useAppSelector((state) => state.sessionReducer)
const { id } = useParams<link>();
const dispatch = useAppDispatch();
const history = useHistory()
useEffect(() => {
dispatch(connectSession(id)).unwrap().then(() => {
loadStream()
}).catch((res) => {
alert(res);
history.push('/')
})
return () => {
dispatch(cleanErrors());
handleDisconnect()
window.removeEventListener("change ", (event: any) => {
setPopup(false);
});
};
}, []);
useEffect(() => {
if (isMobile) {
if (width < height) {
setPopup(true);
} else {
setPopup(false);
}
}
}, [width, height, isMobile]);
return (
<>
{popup && (
<div className="popup-screen" style={{ height: height }}>
<h2>Переверните устройство</h2>
</div>
)}
<Player></Player>
<Sidebar
handleDisconnect={handleDisconnect}
players={playerCount}
heightDevice={height}
isMobile={isMobile}
></Sidebar>
</>
);
};
@@ -0,0 +1,26 @@
.playerOn {
user-select: none;
pointer-events: all;
}
.playerOff {
pointer-events: none;
}
.popup-screen {
position: absolute;
top: 0;
left: 0;
right: 0;
display: flex;
z-index: 99;
background: #1C1D21;
font-style: normal;
font-weight: 400;
font-size: 24px;
line-height: 125%;
align-items: center;
justify-content: center;
}
@@ -1,15 +1,11 @@
.toolbar-container {
user-select: none;
display: flex;
position: relative;
height: 100vh;
/* width: 74px; */
width: 64px;
padding: 0;
margin: 0;
/* transform: translateX(-60px); */
border-right: 1px solid #4f4f4f;
background: #333333;
padding: 10px;
background: #1c1d21;
box-sizing: border-box;
}
@@ -18,7 +14,6 @@
left: 50%;
transform: translate(-50%, -50%);
position: absolute;
}
.toolbar-confirm-block {
@@ -29,14 +24,13 @@
.mobile-users-part-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 24px;
align-items: flex-start;
padding-bottom: 32px;
}
.toolbar-field {
background: #333333;
overflow: hidden;
padding: 14px 15px;
width: 100%;
background: transparent;
box-sizing: border-box;
height: 100%;
display: flex;
@@ -46,11 +40,23 @@
}
.toolbar-field-part {
width: 100%;
display: flex;
flex-direction: column;
position: relative;
gap: 10px;
}
.toolbar-button-caption {
color: #c5c7ce;
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 140%;
white-space: nowrap;
position: relative;
}
.toolbar-button-container {
display: flex;
flex-direction: column;
@@ -75,7 +81,7 @@
width: 32px;
height: 128px;
border: none;
background: url("openToolbarBackIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/openToolbarBackIcon.svg") 50% 50% no-repeat;
background-size: 100% 100%;
/* background-color: #2F80ED; */
@@ -86,11 +92,11 @@
.toolbar-open-button-icon {
width: 24px;
height: 24px;
background: url("pointerIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/pointerIcon.svg") 50% 50% no-repeat;
}
.toolbar-container.opened .toolbar-open-button-icon {
background: url("closeToolbarIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/closeToolbarIcon.svg") 50% 50% no-repeat;
}
.toolbar-button-area-container {
@@ -98,12 +104,15 @@
}
.toolbar-button-area {
border-radius: 4px;
cursor: pointer;
align-items: center;
position: relative;
display: flex;
gap: 8px;
/* background-color: #333333; */
/* border-top-left-radius: 50px solid #4f4f4f; */
background-color: transparent;
padding: 0;
}
.toolbar-button-area.hidden {
@@ -142,51 +151,28 @@
}
.toolbar-button {
background-color: #4f4f4f;
background: transparent;
position: relative;
width: 40px;
height: 40px;
border: none;
border-radius: 50px;
background-size: 100% 100%;
cursor: pointer;
flex-shrink: 0;
display: flex;
align-items: center;
color: #ffffff;
font-weight: 400;
font-size: 14px;
justify-content: center;
line-height: 17px;
padding: 0px;
box-sizing: border-box;
padding: 8px;
}
.toolbar-button-active {
background: #567ece;
}
.user-icon {
position: relative;
width: 40px;
height: 40px;
border: none;
border-radius: 50px;
background-size: 100% 100%;
background-color: #4f4f4f;
cursor: pointer;
flex-shrink: 0;
display: flex;
align-items: center;
color: #ffffff;
font-weight: 400;
font-size: 14px;
justify-content: center;
line-height: 17px;
padding: 0px;
}
.toolbar-button_exit {
background: #eb5757;
background: transparent;
}
.language-caption {
@@ -196,8 +182,8 @@
}
.toolbar-icon {
object-fit: cover;
width: 45%;
width: 24px;
height: 100%;
}
.empty__block {
@@ -244,7 +230,7 @@
margin-left: 2px;
opacity: 0;
position: absolute;
left: 100%;
left: 126%;
height: 24px;
top: calc(50% - 12px);
/* background-color: #27AE60; */
@@ -256,15 +242,15 @@
box-sizing: content-box;
}
.toolbar-button-description-rectangle {
/* display: inline-block; */
white-space: nowrap;
/* flex-shrink: 0; */
background-color: #333333;
background: #23242a;
height: 24px;
border-top-right-radius: 50px;
border-bottom-right-radius: 50px;
border: 1px solid #4f4f4f;
border-left: none;
color: white;
display: flex;
@@ -281,7 +267,7 @@
width: 18px;
height: 26px;
background-color: #eb5757;
background: url("icons/newCaptTriangleIcon.svg");
background: url("../../../../../images/icons/newCaptTriangleIcon.svg");
}
.user-control-popup {
@@ -302,18 +288,16 @@
}
.toolbar-button.fullscreen.on {
background: url("./icons/openFullscreenIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/openFullscreenIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.fullscreen {
background: url("./icons/closeFullscreenIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/fullscreen.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.user {
background: url("icons/userIcon.svg") 50% 50% no-repeat;
}
.toolbar-button.admin {
background-color: #2f80ed;
@@ -328,57 +312,57 @@
}
.toolbar-button.users {
background: url("./icons/usersIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/usersIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.micro {
background: url("./icons/microOffIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/microOffIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.micro.on {
background: url("./icons/microOnIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/microOnIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.control {
background: url("./icons/constrolOffIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/constrolOffIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.control.on {
background: url("./icons/constrolOnIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/constrolOnIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.other {
background: url("./icons/othertIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/othertIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.sound {
background: url("./icons/soundOffIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/soundOffIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.sound.on {
background: url("./icons/soundOnIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/soundOnIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.exit {
background: url("./icons/exitIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/exitIcon.svg") 50% 50% no-repeat;
background-color: #eb5757;
}
.toolbar-button.copy {
background: url("./icons/copyIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/copyIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.share {
background: url("./icons/shareIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/shareIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
@@ -387,12 +371,12 @@
}
.toolbar-button.language.ru {
background: url("./icons/ruIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/ruIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
.toolbar-button.language.en {
background: url("./icons/enIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/enIcon.svg") 50% 50% no-repeat;
background-color: #4f4f4f;
}
@@ -447,14 +431,14 @@
.toolbar-button.large.control .toolbar-button-large-view-icon {
width: 24px;
height: 24px;
background: url("icons/constrolOffIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/constrolOffIcon.svg") 50% 50% no-repeat;
background-size: 100% 100%;
}
.toolbar-button.large.control.on .toolbar-button-large-view-icon {
width: 24px;
height: 24px;
background: url("icons/constrolOnIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/constrolOnIcon.svg") 50% 50% no-repeat;
background-size: 100% 100%;
}
@@ -487,7 +471,7 @@
.toolbar-button.yes .toolbar-button-medium-view-icon {
width: 24px;
height: 24px;
background: url("icons/constrolOnIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/constrolOnIcon.svg") 50% 50% no-repeat;
background-size: 100% 100%;
}
@@ -503,7 +487,7 @@
.toolbar-button.no .toolbar-button-medium-view-icon {
width: 24px;
height: 24px;
background: url("icons/constrolOffIcon.svg") 50% 50% no-repeat;
background: url("../../../../../images/icons/constrolOffIcon.svg") 50% 50% no-repeat;
background-size: 100% 100%;
}
@@ -593,14 +577,8 @@
}
@media screen and (max-height: 700px) {
.toolbar-field {
padding: 20px 14px;
}
.toolbar-field {}
.toolbar-button {
width: 44px;
height: 44px;
}
.toolbar-button:hover {
opacity: 1;
@@ -666,4 +644,4 @@
opacity: 0;
pointer-events: none;
}
}
}
@@ -0,0 +1,70 @@
import "./Sidebar.css";
import React, { useState } from "react";
import { SidebarDesktop } from "../SidebarDesktop/SidebarDesktop";
import { SidebarMobile } from "../SidebarMobile/SidebarMobile";
import { useAppDispatch, useAppSelector } from "hooks/redux";
import { languageSlice } from "store/reducers/languageSlice";
export const Sidebar: React.FC<any> = ({ exitPopup, isMobile, heightDevice, players, handleDisconnect }) => {
const [isMuted, setMuted] = useState(true);
const [isControl, setControl] = useState(false);
const { playerCount } = useAppSelector((state) => state.sessionReducer);
const { currentLang } = useAppSelector((state) => state.languageReducer)
const { handleChangeLanguage } = languageSlice.actions;
const dispatch = useAppDispatch()
const handleCloseStream = () => {
handleDisconnect()
}
const handleLanguage = (value: string) => {
dispatch(handleChangeLanguage(value))
}
const handleMuteClick = () => {
setMuted((prev) => !prev);
};
const handleControlClick = () => {
setControl((prev) => !prev);
};
return (
<>
{isMobile ? (
<SidebarMobile
handleLanguage={handleLanguage}
currentLang={currentLang}
userArr={playerCount}
isMobile={isMobile}
height={heightDevice}
isMuted={isMuted}
isControl={isControl}
handleMuteClick={handleMuteClick}
handleControlClick={handleControlClick}
closeStream={handleCloseStream}
handleOpenExitPopup={handleCloseStream}
></SidebarMobile>
) : (
<SidebarDesktop
userArr={playerCount}
height={heightDevice}
isMobile={isMobile}
isMuted={isMuted}
isControl={isControl}
handleMuteClick={handleMuteClick}
handleControlClick={handleControlClick}
closeStream={handleCloseStream}
exitPopup={exitPopup}
></SidebarDesktop>
)}
</>
);
};
@@ -0,0 +1,176 @@
import React, { useState, useEffect } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { UserList } from "components/pages/Stream/components/UserList/UserList";
import { FullscreenButton } from "components/pages/Stream/components/buttons/FullscreenButton/FullscreenButton";
import { PopupShare } from "components/pages/Stream/components/popups/PopupShare/PopupShare";
import { PopupExit } from "components/pages/Stream/components/popups/PopupExit/PopupExit";
import { ControlPanel } from "../ControlPanel/ControlPanel";
import { WideSidebarButton } from "components/pages/Stream/components/buttons/WideSidebarButton/WideSidebarButton";
import {
sidebarVariants,
popupAnimation,
wideSidebarVariants,
wideSidebarAdminVariants,
} from "utils/animationProps";
export const SidebarDesktop: React.FC<any> = ({
closeStream,
exitPopup,
isMuted,
isControl,
height,
handleMuteClick,
handleControlClick,
isMobile,
userArr
}) => {
const [open, setOpen] = useState(false);
const [popup, setPopup] = useState({
popup1: false,
popup2: false,
});
useEffect(() => {
console.log("test");
}, [exitPopup]);
const isAdmin = false;
const [wideSidebar, setWideSidebar] = useState(false);
function handleClosePopups() {
setPopup({
popup1: false,
popup2: false,
});
}
function handleOpenSharePopup() {
setPopup({
popup1: true,
popup2: false,
});
}
function handleOpenExitPopup() {
setPopup({
popup1: false,
popup2: true,
});
}
function closeSideBar() {
setOpen(false);
setWideSidebar(false);
}
useEffect(() => () => unmountComponent(), []);
function unmountComponent() {
setOpen(false);
setPopup({
popup1: false,
popup2: false,
});
}
const setAnimation = () => {
if (isAdmin) return wideSidebarAdminVariants;
else {
return wideSidebarVariants;
}
};
return (
<>
<motion.div
onHoverStart={() => setWideSidebar(true)}
onHoverEnd={() => setWideSidebar(false)}
initial={false}
animate={open ? "open" : "closed"}
variants={wideSidebar ? setAnimation() : sidebarVariants}
className="toolbar-container"
style={{ height: height }}
>
<div
style={wideSidebar ? { overflow: "hidden" } : { overflow: "visible" }}
className="toolbar-field"
>
<div className="toolbar-field-part">
<FullscreenButton isSidebarWide={wideSidebar}> </FullscreenButton>
<UserList
userArr={userArr}
isMobile={isMobile}
closeSidebar={closeSideBar}
isSidebarWide={wideSidebar} /// this is for disable showhing button's caption
isAdmin={isAdmin}
></UserList>
</div>
<motion.div
className="toolbar-field-part"
>
<WideSidebarButton
isSidebarWide={wideSidebar}
close={closeSideBar}
></WideSidebarButton>
</motion.div>
<ControlPanel
isControl={isControl}
handleControlClick={handleControlClick}
isSidebarWide={wideSidebar}
isMuted={isMuted}
handleMuteClick={handleMuteClick}
handleOpenSharePopup={handleOpenSharePopup}
handleOpenExitPopup={handleOpenExitPopup}
></ControlPanel>
</div>
<AnimatePresence>
{!open && (
<motion.div
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
onClick={() => setOpen(true)}
className="toolbar-open-button"
>
<span className="toolbar-open-button-icon"></span>
</motion.div>
)}
</AnimatePresence>
</motion.div>
<AnimatePresence>
{popup.popup1 && (
<motion.div
key={1}
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<PopupShare setClose={handleClosePopups}></PopupShare>
</motion.div>
)}
{popup.popup2 && (
<motion.div
key={2}
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<PopupExit
closeStream={closeStream}
setClose={handleClosePopups}
></PopupExit>
</motion.div>
)}
</AnimatePresence>
</>
);
};
@@ -0,0 +1,105 @@
import { AnimatePresence, motion } from "framer-motion";
import {
sidebarVariants,
popupAnimation,
} from "utils/animationProps";
import { useEffect, useState } from "react";
import { FullscreenButton } from "components/pages/Stream/components/buttons/FullscreenButton/FullscreenButton";
import { WideSidebarButton } from "components/pages/Stream/components/buttons/WideSidebarButton/WideSidebarButton";
import { UserButtonMobile } from "components/pages/Stream/components/buttons/UserButtonMobile/UserButtonMobile";
import { ControlButton } from "components/pages/Stream/components/buttons/ControlButton/ControlButton";
import { MicroButton } from "components/pages/Stream/components/buttons/MicroButton/MicroButton";
import { AdditionalButton } from "components/pages/Stream/components/buttons/AdditionalButton/AdditionalButton";
import { UserListMobilePopup } from "components/pages/Stream/components/popups/UserListMobliePopup/UserListMobilePopup";
import { ExitButton } from "components/pages/Stream/components/buttons/ExitButton/ExitButton";
import { PopupAdditional } from 'components/pages/Stream/components/popups/PopupAdditional/PopupAdditional'
export const SidebarMobile: React.FC<any> = ({ height, isMobile, handleLanguage, userArr, currentLang, closeStream }) => {
const [open, setOpen] = useState(false);
const [userListPopup, setUserListPopup] = useState(false);
const [popupAdditional, setPopupAdditional] = useState(false); //should be insted of exit button, but popup doesn't ready yet
const handleOpenPopupAdditional = () => {
if (userListPopup) {
setUserListPopup(false)
}
setPopupAdditional(true)
}
const handleOpenPopupUserList = () => {
if (popupAdditional) {
setPopupAdditional(false)
}
setUserListPopup(true)
}
const handleClosePopups = () => {
setPopupAdditional(false)
setUserListPopup(false)
}
useEffect(() => () => handleClosePopups(), []);
return (
<> <motion.div
initial={false}
animate={open ? "open" : "closed"}
variants={sidebarVariants}
style={{ height: height }}
className="toolbar-container"
>
<div className="toolbar-field">
<div className="toolbar-field-part">
<FullscreenButton isSidebarWide={false}> </FullscreenButton>
<WideSidebarButton close={() => setOpen(false)}></WideSidebarButton>
<div className="toolbar-button-container-border-line"></div>
<UserButtonMobile
active={userListPopup}
onClick={handleOpenPopupUserList}
></UserButtonMobile>
</div>
<div className="toolbar-field-part">
<div className="toolbar-button-container-border-line"></div>
<AdditionalButton active={popupAdditional} isSidebarWide={false} onClick={handleOpenPopupAdditional}></AdditionalButton>
</div>
<AnimatePresence>
{!open && (
<motion.div
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
onClick={() => setOpen(true)}
className="toolbar-open-button"
>
<span className="toolbar-open-button-icon"></span>
</motion.div>
)}
</AnimatePresence>
</div>
</motion.div>
<AnimatePresence mode="wait">
{userListPopup && (<motion.div key={1}
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<UserListMobilePopup height={height} userArr={userArr} closePopup={() => setUserListPopup(false)} isMobile={isMobile}></UserListMobilePopup>
</motion.div>)}
{popupAdditional && (<motion.div key={2} variants={popupAnimation} initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
<PopupAdditional height={height} handleLanguage={handleLanguage} closeStream={closeStream} active={popupAdditional} currentLang={currentLang} closePopup={() => setPopupAdditional(false)}></PopupAdditional>
</motion.div>)}
</AnimatePresence>
</>
);
};
@@ -0,0 +1,98 @@
.user-container {
user-select: none;
cursor: pointer;
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.user-container-mobile {
box-sizing: border-box;
padding: 8px;
background: #1C1D21;
border-radius: 4px;
}
.user {
display: flex;
align-items: center;
}
.user-name {
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 140%;
color: #c5c7ce;
white-space: nowrap;
}
.icon-container {
align-items: center;
display: flex;
gap: 3px;
flex-direction: column;
}
.control-elements {
gap: 8px;
display: flex;
}
.you-caption {
font-weight: 400;
font-size: 12px;
line-height: 130%;
/* identical to box height, or 16px */
/* Button_3 */
color: #73788c;
}
.user-wrapper {
position: relative;
}
.user-wrapper:nth-child(1) {
margin-top: 10px;
}
.user-button-container {
display: flex;
gap: 8px;
padding: 8px;
}
.user-button-container-mobile {
background: #1C1D21;
padding: 0px 8px 8px 8px;
}
.icon-container {
cursor: pointer;
padding: 8px;
}
.control-circle {
width: 8px;
height: 8px;
background-color: #798fff;
border-radius: 50%;
margin-bottom: 5px;
}
.user-control-caption {
display: flex;
gap: 4px;
font-weight: 400;
font-size: 10px;
line-height: 130%;
align-items: center;
color: #798fff;
}
@@ -0,0 +1,163 @@
import "./User.css";
import { AnimatePresence, motion } from "framer-motion";
import { useState, useEffect } from "react";
import userPic from "images/icons/Person.svg";
import chevronDown from "images/icons/ChevronDown.svg";
import control from "images/icons/control.svg";
import controlOff from "images/icons/HandOff.svg";
import microOn from "images/icons/MicroOn.svg";
import microOff from "images/icons/MicroOff.svg";
import {
popupAnimation,
userMenuAnimation,
animationButton,
transition,
iconAnimation,
} from "utils/animationProps";
import { useTranslation } from "react-i18next";
export const User: React.FC<any> = ({
isAdmin,
isSidebarWide,
closeSidebar,
isMobile
}) => {
const [hover, setHover] = useState(false);
const [expand, setExpand] = useState(false);
const [mute, setMute] = useState(true);
const [isControl, setControl] = useState(true);
const {t} = useTranslation()
useEffect(() => {
setExpand(false);
}, [closeSidebar]);
const expandMenu = () => {
if (isSidebarWide) {
if (isAdmin) setExpand((prev) => !prev);
}
};
return (
<>
<motion.div
variants={userMenuAnimation}
animate={expand ? "open" : "closed"}
className="user-wrapper"
>
<div onClick={expandMenu} className={isMobile ? "user-container-mobile user-container" : "user-container"}>
<div className="user">
<motion.div
onHoverStart={() => setHover(true)}
onHoverEnd={() => setHover(false)}
className="icon-container"
>
<img
alt="иконка пользователя"
className="user-icon"
src={userPic}
></img>
</motion.div>
<motion.span
initial={false}
variants={animationButton}
animate={isSidebarWide ? "show" : "hidden"}
className="user-name"
>
{t('user')}
</motion.span>
</div>
{isSidebarWide && (
<div className="control-elements">
<AnimatePresence>
{!isControl && (
<motion.div
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
className="user-control-caption"
>
<div className="control-circle"></div>
<span className="user-control-caption">Управление</span>
</motion.div>
)}
</AnimatePresence>
{isAdmin && (
<motion.div
initial={false}
variants={animationButton}
animate={isSidebarWide ? "show" : "hidden"}
className="icon-container"
>
<motion.img
initial={false}
transition={transition}
variants={iconAnimation}
animate={expand ? "open" : "closed"}
src={chevronDown}
className="arrow-caption"
></motion.img>
</motion.div>
)}
</div>
)}
</div>
{expand && (
<AnimatePresence>
<motion.div
variants={popupAnimation}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
className={isMobile ? "user-button-container user-button-container-mobile" : "user-button-container"}
>
<div
onClick={() => setControl((prev) => !prev)}
style={{ height: "40px" }}
className="button button-secondary"
>
<img src={isControl ? control : controlOff}></img>
<span>
{isControl ? "Передать управление" : "Вернуть управление"}
</span>
</div>
<div
style={{ width: "40px", height: "40px", padding: "16px" }}
onClick={() => setMute((prev) => !prev)}
className="button button-teritary"
>
<img
alt="иконка звук выкл"
src={mute ? microOn : microOff}
className="mic"
></img>
</div>
</motion.div>
</AnimatePresence>
)}
</motion.div>
<AnimatePresence initial={false}>
{!isSidebarWide && hover && (
<motion.div
variants={animationButton}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
className="toolbar-button-description-container toolbar-button-description-user"
>
<span className="toolbar-button-description-triangle"></span>
<span className="toolbar-button-description-rectangle">
Пользователь
</span>
</motion.div>
)}
</AnimatePresence>
</>
);
};
@@ -0,0 +1,12 @@
import { User } from "components/pages/Stream/components/User/User";
export const UserList: React.FC<any> = ({ isSidebarWide, isAdmin, closeSidebar, isMobile, userArr }) => {
console.log(userArr)
return (
<div className="toolbar-field-part">
{userArr.map((i: any) => (<User closeSidebar={closeSidebar} isAdmin={isAdmin} isSidebarWide={isSidebarWide}></User>
))}
<div className="toolbar-button-container-border-line"></div>
</div>
);
};
@@ -0,0 +1,21 @@
import { Button } from "components/shared/Button/Button";
import more from "images/icons/More.svg";
export const AdditionalButton: React.FC<any> = ({ active, onClick }) => {
const button = {
icon: more,
inactive: "",
active: "",
type: "",
};
return (
<div
style={active ? { background: "#2E3038" } : { background: "transparent" }}
className="toolbar-button-area"
onClick={() => onClick()}
>
<Button button={button}></Button>
</div>
);
};
@@ -0,0 +1,40 @@
import control from "images/icons/control.svg";
import controlOff from "images/icons/HandOff.svg";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "components/shared/Button/Button";
export const ControlButton: React.FC<any> = ({
onClick,
isSidebarWide,
isActive,
}) => {
const { t } = useTranslation();
const [button, setButton] = useState({
icon: control,
active: "request-control-btn",
inactive: "request-control-btn-disable",
type: "control",
});
useEffect(() => {
setButton({ ...button, icon: isActive ? control : controlOff });
}, [isActive]);
function handleClick() {
onClick();
}
return (
<div style={{pointerEvents: "none"}} onClick={handleClick} className="toolbar-button-area">
<Button
isSidebarWide={isSidebarWide}
button={button}
active={isActive}
></Button>
</div>
);
};
@@ -1,7 +1,8 @@
import exit from "images/icons/exit.svg";
import { useState } from "react";
import { Button } from "../button/button";
import exit from "../../../images/icons/exit.svg";
import { THOC } from "../../../utils/types";
import { Button } from "components/shared/Button/Button";
export const ExitButton: React.FC<any> = ({ onClick, isSidebarWide }) => {
const [active, setActive] = useState(false);
@@ -13,14 +14,17 @@ export const ExitButton: React.FC<any> = ({ onClick, isSidebarWide }) => {
};
function handleClick() {
console.log("click");
setActive((prev) => !prev);
onClick()
onClick();
}
return (
<div className="toolbar-button-area">
<Button isSidebarWide={isSidebarWide} button={button} onClick={handleClick}></Button>
</div>
<button
tabIndex={-1}
onClick={handleClick}
className="toolbar-button-area"
>
<Button isSidebarWide={isSidebarWide} button={button}></Button>
</button>
);
};
};
@@ -0,0 +1,58 @@
import fullscreen from "images/icons/fullscreen.svg";
import fullscreenOff from "images/icons/fullscreenOff.svg";
import { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "components/shared/Button/Button";
export const FullscreenButton: React.FC<any> = ({ isSidebarWide }) => {
const [active, setActive] = useState(Boolean(document.fullscreenElement));
const [button, setButton] = useState({
icon: fullscreen,
inactive: "fullscreen-control-btn",
active: "fullscreen-control-btn-active",
type: "fullscreen",
});
const { t } = useTranslation();
const handleClick = () => {
setActive(Boolean(document.fullscreenElement));
if (!document.fullscreenElement) {
document.body.requestFullscreen();
} else {
document.exitFullscreen();
}
};
useEffect(() => {
setButton({
icon: !active ? fullscreen : fullscreenOff,
inactive: "fullscreen-control-btn",
active: "fullscreen-control-btn-active",
type: "fullscreen",
});
}, [active]);
useEffect(() => {
const onFullscreenChange = () => {
setActive(Boolean(document.fullscreenElement));
};
document.addEventListener("fullscreenchange", onFullscreenChange);
return () =>
document.removeEventListener("fullscreenchange", onFullscreenChange);
}, []);
return (
<div onFocus={(e) => e.target.blur()}
tabIndex={-1} onClick={handleClick} className="toolbar-button-area">
<Button
isSidebarWide={isSidebarWide}
button={button}
active={active}
></Button>
</div>
);
};
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 19C2.44772 19 2 18.5523 2 18V7C2 6.44772 2.44772 6 3 6H8.82201C9.28827 6 9.66624 6.37798 9.66624 6.84424C9.66624 7.3105 9.28827 7.68848 8.82201 7.68848H4.34491C4.06877 7.68848 3.84491 7.91233 3.84491 8.18848V11.1494C3.84491 11.4256 4.06877 11.6494 4.34491 11.6494H8.42515C8.88966 11.6494 9.26621 12.026 9.26621 12.4905C9.26621 12.955 8.88966 13.3315 8.42515 13.3315H4.34491C4.06877 13.3315 3.84491 13.5554 3.84491 13.8315V16.8115C3.84491 17.0877 4.06877 17.3115 4.34491 17.3115H8.89365C9.35991 17.3115 9.73789 17.6895 9.73789 18.1558C9.73789 18.622 9.35991 19 8.89365 19H3Z" fill="#C5C7CE"/>
<path d="M21.0865 6C21.591 6 22 6.40899 22 6.9135V18C22 18.5523 21.5523 19 21 19H20.8462C20.5082 19 20.193 18.8293 20.0085 18.5461L14.1107 9.49922C14.0972 9.47838 14.074 9.46582 14.0491 9.46582C14.0085 9.46582 13.9755 9.49876 13.9755 9.5394V18.0775C13.9755 18.587 13.5625 19 13.0531 19C12.5436 19 12.1306 18.587 12.1306 18.0775V7C12.1306 6.44772 12.5783 6 13.1306 6H13.2963C13.6343 6 13.9495 6.1708 14.1341 6.45403L20.0378 15.5135C20.0514 15.5343 20.0746 15.5469 20.0994 15.5469C20.1401 15.5469 20.173 15.5139 20.173 15.4733V6.9135C20.173 6.40899 20.582 6 21.0865 6Z" fill="#C5C7CE"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,77 @@
import en from "./EN.svg";
import ru from "./RU.svg";
import { useEffect, useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { useAppDispatch, useAppSelector } from "hooks/redux";
import { languageSlice } from "store/reducers/languageSlice";
import { Button } from "components/shared/Button/Button";
const container = {
hidden: {
opacity: 0,
transition: { duration: 0.2, ease: "easeOut" },
},
show: {
opacity: 1,
transition: { delay: 0.15, duration: 0.2, ease: "easeIn" },
},
exit: {
opacity: 0,
transition: { duration: 0.2, ease: "easeOut" },
},
};
export const LanguageButton: React.FC<any> = ({ hover, setHover, isSidebarWide }) => {
const { handleChangeLanguage } = languageSlice.actions;
const { currentLang } = useAppSelector((state) => state.languageReducer);
const dispatch = useAppDispatch()
const [open, setOpen] = useState(false);
const [button, setButton] = useState({
icon: currentLang === 'en' ? en : ru,
active: "language-control-btn",
inactive: "language-control-btn",
type: "language",
});
useEffect(() => {
setButton({
icon: currentLang === 'en' ? en : ru,
active: "language-control-btn",
inactive: "language-control-btn",
type: "language",
})
}, [currentLang])
const handleClick = () => {
if (currentLang === 'en') {
dispatch(handleChangeLanguage('ru'))
} else {
dispatch(handleChangeLanguage('en'))
}
}
return (
<div onClick={handleClick} className="toolbar-button-area">
<Button isSidebarWide={isSidebarWide} button={button}></Button>
<AnimatePresence>
{open && (
<motion.div
variants={container}
initial={"hidden"}
animate={"show"}
exit={"hidden"}
>
</motion.div>
)}
</AnimatePresence>
</div>
);
};
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.89773 18.7877C2.40193 18.7877 2 18.3858 2 17.89V7C2 6.44771 2.44772 6 3 6H6.24172C7.16366 6 7.92872 6.17067 8.5369 6.51201C9.14894 6.85335 9.60604 7.32581 9.90819 7.9294C10.2103 8.52882 10.3614 9.2219 10.3614 10.0086C10.3614 10.7912 10.2084 11.4801 9.90238 12.0754C9.60023 12.6665 9.14313 13.1265 8.53109 13.4553C7.92291 13.7842 7.15785 13.9486 6.23591 13.9486H3.85311C3.39447 13.9486 3.02266 13.5768 3.02266 13.1182V13.1182C3.02266 12.6595 3.39447 12.2877 3.85311 12.2877H6.07321C6.65427 12.2877 7.12686 12.1982 7.49099 12.0192C7.859 11.8402 8.12822 11.5801 8.29866 11.2387C8.46911 10.8974 8.55433 10.4874 8.55433 10.0086C8.55433 9.52578 8.46717 9.10743 8.29285 8.7536C8.12241 8.39978 7.85319 8.1292 7.48518 7.94188C7.12105 7.7504 6.64265 7.65466 6.04997 7.65466H4.29547C4.01933 7.65466 3.79547 7.87852 3.79547 8.15466V17.89C3.79547 18.3858 3.39354 18.7877 2.89773 18.7877V18.7877ZM7.32848 13.0183C7.66354 13.0183 7.97028 13.2062 8.12241 13.5047L10.1411 17.4661C10.4493 18.0708 10.01 18.7877 9.3313 18.7877V18.7877C8.9869 18.7877 8.67209 18.5931 8.51818 18.285L6.53132 14.3075C6.23536 13.7151 6.66621 13.0183 7.32848 13.0183V13.0183Z" fill="#C5C7CE"/>
<path d="M20.1987 6.90064C20.1987 6.40323 20.602 6 21.0994 6V6C21.5968 6 22 6.40323 22 6.90064V14.4107C22 15.3056 21.8044 16.0986 21.4131 16.7896C21.0219 17.4765 20.4718 18.0176 19.7629 18.4131C19.054 18.8044 18.2231 19 17.2702 19C16.3211 19 15.4922 18.8044 14.7833 18.4131C14.0744 18.0176 13.5243 17.4765 13.1331 16.7896C12.7418 16.0986 12.5462 15.3056 12.5462 14.4107V6.89773C12.5462 6.40193 12.9481 6 13.4439 6V6C13.9397 6 14.3417 6.40193 14.3417 6.89773V14.2546C14.3417 14.8332 14.4598 15.3473 14.6961 15.7968C14.9363 16.2464 15.2752 16.6002 15.713 16.8583C16.1507 17.1122 16.6698 17.2392 17.2702 17.2392C17.8745 17.2392 18.3955 17.1122 18.8332 16.8583C19.2748 16.6002 19.6119 16.2464 19.8443 15.7968C20.0806 15.3473 20.1987 14.8332 20.1987 14.2546V6.90064Z" fill="#C5C7CE"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,39 @@
import microOn from "images/icons/MicroOn.svg";
import microOff from "images/icons/MicroOff.svg";
import { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "components/shared/Button/Button";
export const MicroButton: React.FC<any> = ({
onClick,
isActive,
isSidebarWide,
}) => {
const { t } = useTranslation();
const [button, setButton] = useState({
icon: microOn,
active: "mute-control-btn",
inactive: "mute-control-btn-disable",
type: "microphone",
});
useEffect(() => {
setButton({ ...button, icon: isActive ? microOn : microOff });
}, [isActive]);
function handleClick() {
onClick();
}
return (
<div style={{pointerEvents: "none"}} tabIndex={-1} onClick={handleClick} className="toolbar-button-area">
<Button
isSidebarWide={isSidebarWide}
button={button}
active={isActive}
></Button>
</div>
);
};
@@ -0,0 +1,29 @@
import share from "images/icons/Share.svg";
import { useTranslation } from "react-i18next";
import { Button } from "components/shared/Button/Button";
export const ShareButton: React.FC<any> = ({ onClick, isSidebarWide }) => {
const button = {
icon: share,
active: "share-contro-btn",
inactive: "share-contro-btn",
type: "share",
};
const { t } = useTranslation();
function handleClick() {
onClick();
}
return (
<button
onClick={handleClick}
className="toolbar-button-area"
>
<Button isSidebarWide={isSidebarWide} button={button}></Button>
</button>
);
};
@@ -0,0 +1,20 @@
import persons from "images/icons/Persons.svg";
import { Button } from "components/shared/Button/Button";
export const UserButtonMobile: React.FC<any> = ({ active, onClick }) => {
const button = {
icon: persons,
inactive: "",
active: "",
type: "",
};
return (
<div
style={active ? { background: "#2E3038" } : { background: "transparent" }}
className="toolbar-button-area"
onClick={() => onClick()}
>
<Button button={button}></Button>
</div>
);
};
@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 17L11 12L16 7" stroke="#C5C7CE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 22L7 12L7 2" stroke="#C5C7CE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 322 B

@@ -0,0 +1,38 @@
import wideButton from "./Menu.svg";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "components/shared/Button/Button";
export const WideSidebarButton: React.FC<any> = ({ close, isSidebarWide }) => {
const { t } = useTranslation();
const [active, setActive] = useState(false);
const [button, setButton] = useState({
icon: wideButton,
inactive: "sidebar-hide",
active: "sidebar-hide",
type: "fullscreen",
noHover: true,
});
const handleClick = () => {
close();
setActive((prev) => !prev);
};
return (
<button
tabIndex={-1}
onFocus={(e) => e.target.blur()}
onClick={handleClick}
className="toolbar-button-area"
>
<Button
isSidebarWide={isSidebarWide}
button={button}
active={active}
></Button>
</button>
);
};
@@ -0,0 +1,65 @@
import '../popup.css'
import { ControlPanelMobile } from "components/pages/Stream/components/ControlPanelMobile/ControlPanelMobile"
import { PopupInviteMobile } from "../PopupInviteMobile/PopupInviteMobile"
import { PopupExitMobile } from '../PopupExitMobile/PopupExitMobile'
import { PopupLanguageMobile } from '../PopupLanguageMobile/PopupLanguageMobile'
import { useEffect, useState } from 'react'
export const PopupAdditional: React.FC<any> = ({ closePopup, currentLang, active, closeStream, handleLanguage, height }) => {
const [popup, setPopup] = useState(true)
const [popupShare, setPopupShare] = useState(false)
const [popupLanguage, setPopupLanguage] = useState(false)
const [popupExit, setPopupExit] = useState(false)
const handleCloseComponent = () => {
setPopup(true)
setPopupExit(false)
setPopupShare(false)
setPopupLanguage(false)
}
useEffect(() => {
if (active) {
handleCloseComponent()
}
}, [active])
useEffect(() => () => handleCloseComponent(), []);
const handleMainPopupOff = () => {
setPopup(false)
}
const handlePopupShare = () => {
handleMainPopupOff()
setPopupShare(true)
}
const handlePopupExit = () => {
handleMainPopupOff()
setPopupExit(true)
}
const handleLanguagePopup = () => {
handleMainPopupOff()
setPopupLanguage(true)
}
console.log(height)
return (<div style={{height: height}} className="popup-mobile">
<div className="popup-mobile-container">
{popup && (<ControlPanelMobile closePopup={closePopup} currentLang={currentLang} handleReturn={() => setPopup(true)} setPopupExit={handlePopupExit} setPopupShare={handlePopupShare} setPopupInvite={handlePopupShare} setLanguagePopup={handleLanguagePopup}></ControlPanelMobile>
)}
{popupShare && (<PopupInviteMobile handleReturn={() => setPopup(true)} setPopup={() => setPopupShare(false)}></PopupInviteMobile>
)}
{popupLanguage && (<PopupLanguageMobile handleLanguage={handleLanguage} currentLang={currentLang} handleReturn={() => setPopup(true)} setPopup={() => setPopupLanguage(false)}></PopupLanguageMobile>
)}
{popupExit && (<PopupExitMobile closeStream={closeStream} handleReturn={() => setPopup(true)} setPopup={() => setPopupExit(false)}></PopupExitMobile>
)}
</div>
</div>)
}
@@ -0,0 +1,50 @@
.exit-popup-container {
position: absolute;
display: flex;
flex-direction: column;
box-sizing: border-box;
background-color: #333333;
color: #ffffff;
padding: 40px;
top: 50%;
width: 400px;
left: 50%;
background: #151619;
transform: translate(-50%, -50%);
/* gap: 24px; */
justify-content: space-between;
}
.exit-popup-button-container {
display: flex;
flex-direction: column;
gap: 24px;
}
.exit-popup-button {
border-radius: 12px;
border: none;
color: white;
height: 60px;
font-style: normal;
font-weight: 600;
font-size: 16px;
line-height: 20px;
cursor: pointer;
}
.exit-popup-button_confirm {
background: #567ece;
opacity: 1;
}
.exit-popup-button_finish {
background: #404040;
}
.exit-popup-button_confirm:hover {
opacity: 0.7;
transition: 0.3s;
}
@@ -0,0 +1,31 @@
import "../PopupShare/sharePopup.css";
import "./PopupExit.css";
import { useTranslation } from "react-i18next";
export const PopupExit: React.FC<any> = ({ setClose, closeStream }) => {
const { t } = useTranslation();
return (
<div className="popup exit-popup-container popup">
<div className="popup-container-position">
<h2 className="mobile-users-part-header-title">
{t("popup-control-exit-title")}
</h2>
<div className="exit-popup-button-container">
<button onClick={() => setClose()} className="button button-primary">
{t("popup-control-no")}
</button>
<button
onClick={closeStream}
style={{ height: "48px" }}
className=" button button-secondary"
>
{t("popup-control-yes")}
</button>
</div>
</div>
</div>
);
};
@@ -0,0 +1,27 @@
import { useTranslation } from "react-i18next"
export const PopupExitMobile: React.FC<any> = ({ handleReturn, setPopup, closeStream }) => {
const onReturnClick = () => {
setPopup()
handleReturn()
}
const { t } = useTranslation()
return (<div className="popup-exit-container">
<h2 style={{ marginBottom: "24px" }} className="mobile-users-part-header-title">
{t("popup-control-exit-title")}
</h2>
<div className="popup-exit-button-container-mobile">
<button onClick={onReturnClick} className="button button-primary">
{t("popup-control-no")}
</button>
<button
onClick={closeStream}
style={{ height: "48px" }}
className=" button button-secondary"
>
{t("popup-control-yes")}
</button>
</div>
</div>)
}
@@ -0,0 +1,50 @@
import back from 'images/icons/Back.svg'
import { useTranslation } from 'react-i18next'
import { useState } from 'react'
export const PopupInviteMobile: React.FC<any> = ({ setPopup, handleReturn }) => {
const { t } = useTranslation()
const [copy, setCopy] = useState(false);
function copyLink() {
navigator.clipboard.writeText(window.location.href);
setCopy((prev) => !prev);
}
const onReturnClick = () => {
setPopup()
handleReturn()
}
return (
<>
<div style={{ marginBottom: "28px" }} className='popup-header-mobile'>
<img onClick={onReturnClick} src={back}></img>
<h2>{t('popup-control-invite-title')}</h2>
</div>
<div className='popup-invite-container'>
<p className='popup-invite-title'>{t('popup-control-link')}</p>
<input
className="share-popup-data-input href"
value={window.location.href}
readOnly
></input>
{!copy ? (
<button style={{ width: '171px' }} onClick={copyLink} className="button button-primary ">
<span className="share-popup-copy-button-icon"></span>
<span className="">
{copy ? t("popup-control-btn-active") : t("popup-control-btn")}
</span>
</button>
) : (
<button style={{ width: '171px' }} onClick={copyLink} className="button button-teritary">
<span className="share-popup-copied-button-icon"></span>
<span className="">{t("popup-control-btn-active")}</span>
</button>
)}
</div>
</>
)
}
@@ -0,0 +1,44 @@
import back from 'images/icons/Back.svg'
import ok from 'images/icons/OK.svg'
import { useTranslation } from 'react-i18next'
export const PopupLanguageMobile: React.FC<any> = ({ currentLang, setPopup, handleReturn, handleLanguage }) => {
console.log(currentLang)
const buttonArray = [{
value: 'ru',
captionInt: "Русский",
caption: 'Русский',
}, {
value: 'en',
captionInt: "English",
caption: 'Английский',
}]
const { t } = useTranslation()
const onReturnClick = () => {
setPopup()
handleReturn()
}
return (
<>
<div style={{ marginBottom: "28px" }} className='popup-header-mobile'>
<img onClick={onReturnClick} src={back}></img>
<h2>{t('popup-change-language')}</h2>
</div>
<div className="popup-exit-button-container-mobile">
{buttonArray.map((i, index) => (
<button onClick={() => handleLanguage(i.value)} disabled={i.value === currentLang} key={index} className='popup-button-language'>
<div className='popup-button-container-language'>
<span>{i.caption}</span>
<span style={{ fontSize: "12px" }}>{i.captionInt}</span>
</div>
{currentLang === i.value && (<img className='popup-button-icon-active' src={ok} />
)}
</button>
))}
</div>
</>
)
}
@@ -0,0 +1,65 @@
import "./sharePopup.css";
import close from 'images/icons/close.svg'
import { useState, useEffect } from "react";
import { useTranslation } from "react-i18next";
export const PopupShare: React.FC<any> = ({ setClose }) => {
const [copy, setCopy] = useState(false);
function copyLink() {
navigator.clipboard.writeText(window.location.href);
setCopy((prev) => !prev);
}
function closePopup() {
setClose();
setCopy(false);
}
useEffect(() => () => setCopy(false), []);
const { t } = useTranslation();
return (
<div className="share-popup-container popup">
<div className="popup-container-position">
<div onClick={closePopup} className="icon-close-container">
<img src={close}></img>
</div>
<div className="mobile-users-part-header share-header-popup">
<h2 className="mobile-users-part-header-title">
{t("popup-control-invite-title")}
</h2>
</div>
<div className="share-popup-data-container">
<h3 className="share-popup-data-title">
{t("popup-control-link")}
</h3>
<input
className="share-popup-data-input href"
value={window.location.href}
readOnly
></input>
</div>
<div className="share-popup-data-container">
{!copy ? (
<button onClick={copyLink} className="button button-primary ">
<span className="share-popup-copy-button-icon"></span>
<span className="">
{copy ? t("popup-control-btn-active") : t("popup-control-btn")}
</span>
</button>
) : (
<button onClick={copyLink} className="button button-teritary">
<span className="share-popup-copied-button-icon"></span>
<span className="">{t("popup-control-btn-active")}</span>
</button>
)}
</div>
</div>
</div>
);
};
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 12L10 17L19 8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 211 B

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 7H10C9.44772 7 9 7.44772 9 8V20C9 20.5523 9.44772 21 10 21H18C18.5523 21 19 20.5523 19 20V8C19 7.44772 18.5523 7 18 7Z" fill="white"/>
<path d="M5 5V17C5 17.5523 5.44772 18 6 18C6.55228 18 7 17.5523 7 17V6C7 5.44772 7.44772 5 8 5H15C15.5523 5 16 4.55228 16 4C16 3.44772 15.5523 3 15 3H7C5.89543 3 5 3.89543 5 5Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 444 B

@@ -0,0 +1,179 @@
.share-popup-container {
position: absolute;
top: 50%;
left: 50%;
background: #151619;
transform: translate(-50%, -50%);
border-radius: 4px;
width: 400px;
color: #ffffff;
padding: 40px;
}
.icon-close-container {
cursor: pointer;
position: absolute;
padding: 8px;
height: 40px;
width: 40px;
box-sizing: border-box;
top: -40px;
right: -40px;
}
.popup-container-position {
position: relative;
display: flex;
flex-direction: column;
gap: 32px;
}
.share-header-popup {
padding-bottom: 0px;
}
.border-line {
width: 80%;
height: 1px;
background-color: #404040;
}
.share-popup-data-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.share-popup-data-title {
font-weight: 600;
font-size: 18px;
line-height: 22px;
user-select: none;
}
.share-popup-data-input {
background: #23242a;
border: 1px solid #23242a;
border-radius: 4px;
outline: none;
width: 100%;
border: none;
color: #ffffff;
box-sizing: border-box;
}
.share-popup-copied-button-icon {
width: 24px;
height: 24px;
background: url("copiedIcon.svg") 50% 50% no-repeat;
background-size: 100% 100%;
}
.button-copy {
width: fit-content;
display: flex;
padding: 12px 24px 12px 16px;
}
.mobile-users-part-header-title {
display: flex;
gap: 12px;
align-items: flex-start;
font-weight: 400;
font-style: normal;
font-weight: 400;
font-size: 24px;
line-height: 125%;
-webkit-user-select: none;
user-select: none;
}
.popup-position-container {
position: relative;
}
.mobile-users-part-header-close-button {
top: 0;
right: 0;
position: absolute;
width: 15px;
height: 15px;
border: none;
background-color: transparent;
background: url(images/icons/close.svg) 50% 50% no-repeat;
background-size: 100% 100%;
cursor: pointer;
}
.share-popup-data-input.code {
height: 76px;
font-weight: 300;
font-size: 38px;
line-height: 46px;
letter-spacing: 0.3em;
text-align: center;
padding: 15px 0;
}
.share-popup-data-input.href {
height: 48px;
padding: 14.5px 16px;
font-weight: 400;
font-size: 16px;
line-height: 19px;
}
.share-popup-copy-button {
display: flex;
box-sizing: border-box;
width: 176px;
height: 48px;
align-items: center;
justify-content: center;
border-radius: 12px;
background-color: #567ece;
border: none;
color: #ffffff;
font-weight: 500;
font-size: 16px;
line-height: 20px;
gap: 4px;
cursor: pointer;
transition: 0.3s;
}
.share-popup-copy-button:hover {
opacity: 0.7;
}
.share-popup-copy-button.copied {
background-color: #219653;
transition: 0.3s;
}
.share-popup-copy-button-icon {
width: 24px;
height: 24px;
background: url("copyIcon.svg") 50% 50% no-repeat;
background-size: 100% 100%;
}
.show-share-popup-enter {
opacity: 0;
}
.show-share-popup-enter-active {
opacity: 1;
transition: 0.3s;
}
.show-share-popup-exit {
opacity: 1;
}
.show-share-popup-exit-active {
opacity: 0;
transition: 0.3s;
}
@@ -0,0 +1,21 @@
import { User } from "components/pages/Stream/components/User/User"
import close from 'images/icons/close.svg'
import { useTranslation } from "react-i18next"
export const UserListMobilePopup: React.FC<any> = ({ isMobile, closePopup, userArr, height }) => {
const {t} = useTranslation()
return (<div style={{height: height}} className="popup-mobile">
<div className="popup-mobile-container">
<div style={{ top: "-20px", right: "-20px" }} onClick={closePopup} className="icon-close-container">
<img src={close}></img>
</div>
<h2 style={{ marginBottom: "20px" }}>{t('popup-userlist')}</h2>
{userArr.map((i: any) => (<User isMobile={isMobile} closeSidebar={closePopup}
isSidebarWide={true} /// this is for disable showhing button's caption
isAdmin={false}
></User>
))}
</div>
</div>)
}
@@ -0,0 +1,65 @@
.popup-header-mobile {
display: flex;
flex-direction: row;
gap: 12px;
align-items: center;
}
.popup-invite-title {
font-style: normal;
font-weight: 400;
font-size: 20px;
line-height: 120%;
/* identical to box height, or 24px */
/* Graff/White/Inactive */
color: #C5C7CE;
margin: 0;
}
.popup-invite-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.popup-exit-container {
padding-top: 48px;
}
.popup-exit-button-container-mobile {
display: flex;
flex-direction: row;
gap: 8px;
}
.popup-button-language {
position: relative;
text-align: left;
font-family: 'Inter';
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 50%;
color: #FFFFFF;
padding: 16px 12px;
position: relative;
/* Button_1 */
background: #23242A;
border-radius: 4px;
}
.popup-button-container-language {
display: flex;
flex-direction: column;
gap: 2px;
}
.popup-button-icon-active {
position: absolute;
right: 12px;
top: 20px;
}
@@ -1,45 +0,0 @@
import "./playerStyles.css";
import { useHistory, useParams } from "react-router-dom";
import React, { useEffect } from "react";
import { Sidebar } from "../sidebar/sidebar";
import { connectSession } from "../../store/reducers/ActionCreator";
import { useAppDispatch, useAppSelector } from "../../hooks/redux";
import { sessionSlice } from "../../store/reducers/sessionSlice";
type link = {
id: string;
};
export const PlayerComponent: React.FC<any> = ({ closeStream }) => {
const { id } = useParams<link>();
const dispatch = useAppDispatch();
const { cleanErrors } = sessionSlice.actions;
const history = useHistory()
useEffect(() => {
dispatch(connectSession(id)).then((res: any) => {
if (res.error) {
alert(res.payload);
history.push('/')
}
});
return () => {
dispatch(cleanErrors());
};
}, []);
const { url } = useAppSelector((state) => state.sessionReducer);
return (
<>
<iframe
id='player'
onBlur={(e) => e.target.focus()} /// element loosing focus and keyboard input doesn't work
src={url}
className={"player playerOn"}
security={""}
></iframe>
<Sidebar closeStream={closeStream}></Sidebar>
</>
);
};
@@ -1,8 +0,0 @@
.playerOn {
user-select: none;
pointer-events: all;
}
.playerOff {
pointer-events: none;
}
@@ -1,63 +0,0 @@
.popup-connect {
width: 100%;
background: transparent;
padding: 56px;
box-sizing: border-box;
}
.popup-logo {
height: 94px;
width: 100%;
margin-bottom: 32px;
object-fit: contain;
}
.popup-button-container {
display: flex;
flex-direction: column;
gap: 16px;
}
.line {
height: 1px;
background-color: #23242A;
width: 100%;
}
.button-connect {
color: #FFFFFF;
font-style: normal;
font-weight: 400;
font-size: 16px;
line-height: 150%;
height: 48px;
background: linear-gradient(180deg, #BC75FF 0%, #798FFF 100%);
border-radius: 4px;
padding: 12px 24px;
}
.button-back {
padding: 8px 16px;
border-radius: 4px;
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 130%;
color: #C5C7CE;
background: #1C1D21;
border-radius: 4px;
height: 32px;
}
@media screen and (max-width: 640px) {
.popup-connect {
padding: 16px;
}
}
@@ -1,35 +0,0 @@
import { useHistory } from "react-router-dom";
import "./popupConnect.css";
import { useTranslation } from "react-i18next";
export const PopupConnect: React.FC<any> = ({ onConnect, logo, isLoading }) => {
const history = useHistory();
const { t } = useTranslation();
const handleConnect = () => {
onConnect().then((res: any) => {
if (!res.error) {
history.push(`/stream/${res.payload.session_id}`);
} else {
alert(res.payload);
history.push('/')
}
});
};
return (
<div className="popup-connect">
<img className="popup-logo" src={logo} alt="лого" />
<div className="popup-button-container">
<button
onClick={handleConnect}
className="button-connect">{t("popup-main-btn-start")}
</button>
<div className="popup-line"></div>
<button onClick={() => history.goBack()} className="button-back">{t("popup-main-select")}
</button>
</div>
</div>
);
};
-76
View File
@@ -1,76 +0,0 @@
.popup__user {
position: relative;
width: 494px;
background: #262626;
border-radius: 32px;
padding: 48px;
box-sizing: border-box;
}
.popup__user_back {
position: absolute;
width: 16px;
height: 16px;
cursor: pointer;
}
.popup__user_content {
display: flex;
flex-direction: column;
gap: 24px;
align-items: center;
}
.popup__user_picture {
background: #219653;
width: 198px;
height: 198px;
border-radius: 172px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.popup__user_name {
font-style: normal;
font-weight: 400;
font-size: 38px;
line-height: 130%;
/* or 49px */
text-align: center;
/* App/White */
color: #ffffff;
border-bottom: 1px solid #FFFFFF;
margin: 0 0 42px;
width: 57%;
}
.popup__button {
display: none;
}
@media screen and (max-width: 639px) {
.popup__user {
width: 100%;
border-radius: 0px;
height: 100%;
padding: 32px 10px 67px;
height: 100vh;
}
.popup__button {
display: flex;
flex-direction: column;
gap: 16px;
}
.popup__user_name {
margin: 0 0 54px;
}
.popup__user_back {
display: none;
}
}
-25
View File
@@ -1,25 +0,0 @@
import "../../styles/styles.css";
import "./popupUser.css";
import person from "../../images/Person.png";
import buttonBack from "../../images/backButton.svg";
export const PopupUser: React.FC = ({}) => {
return (
<div className="popup__user">
<img alt="btn-back" className="popup__user_back" src={buttonBack}></img>
<div className="popup__user_content">
<div className="popup__user_picture">
<img alt="avatar" src={person}></img>
</div>
<p className="popup__user_name">Константин Коренецкий </p>
<button className="button">Продолжить</button>
<div className="popup__button">
<span className="line"></span>
<button className="popup__caption">
Выбор способа демонстрации{" "}
</button>
</div>
</div>
</div>
);
};
@@ -1,48 +0,0 @@
import "../sidebar/toolbar.css";
import { useState } from "react";
import { TUserControl } from "../../utils/types";
import { MicroButton } from "../ui/MicroButton/MicroButton";
import iconUser from "../../images/icons/user.svg";
import { PopupUserControlButton } from "../popupUserControlButton/PopupUserControlButton";
export const PopupUserControl: React.FC<TUserControl> = ({
user,
isAdmin,
sendControlRequest,
localUser,
handleControl,
setNotification,
handleReturnControl,
setMuted,
isMuted,
}) => {
const handleMuteUser = () => {
const element = document.getElementById(user.id) as HTMLAudioElement;
if (isMuted) {
console.log(element);
element.volume = 1;
setMuted(false);
} else {
element.volume = 0;
setMuted(true);
}
};
return (
<div className="toolbar-open-add-button-container">
<div className="user-icon">
<img alt="icon" className="toolbar-icon" src={iconUser}></img>
</div>
<MicroButton isMuted={isMuted} onClick={handleMuteUser}></MicroButton>
<PopupUserControlButton
handleControl={handleControl}
localUser={localUser}
sendControlRequest={sendControlRequest}
isAdmin={isAdmin}
setNotification={setNotification}
user={user}
handleReturnControl={handleReturnControl}
></PopupUserControlButton>
</div>
);
};

Some files were not shown because too many files have changed in this diff Show More