From 5c8403fd7f4c253ac8d7bba73dfb83eebeb21a3d Mon Sep 17 00:00:00 2001 From: VyacheslavShtyrlin Date: Tue, 27 Dec 2022 19:49:00 +0500 Subject: [PATCH] added i18next language lib and some style fixes --- package-lock.json | 263 +++++++++++++++++- package.json | 6 + public/assets/locales/en/translation.json | 28 ++ public/assets/locales/ru/translation.json | 29 ++ src/App.tsx | 80 +----- src/components/ControlPanel/ControlPanel.tsx | 5 +- src/components/ExitPopup/ExitPopup.tsx | 49 ++-- .../LanguagePopup/LanguagePopup.tsx | 46 ++- src/components/Main/Main.tsx | 4 +- src/components/PopupShare/PopupShare.tsx | 11 +- src/components/demos/demos.css | 21 +- src/components/demos/demos.tsx | 25 +- src/components/header/header.tsx | 151 ++++++---- src/components/popupConnect/popupConnect.tsx | 20 +- .../popupConnectEx/popupConnectEx.tsx | 9 +- src/components/sidebar/sidebar.tsx | 21 +- src/components/sidebar/toolbar.css | 4 + .../ui/ControlButton/ControlButton.tsx | 4 +- src/components/ui/ExitButton/ExitButton.tsx | 4 +- .../ui/FullscreenButton/FullscreenButton.tsx | 29 +- .../ui/LanguageButton/LanguageButton.tsx | 41 +-- src/components/ui/MicroButton/MicroButton.tsx | 17 +- src/components/ui/ShareButton/ShareButton.tsx | 4 +- src/components/ui/button/button.tsx | 50 ++-- src/index.tsx | 45 ++- src/store/reducers/cardSlice.ts | 1 + src/store/reducers/languageSlice.ts | 25 ++ src/store/store.ts | 2 + src/styles/styles.css | 73 ++--- 29 files changed, 732 insertions(+), 335 deletions(-) create mode 100644 public/assets/locales/en/translation.json create mode 100644 public/assets/locales/ru/translation.json create mode 100644 src/store/reducers/languageSlice.ts diff --git a/package-lock.json b/package-lock.json index e93b0b6..cbd5cb3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,10 +21,15 @@ "framer-motion": "^7.4.0", "freeice": "^2.2.2", "html-react-parser": "^3.0.4", + "i18next": "^22.4.6", + "i18next-browser-languagedetector": "^7.0.1", + "i18next-http-backend": "^2.1.1", + "js-cookie": "^3.0.1", "peer": "^0.6.1", "peerjs": "^1.4.7", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^12.1.1", "react-player": "^2.11.0", "react-redux": "^8.0.5", "react-router-dom": "^5.2.0", @@ -36,6 +41,7 @@ "web-vitals": "^2.1.4" }, "devDependencies": { + "@types/js-cookie": "^3.0.2", "@types/react-router-dom": "^5.3.3", "@types/react-transition-group": "^4.4.5" } @@ -1814,11 +1820,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz", - "integrity": "sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" @@ -3952,6 +3958,12 @@ "pretty-format": "^27.0.0" } }, + "node_modules/@types/js-cookie": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.2.tgz", + "integrity": "sha512-6+0ekgfusHftJNYpihfkMu8BWdeHs9EOJuGcSofErjstGPfPGEu9yTu4t460lTzzAMl2cM5zngQJqPMHbbnvYA==", + "dev": true + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -5889,6 +5901,14 @@ "node": ">=10" } }, + "node_modules/cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "dependencies": { + "node-fetch": "2.6.7" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -8827,6 +8847,14 @@ "node": ">=12" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/html-react-parser": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-3.0.4.tgz", @@ -8989,6 +9017,44 @@ "node": ">=10.17.0" } }, + "node_modules/i18next": { + "version": "22.4.6", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.6.tgz", + "integrity": "sha512-9Tm1ezxWyzV+306CIDMBbYBitC1jedQyYuuLtIv7oxjp2ohh8eyxP9xytIf+2bbQfhH784IQKPSYp+Zq9+YSbw==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.20.6" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz", + "integrity": "sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==", + "dependencies": { + "@babel/runtime": "^7.19.4" + } + }, + "node_modules/i18next-http-backend": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.1.1.tgz", + "integrity": "sha512-jByfUCDVgQ8+/Wens7queQhYYvMcGTW/lR4IJJNEDDXnmqjLrwi8ubXKpmp76/JIWEZHffNdWqnxFJcTVGeaOw==", + "dependencies": { + "cross-fetch": "3.1.5" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -11507,6 +11573,14 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", + "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==", + "engines": { + "node": ">=12" + } + }, "node_modules/js-sdsl": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz", @@ -12130,6 +12204,44 @@ "tslib": "^2.0.3" } }, + "node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -14510,6 +14622,27 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, + "node_modules/react-i18next": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz", + "integrity": "sha512-mFdieOI0LDy84q3JuZU6Aou1DoWW2fhapcTGeBS8+vWSJuViuoCLQAMYSb0QoHhXS8B0WKUOPpx4cffAP7r/aA==", + "dependencies": { + "@babel/runtime": "^7.14.5", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 19.0.0", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -14840,9 +14973,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/regenerator-transform": { "version": "0.15.0", @@ -16692,6 +16825,14 @@ "node": ">= 0.8" } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -18825,11 +18966,11 @@ } }, "@babel/runtime": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz", - "integrity": "sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" } }, "@babel/runtime-corejs3": { @@ -20333,6 +20474,12 @@ "pretty-format": "^27.0.0" } }, + "@types/js-cookie": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.2.tgz", + "integrity": "sha512-6+0ekgfusHftJNYpihfkMu8BWdeHs9EOJuGcSofErjstGPfPGEu9yTu4t460lTzzAMl2cM5zngQJqPMHbbnvYA==", + "dev": true + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -21808,6 +21955,14 @@ "yaml": "^1.10.0" } }, + "cross-fetch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", + "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", + "requires": { + "node-fetch": "2.6.7" + } + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -23924,6 +24079,14 @@ "terser": "^5.10.0" } }, + "html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "requires": { + "void-elements": "3.1.0" + } + }, "html-react-parser": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-3.0.4.tgz", @@ -24036,6 +24199,30 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" }, + "i18next": { + "version": "22.4.6", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-22.4.6.tgz", + "integrity": "sha512-9Tm1ezxWyzV+306CIDMBbYBitC1jedQyYuuLtIv7oxjp2ohh8eyxP9xytIf+2bbQfhH784IQKPSYp+Zq9+YSbw==", + "requires": { + "@babel/runtime": "^7.20.6" + } + }, + "i18next-browser-languagedetector": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.0.1.tgz", + "integrity": "sha512-Pa5kFwaczXJAeHE56CHG2aWzFBMJNUNghf0Pm4SwSrEMps/PTKqW90EYWlIvhuYStf3Sn1K0vw+gH3+TLdkH1g==", + "requires": { + "@babel/runtime": "^7.19.4" + } + }, + "i18next-http-backend": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-2.1.1.tgz", + "integrity": "sha512-jByfUCDVgQ8+/Wens7queQhYYvMcGTW/lR4IJJNEDDXnmqjLrwi8ubXKpmp76/JIWEZHffNdWqnxFJcTVGeaOw==", + "requires": { + "cross-fetch": "3.1.5" + } + }, "iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -25845,6 +26032,11 @@ } } }, + "js-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", + "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==" + }, "js-sdsl": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz", @@ -26315,6 +26507,35 @@ "tslib": "^2.0.3" } }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -27856,6 +28077,15 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", "integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==" }, + "react-i18next": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-12.1.1.tgz", + "integrity": "sha512-mFdieOI0LDy84q3JuZU6Aou1DoWW2fhapcTGeBS8+vWSJuViuoCLQAMYSb0QoHhXS8B0WKUOPpx4cffAP7r/aA==", + "requires": { + "@babel/runtime": "^7.14.5", + "html-parse-stringify": "^3.0.1" + } + }, "react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -28110,9 +28340,9 @@ } }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "regenerator-transform": { "version": "0.15.0", @@ -29487,6 +29717,11 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" }, + "void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==" + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index abe4322..f866af8 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,15 @@ "framer-motion": "^7.4.0", "freeice": "^2.2.2", "html-react-parser": "^3.0.4", + "i18next": "^22.4.6", + "i18next-browser-languagedetector": "^7.0.1", + "i18next-http-backend": "^2.1.1", + "js-cookie": "^3.0.1", "peer": "^0.6.1", "peerjs": "^1.4.7", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^12.1.1", "react-player": "^2.11.0", "react-redux": "^8.0.5", "react-router-dom": "^5.2.0", @@ -55,6 +60,7 @@ ] }, "devDependencies": { + "@types/js-cookie": "^3.0.2", "@types/react-router-dom": "^5.3.3", "@types/react-transition-group": "^4.4.5" } diff --git a/public/assets/locales/en/translation.json b/public/assets/locales/en/translation.json new file mode 100644 index 0000000..738c19f --- /dev/null +++ b/public/assets/locales/en/translation.json @@ -0,0 +1,28 @@ +{ + "demo-title": "Available demonstrations", + "popup-main-title": "Start new demonstration", + "popup-main-caption": "Connect to an existing one", + "popup-main-select": "Select residential complex", + "popup-main-btn-start": "Start", + "popup-main-btn-connect": "Connect", + "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", + "request-control-btn": "Request control", + "request-control-btn-disable": "Stop control", + "mute-control-btn": "Turn On Mic", + "mute-control-btn-disable": "Turn Off Mic", + "share-contro-btn": "Share", + "popup-control-invite-title": "Invite to the demonstration", + "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-exit-title": "Are you sure you want to end the demo?", + "popup-control-yes": "Finish", + "popup-control-no": "Stay" +} diff --git a/public/assets/locales/ru/translation.json b/public/assets/locales/ru/translation.json new file mode 100644 index 0000000..3059524 --- /dev/null +++ b/public/assets/locales/ru/translation.json @@ -0,0 +1,29 @@ +{ + "demo-title": "Доступные демонстрации", + "popup-main-title": "Начать новую демонстрацию", + "popup-main-caption": "Подключиться к существующией", + "popup-main-select": "Выбор жилого комплекса", + "popup-main-btn-start": "Начать", + "popup-main-btn-connect": "Подключиться", + "popup-connect-title": "Код подключения к демонстрации", + "popup-connect-btn-mode": "Выбор способа демонстрации", + "popup-connect-btn-continue": "Продолжить", + "fullscreen-control-btn": "Развернуть", + "fullscreen-control-btn-active": "Свернуть", + "request-control-btn": "Запрос управления", + "request-control-btn-disable": "Запрет управления", + "mute-control-btn": "Включить микрофон", + "mute-control-btn-disable": "Выключить микрофон", + "share-contro-btn": "Поделиться", + "popup-control-invite-title": "Пригласить на демонстрацию", + "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-yes": "Закончить", + "popup-control-no": "Остаться" + +} diff --git a/src/App.tsx b/src/App.tsx index cd0b52c..640d6f3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,14 +10,12 @@ 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 textRU from "./utils/textRU"; -import textEN from "./utils/textEN"; +import { useTranslation } from "react-i18next"; +import cookies from "js-cookie"; - -function App() { - const [language, setLanguage] = useState(""); - const [text, setText] = useState(textRU); +const App: React.FC = () => { const [visible, setVisible] = useState({ popup1: true, popup2: false, @@ -26,11 +24,12 @@ function App() { const dispatch = useAppDispatch(); const history = useHistory(); const { handleCurrentCard } = cardSlice.actions; + const { handleChangeLanguage } = languageSlice.actions; const { cards } = useAppSelector((state) => state.cardReducer); - const savedLanguage = localStorage.getItem("savedLang"); useEffect(() => { dispatch(fetchCards()); + dispatch(handleChangeLanguage(cookies.get("i18next"))); }, []); const handleCards = (card: ICards) => { @@ -42,61 +41,14 @@ function App() { history.push("/"); }; - useEffect(() => { - if (savedLanguage !== null) { - setSavedLanguage(); - } else { - setInitialLanguage(); - } - }, []); - - function setSavedLanguage() { - if (savedLanguage === "RU") { - setLanguage("RU"); - localStorage.setItem("lang", "RU"); - setText(textRU); - } else if (savedLanguage === "EN") { - setLanguage("EN"); - localStorage.setItem("lang", "EN"); - setText(textEN); - } - } - - function setInitialLanguage() { - if (window.navigator.language === "ru") { - setLanguage("RU"); - localStorage.setItem("lang", "RU"); - setText(textRU); - } else { - setLanguage("EN"); - setText(textEN); - } - } - - function changeLanguage(language: string) { - if (language === "RU") { - setLanguage(language); - setText(textRU); - localStorage.setItem("savedLang", "RU"); - localStorage.setItem("lang", "RU"); - } else { - setLanguage("EN"); - localStorage.setItem("lang", "EN"); - localStorage.setItem("savedLang", "EN"); - setText(textEN); - } - } + const { t } = useTranslation(); return ( -
+
-

{text.demos.title}

+

{t("demo-title")}

{cards.map((i: any) => (
-
+
-
+
@@ -134,6 +78,6 @@ function App() { ); -} +}; export default App; diff --git a/src/components/ControlPanel/ControlPanel.tsx b/src/components/ControlPanel/ControlPanel.tsx index e0a25a5..cafe215 100644 --- a/src/components/ControlPanel/ControlPanel.tsx +++ b/src/components/ControlPanel/ControlPanel.tsx @@ -8,12 +8,15 @@ import React, { useState, useEffect } from "react"; export const ControlPanel: React.FC = ({ handleOpenSharePopup, handleOpenExitPopup, + handleMuteClick, + isMuted }) => { + return (
console.log("click!")}> - +
console.log("click!")}> diff --git a/src/components/ExitPopup/ExitPopup.tsx b/src/components/ExitPopup/ExitPopup.tsx index f3772d1..40ff99e 100644 --- a/src/components/ExitPopup/ExitPopup.tsx +++ b/src/components/ExitPopup/ExitPopup.tsx @@ -1,19 +1,36 @@ -import '../PopupShare/sharePopup.css' -import './ExitPopup.css' -import { TSidebarPopup } from '../../utils/types' +import "../PopupShare/sharePopup.css"; +import "./ExitPopup.css"; +import { TSidebarPopup } from "../../utils/types"; +import { useTranslation } from "react-i18next"; export const ExitPopup: React.FC = ({ setClose, onExit }) => { + const { t } = useTranslation(); - return ( -
-
- Вы уверены, что хотите закончить демонстрацию? - -
-
- - -
-
- ) -} \ No newline at end of file + return ( +
+
+ + {t("popup-control-exit-title")} + + +
+
+ + +
+
+ ); +}; diff --git a/src/components/LanguagePopup/LanguagePopup.tsx b/src/components/LanguagePopup/LanguagePopup.tsx index a9b1c89..4a673bb 100644 --- a/src/components/LanguagePopup/LanguagePopup.tsx +++ b/src/components/LanguagePopup/LanguagePopup.tsx @@ -1,12 +1,38 @@ -import '../sidebar/toolbar.css' +import "../sidebar/toolbar.css"; +import { languageSlice } from "../../store/reducers/languageSlice"; +import { useAppDispatch, useAppSelector } from "../../hooks/redux"; -export const LanguagePopup: React.FC = ({ setOpen}) => { - return (
-
- +export const LanguagePopup: React.FC = ({ 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 ( +
+ {buttons.map((i) => ( +
+
-
- -
-
) -} \ No newline at end of file + ))} +
+ ); +}; diff --git a/src/components/Main/Main.tsx b/src/components/Main/Main.tsx index d3fa0d1..d10707f 100644 --- a/src/components/Main/Main.tsx +++ b/src/components/Main/Main.tsx @@ -11,7 +11,7 @@ import { connectSession, } from "../../store/reducers/ActionCreator"; -export const Main: React.FC = ({ text, visible, setVisible }) => { +export const Main: React.FC = ({ visible, setVisible }) => { const [value, setValue] = useState(""); const dispatch = useAppDispatch(); const { popup1, popup2 } = visible; @@ -33,7 +33,6 @@ export const Main: React.FC = ({ text, visible, setVisible }) => { isLoading={isLoading} logo={currentCard?.image.logo} onConnect={() => dispatch(createSession(currentCard.title))} - text={text.popupConnect} visible={visible} setVisible={setVisible} > @@ -55,7 +54,6 @@ export const Main: React.FC = ({ text, visible, setVisible }) => { onConnect={() => dispatch(connectSession(value))} visible={visible} setVisible={setVisible} - text={text.popupConnectEx} > )} diff --git a/src/components/PopupShare/PopupShare.tsx b/src/components/PopupShare/PopupShare.tsx index b25df0e..92fe4ed 100644 --- a/src/components/PopupShare/PopupShare.tsx +++ b/src/components/PopupShare/PopupShare.tsx @@ -1,6 +1,7 @@ import './sharePopup.css' import { useState, useEffect } from 'react' import { TSidebarPopup } from '../../utils/types' +import { useTranslation } from 'react-i18next' export const PopupShare: React.FC = ({ setClose, data }) => { const [copy, setCopy] = useState(false) @@ -19,26 +20,28 @@ export const PopupShare: React.FC = ({ setClose, data }) => { useEffect(() => () => setCopy(false), []); + const {t} = useTranslation(); + return (
- Пригласить на демонстрацию + {t('popup-control-invite-title')}
- Код подключения + {t('popup-control-code')}
- Ссылка для подключения + {t('popup-control-link')}
diff --git a/src/components/demos/demos.css b/src/components/demos/demos.css index cf277f2..9ce0e59 100644 --- a/src/components/demos/demos.css +++ b/src/components/demos/demos.css @@ -139,7 +139,11 @@ @media screen and (min-width: 1600px) { .demo__icon { - top: 57%; + top: 56%; + } + + .demo_image { + height: 360px; } .title__demo { @@ -157,6 +161,14 @@ .caption2 { font-size: 16px; } + + .demo { + height: 700px; + } + + .demo__container { + gap: 30px; + } } @media screen and (max-width: 1279px) { @@ -202,6 +214,10 @@ gap: 12px; padding: 24px; } + + .demo { + height: 434px; + } } @media screen and (max-width: 1024px) { @@ -256,6 +272,9 @@ .demos__tittle { font-size: 28px; width: 300px; + } + .demo { + height: 436px; } } diff --git a/src/components/demos/demos.tsx b/src/components/demos/demos.tsx index 92bdeb4..42acd79 100644 --- a/src/components/demos/demos.tsx +++ b/src/components/demos/demos.tsx @@ -3,9 +3,12 @@ import "../../styles/styles.css"; import building1 from "./building1.png"; import iconButton from "./iconButton.svg"; import { Link } from "react-router-dom"; +import { useAppSelector } from "../../hooks/redux"; export const Demos: React.FC = ({ item, onClick }) => { - console.log(item) + const { currentLang } = useAppSelector((state) => state.languageReducer); + + console.log(currentLang); return (
onClick()} className="main-block__container">
@@ -20,13 +23,27 @@ export const Demos: React.FC = ({ item, onClick }) => { >
-

{item.title_full.ru}

+

+ {currentLang === "ru" ? item.title_full.ru : item.title_full.en} +

{item.location.ru}

-

{item.description.ru}

- {item.description_second ?

{item.description_second.ru}

: <>} +

+ {currentLang === "ru" + ? item.description.ru + : item.description.en} +

+ {item.description_second ? ( +

+ {currentLang === "ru" + ? item.description_second.ru + : item.description_second.en} +

+ ) : ( + <> + )}
diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index b764e71..b7b0d32 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -4,81 +4,118 @@ 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; - text?: any; }; -export const Header: React.FC = - ({ changeLanguage, language, text }) => { - const [open, setOpen] = useState(false) - const [menuOpen, setMenuOpen] = useState(false) - const [opacity, setOpacity] = useState(false) - const userLanguage = language === 'RU'; - const location = useLocation() - console.log(location) +export const Header: React.FC = ({}) => { + const [open, setOpen] = useState(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) { - changeLanguage(value) - setOpen(prev => !prev) - } + function handleUpdateLanguage(value: string) { + dispatch(handleChangeLanguage(value)); + setOpen((prev) => !prev); + } - function handleOpen() { - setOpen(prev => !prev) - } + function handleOpen() { + setOpen((prev) => !prev); + } - function handleOpacity() { - setOpacity(true) - } + function handleOpacity() { + setOpacity(true); + } - const style = { - background: 'rgba(235, 235, 235, 0.2' - }; + const style = { + background: "rgba(235, 235, 235, 0.2", + }; - const iconTransform = { - transform: 'rotate(-90deg)' - } as CSSProperties + const iconTransform = { + transform: "rotate(-90deg)", + } as CSSProperties; - const setBackground = opacity && !open as boolean; + const setBackground = opacity && (!open as boolean); - function setOpacityAndMenu() { - setMenuOpen(true) - const targetElement = document.querySelector('body') as HTMLElement - targetElement.style.overflow = 'hidden' - } + 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"; + } - function openMenu() { - setMenuOpen(false) - const targetElement = document.querySelector('body') as HTMLElement - targetElement.style.overflow = 'visible' - - } - - return ( -
-
- company-logo -
-
- img -
-
setOpacity(false)} onClick={handleOpen} className=" header-lang-button-picked">{language}
+ return ( +
+
+ company-logo +
+
+ img +
+
setOpacity(false)} + onClick={handleOpen} + className=" header-lang-button-picked" + > + {currentLang.toLocaleUpperCase()}
- {language === 'RU' ? ( - ) : ()}
+ {currentLang === "ru" ? ( + + ) : ( + + )}
- - ); - } - +
+ ); +}; diff --git a/src/components/popupConnect/popupConnect.tsx b/src/components/popupConnect/popupConnect.tsx index 78e9de8..f8dfc44 100644 --- a/src/components/popupConnect/popupConnect.tsx +++ b/src/components/popupConnect/popupConnect.tsx @@ -1,22 +1,18 @@ import { useHistory } from "react-router-dom"; -import { useAppSelector } from "../../hooks/redux"; import "../../styles/styles.css"; -import { PayloadAction } from "@reduxjs/toolkit"; -import { IData } from "../../models/IData"; -import { sessionState } from "../../store/reducers/sessionSlice"; +import { useTranslation } from "react-i18next"; export const PopupConnect: React.FC = ({ setVisible, - text, onConnect, logo, - isLoading + isLoading, }) => { const history = useHistory(); + const { t } = useTranslation(); const handleConnect = () => { onConnect().then((res: any) => { - console.log(res); if (!res.error) { history.push(`/stream/${res.payload.connection_code}`); } else { @@ -30,18 +26,18 @@ export const PopupConnect: React.FC = ({ logoRC
-

{text.caption}

+

{t("popup-main-title")}

-

{text.caption1}

+

{t("popup-main-caption")}

diff --git a/src/components/popupConnectEx/popupConnectEx.tsx b/src/components/popupConnectEx/popupConnectEx.tsx index d7ec331..8ae9c60 100644 --- a/src/components/popupConnectEx/popupConnectEx.tsx +++ b/src/components/popupConnectEx/popupConnectEx.tsx @@ -1,10 +1,10 @@ import "../../styles/styles.css"; import "./popupConnect.css"; import { useHistory } from "react-router-dom"; +import { useTranslation } from "react-i18next"; export const PopupConnectEx: React.FC = ({ setVisible, - text, onConnect, logo, setValue, @@ -12,6 +12,7 @@ export const PopupConnectEx: React.FC = ({ isLoading, }) => { const history = useHistory(); + const { t } = useTranslation(); const handleConnect = () => { onConnect().then((res: any) => { @@ -28,7 +29,7 @@ export const PopupConnectEx: React.FC = ({
logoRC -

{text.caption}

+

{t("popup-connect-title")}

setValue(e.target.value)} @@ -42,7 +43,7 @@ export const PopupConnectEx: React.FC = ({ !!value || isLoading ? "button" : "button button__disabled" } > - {text.button} + {t("popup-connect-btn-continue")}
diff --git a/src/components/sidebar/sidebar.tsx b/src/components/sidebar/sidebar.tsx index e1a7115..aaae35e 100644 --- a/src/components/sidebar/sidebar.tsx +++ b/src/components/sidebar/sidebar.tsx @@ -1,5 +1,5 @@ import "./toolbar.css"; -import React, { useState, useEffect, useCallback } from "react"; +import React, { useState, useEffect } from "react"; import { UserList } from "../UserList/UserList"; import { FullscreenButton } from "../ui/FullscreenButton/FullscreenButton"; import { PopupShare } from "../PopupShare/PopupShare"; @@ -8,7 +8,6 @@ import { ControlPanel } from "../ControlPanel/ControlPanel"; import { AnimatePresence, motion } from "framer-motion"; import { sidebarVariants, popupAnimation } from "../../utils/animationProps"; - type link = { id: string; }; @@ -16,29 +15,22 @@ const userLocal = { id: Math.floor(Math.random() * 100), }; -export const Sidebar: React.FC = ({ - closeStream, - data, -}) => { +export const Sidebar: React.FC = ({ closeStream, data }) => { const [open, setOpen] = useState(true); const [popup, setPopup] = useState({ popup1: false, popup2: false, }); - - const [selected, setSelected] = useState(false); const [icon, setIcon] = useState(""); const [isMuted, setMuted] = useState(true); - const handleMuteClick = () => { setMuted((prev) => !prev); }; - function handleClosePopups() { setPopup({ popup1: false, @@ -65,7 +57,6 @@ export const Sidebar: React.FC = ({ setSelected(false); } - useEffect(() => () => unmountComponent(), []); function unmountComponent() { @@ -77,7 +68,6 @@ export const Sidebar: React.FC = ({ } - return (
= ({
- +
diff --git a/src/components/sidebar/toolbar.css b/src/components/sidebar/toolbar.css index 86ebc94..7884141 100644 --- a/src/components/sidebar/toolbar.css +++ b/src/components/sidebar/toolbar.css @@ -151,6 +151,10 @@ padding: 0px; } +.toolbar-button-active { + background: #567ece; +} + .user-icon { position: relative; width: 40px; diff --git a/src/components/ui/ControlButton/ControlButton.tsx b/src/components/ui/ControlButton/ControlButton.tsx index 91473ff..6568b10 100644 --- a/src/components/ui/ControlButton/ControlButton.tsx +++ b/src/components/ui/ControlButton/ControlButton.tsx @@ -8,8 +8,8 @@ export const ControlButton: React.FC = ({ onClick }) => { const [active, setActive] = useState(false); const button = { icon: control, - active: "Запрет управления", - inactive: "Запрос управления", + active: "request-control-btn", + inactive: "request-control-btn-disable", type: "control", }; diff --git a/src/components/ui/ExitButton/ExitButton.tsx b/src/components/ui/ExitButton/ExitButton.tsx index e5611bd..ad6199c 100644 --- a/src/components/ui/ExitButton/ExitButton.tsx +++ b/src/components/ui/ExitButton/ExitButton.tsx @@ -7,8 +7,8 @@ export const ExitButton: React.FC = ({ onClick }) => { const [active, setActive] = useState(false); const button = { icon: exit, - active: "Завершить презентацию", - inactive: "Завершить презентацию", + active: "exit-control-btn", + inactive: "exit-control-btn", type: "exit", }; diff --git a/src/components/ui/FullscreenButton/FullscreenButton.tsx b/src/components/ui/FullscreenButton/FullscreenButton.tsx index e03cc24..7015ceb 100644 --- a/src/components/ui/FullscreenButton/FullscreenButton.tsx +++ b/src/components/ui/FullscreenButton/FullscreenButton.tsx @@ -3,43 +3,40 @@ import { Button } from "../button/button"; import fullscreen from "../../../images/icons/fullscreen.svg"; import fullscreenOff from "../../../images/icons/fullscreenOff.svg"; - -export const FullscreenButton = ({ }) => { +export const FullscreenButton = ({}) => { const [active, setActive] = useState(false); const [button, setButton] = useState({ icon: fullscreen, - active: "Развернуть", - inactive: "Свернуть", + inactive: "fullscreen-control-btn", + active: "fullscreen-control-btn-active", type: "fullscreen", - - }) + }); function handleClick() { setActive((prev) => !prev); setButton({ icon: active ? fullscreen : fullscreenOff, - active: "Развернуть", - inactive: "Свернуть", + inactive: "fullscreen-control-btn", + active: "fullscreen-control-btn-active", type: "fullscreen", - }) + }); if (active) { - document.exitFullscreen() + document.exitFullscreen(); } else { - document.body.requestFullscreen() + document.body.requestFullscreen(); } } - useEffect(() => { return () => { - document.exitFullscreen() - } - }, []) + document.exitFullscreen(); + }; + }, []); return (
); -}; \ No newline at end of file +}; diff --git a/src/components/ui/LanguageButton/LanguageButton.tsx b/src/components/ui/LanguageButton/LanguageButton.tsx index 9c26de7..8e35fb4 100644 --- a/src/components/ui/LanguageButton/LanguageButton.tsx +++ b/src/components/ui/LanguageButton/LanguageButton.tsx @@ -6,33 +6,31 @@ import { AnimatePresence, motion } from "framer-motion"; const container = { hidden: { opacity: 0, - transition: { duration: 0.2, ease: 'easeOut' } + transition: { duration: 0.2, ease: "easeOut" }, }, show: { opacity: 1, - transition: { delay: 0.15, duration: 0.2, ease: 'easeIn' }, + transition: { delay: 0.15, duration: 0.2, ease: "easeIn" }, }, exit: { opacity: 0, - transition: { duration: 0.2, ease: 'easeOut' } - } -} - - - + transition: { duration: 0.2, ease: "easeOut" }, + }, +}; export const LanguageButton: React.FC = ({ hover, setHover }) => { const [active, setActive] = useState(false); - const [open, setOpen] = useState(false) + const [open, setOpen] = useState(false); + const button = { icon: microOn, - active: "Язык", - inactive: "Язык", + active: "language-control-btn", + inactive: "language-control-btn", type: "language", }; function handleClick() { - setOpen(true) + setOpen(true); setActive((prev) => !prev); } @@ -40,14 +38,17 @@ export const LanguageButton: React.FC = ({ hover, setHover }) => {
- {open && ( - - )} + {open && ( + + + + )}
); -}; \ No newline at end of file +}; diff --git a/src/components/ui/MicroButton/MicroButton.tsx b/src/components/ui/MicroButton/MicroButton.tsx index dc6c39a..b50baea 100644 --- a/src/components/ui/MicroButton/MicroButton.tsx +++ b/src/components/ui/MicroButton/MicroButton.tsx @@ -1,23 +1,22 @@ -import { useState } from "react"; import { Button } from "../button/button"; import microOn from "../../../images/icons/MicroOn.svg"; -import { THOC } from "../../../utils/types"; -export const MicroButton: React.FC = ({ onClick, isMuted }) => { +export const MicroButton: React.FC = ({ + onClick, + isMuted, + translation, +}) => { const button = { icon: microOn, - active: "Включить микрофон", - inactive: "Выключить микрофон", + active: "mute-control-btn", + inactive: "mute-control-btn-disable", type: "microphone", }; - - function handleClick() { - onClick() + onClick(); } - return (
diff --git a/src/components/ui/ShareButton/ShareButton.tsx b/src/components/ui/ShareButton/ShareButton.tsx index 14acf04..96b1e5f 100644 --- a/src/components/ui/ShareButton/ShareButton.tsx +++ b/src/components/ui/ShareButton/ShareButton.tsx @@ -6,8 +6,8 @@ export const ShareButton: React.FC = ({ onClick }) => { const button = { icon: share, - active: "Поделиться", - inactive: "Поделиться", + active: "share-contro-btn", + inactive: "share-contro-btn", type: "share", }; diff --git a/src/components/ui/button/button.tsx b/src/components/ui/button/button.tsx index ed5a981..8a810a2 100644 --- a/src/components/ui/button/button.tsx +++ b/src/components/ui/button/button.tsx @@ -1,45 +1,59 @@ import "../../sidebar/toolbar.css"; import disabledImg from "../../../images/icons/line.svg"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { AnimatePresence, motion } from "framer-motion"; import { animationButton } from "../../../utils/animationProps"; -import { TButton } from "../../../utils/types"; - +import { useTranslation } from "react-i18next"; +import { useAppSelector } from "../../../hooks/redux"; export const Button: React.FC = ({ button, onClick, active }) => { + const [hover, setHover] = useState(false); + const { currentLang } = useAppSelector((state) => state.languageReducer); - const [hover, setHover] = useState(false) - - const typeButton = - button.type !== 'fullscreen' && button.type !== 'language' + const typeButton = button.type !== "fullscreen" && button.type !== "language"; function handleClick() { - onClick() + onClick(); } + const { t } = useTranslation(); + return ( -
+
setHover(true)} onHoverEnd={() => setHover(false)} - className={button.type === 'exit' ? 'toolbar-button_exit toolbar-button' : 'toolbar-button'}> + className={ + button.type === "exit" + ? "toolbar-button_exit toolbar-button" + : "toolbar-button" + } + > {button.type === "language" ? ( - EN + {currentLang.toUpperCase()} ) : ( icon )} - {active && typeButton && iconDisabled} + {active && typeButton && ( + iconDisabled + )} {hover && ( - + - {active ? button.active : button.inactive} + {active ? t(button.active) : t(button.inactive)} )} diff --git a/src/index.tsx b/src/index.tsx index 4efc6a7..6d82818 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,22 +1,47 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; +import React, { Suspense, useEffect } from "react"; +import ReactDOM from "react-dom/client"; + +import "./index.css"; +import App from "./App"; +import reportWebVitals from "./reportWebVitals"; import { BrowserRouter } from "react-router-dom"; -import {Provider} from "react-redux"; -import {setupStore} from "./store/store"; +import { Provider } from "react-redux"; +import { setupStore } from "./store/store"; + +import i18next from "i18next"; +import { initReactI18next } from "react-i18next"; +import HttpApi from "i18next-http-backend"; +import LanguageDetector from "i18next-browser-languagedetector"; const store = setupStore(); +i18next + .use(HttpApi) + .use(LanguageDetector) + .use(initReactI18next) + .init({ + supportedLngs: ["en", "ru"], + fallbackLng: "en", + debug: false, + // Options for language detector + detection: { + order: ["cookie", "navigator"], + caches: ["cookie"], + }, + // react: { useSuspense: false }, + backend: { + loadPath: "/assets/locales/{{lng}}/translation.json", + }, + }); const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement + document.getElementById("root") as HTMLElement ); + root.render( - - + + ); diff --git a/src/store/reducers/cardSlice.ts b/src/store/reducers/cardSlice.ts index a244b82..f153e67 100644 --- a/src/store/reducers/cardSlice.ts +++ b/src/store/reducers/cardSlice.ts @@ -24,6 +24,7 @@ export const cardSlice = createSlice({ }, }, extraReducers: { + [fetchCards.fulfilled.type]: (state, action: PayloadAction) => { state.isLoading = false; state.error = ""; diff --git a/src/store/reducers/languageSlice.ts b/src/store/reducers/languageSlice.ts new file mode 100644 index 0000000..979822e --- /dev/null +++ b/src/store/reducers/languageSlice.ts @@ -0,0 +1,25 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import i18next from "i18next"; + +interface LanguageState { + currentLang: string; +} + +const initialState: LanguageState = { + currentLang: '', +}; + +export const languageSlice = createSlice({ + name: "language", + initialState, + reducers: { + handleChangeLanguage(state, action: PayloadAction) { + i18next.changeLanguage(action.payload); + state.currentLang = action.payload; + }, + }, +}); + + +export default languageSlice.reducer; + diff --git a/src/store/store.ts b/src/store/store.ts index f7ac598..6ab9d98 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -1,10 +1,12 @@ import { combineReducers, configureStore } from "@reduxjs/toolkit"; import cardReducer from "./reducers/cardSlice"; import sessionReducer from "./reducers/sessionSlice"; +import languageReducer from "./reducers/languageSlice"; const rootReducer = combineReducers({ cardReducer, sessionReducer, + languageReducer, }); export const setupStore = () => { diff --git a/src/styles/styles.css b/src/styles/styles.css index 695622d..71ea805 100644 --- a/src/styles/styles.css +++ b/src/styles/styles.css @@ -63,7 +63,7 @@ font-size: 64px; line-height: 100%; margin: 0; - background-image: linear-gradient(56.75deg, #D375FF 19.74%, #798FFF 82.32%); + background-image: linear-gradient(56.75deg, #d375ff 19.74%, #798fff 82.32%); background-size: 100%; background-repeat: repeat; background-color: transparent; @@ -76,7 +76,7 @@ font-weight: 400; line-height: 100%; margin: 0; - background-image: linear-gradient(56.75deg, #D375FF 19.74%, #798FFF 82.32%); + background-image: linear-gradient(56.75deg, #d375ff 19.74%, #798fff 82.32%); background-size: 100%; background-repeat: repeat; background-color: transparent; @@ -109,7 +109,7 @@ } .main-block__button { - font-family: 'GilroyWebRegular'; + font-family: "GilroyWebRegular"; font-style: normal; font-weight: 400; font-size: 16px; @@ -154,24 +154,21 @@ border: 1px solid #ffffff; } -.main-block__button:hover>div>img { +.main-block__button:hover > div > img { transition: opacity ease-out 0.2s; opacity: 1; } - .main-block__button:hover { - border: 1px solid #FFFFFF; + border: 1px solid #ffffff; transition: border ease-out 0.2s; } .link { text-decoration: none; - } @media screen and (min-width: 1600px) { - .main-block__title { font-size: 76px; } @@ -215,7 +212,6 @@ font-weight: 400; font-size: 20px; line-height: 125%; - } .main-block__icon_container { @@ -284,12 +280,27 @@ line-height: 20px; cursor: pointer; box-sizing: border-box; +} + +.button:hover { + background: #6a92e2; + transition: 0.3s ease; +} + +.button:disabled { + background-color: #404040; + transition: 0.3 ease; +} + +.button__disabled:hover { + background: #595959; + transition: 0.3 ease; } .button__disabled { background-color: #404040; - transition: background-color 0.3 ease; + transition: 0.3 ease; } .line { @@ -313,25 +324,23 @@ color: #ffffff; } - +.popup__caption:hover { + opacity: 0.8; + transition: 0.3s ease; +} @media screen and (max-width: 1279px) { .main-block__container { gap: 44px; } - } - - @media screen and (max-width: 1024px) { - .main-block__container { margin: 30px 12px 60px; gap: 40px; } - .main-block__caption { font-size: 2.8vw; font-style: normal; @@ -351,8 +360,6 @@ line-height: 140%; } - - .main-block__title { font-size: 10.6vw; font-style: normal; @@ -367,7 +374,6 @@ line-height: 100%; } - .main-block_subblock { font-weight: 400; line-height: 140%; @@ -379,16 +385,12 @@ font-weight: 400; line-height: 100%; font-size: 6.25vw; - } - .main-block__title_gardient_small { font-size: 6.25vw; } - - .main-block__subblock-container { display: block; margin: 0 0; @@ -397,11 +399,8 @@ .main-block_subblock { width: 80%; margin: 0; - font-size: 3vw + font-size: 3vw; } - - - } @media screen and (max-width: 639px) { @@ -411,7 +410,6 @@ height: 100%; padding: 32px 10px 67px; height: 100vh; - } .logo__popup { @@ -440,10 +438,8 @@ font-size: 14px; padding-top: 14px; padding-bottom: 14px; - } - .main-block_subblock { gap: 30px; width: 100%; @@ -453,7 +449,6 @@ width: 100%; margin-top: 10px; font-size: 3.6vw; - } .main-block__caption { @@ -465,13 +460,8 @@ font-style: normal; font-weight: 400; line-height: 140%; - } - - - - .main-block__button { font-style: normal; font-weight: 400; @@ -486,10 +476,6 @@ line-height: 140%; } - - - - .main-block__title_gardient { font-size: 13.3vw; font-style: normal; @@ -506,7 +492,6 @@ font-style: normal; font-weight: 400; line-height: 100%; - } .main-block__title_small { @@ -514,7 +499,6 @@ font-weight: 400; line-height: 100%; font-size: 9.25vw; - } .main-block_subblock { @@ -522,7 +506,4 @@ line-height: 140%; font-size: 4.5vw; } - - - -} \ No newline at end of file +}