diff --git a/package-lock.json b/package-lock.json index 039a56eb74f70bd05ba34720ae27ad8fe2405585..825b7e4153e9548048930504c3b46a0856f3d2bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1421,6 +1421,11 @@ "prop-types": "^15.7.2" } }, + "@rooks/use-window-size": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@rooks/use-window-size/-/use-window-size-4.5.0.tgz", + "integrity": "sha512-numJ/GJcBezv8OdA9ahNmYywAh3HTeGFKxdChSk2kPnLZhxHBODvyx5CKolJp+8FDruIGbe36TEyqE/ZOUGcJA==" + }, "@sentry/browser": { "version": "5.23.0", "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.23.0.tgz", @@ -3573,6 +3578,12 @@ "node-int64": "^0.4.0" } }, + "btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "dev": true + }, "buffer": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", @@ -4672,6 +4683,11 @@ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, "default-gateway": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", @@ -5050,6 +5066,15 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "ejs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-dldq3ZfFtgVTJMLjOe+/3sROTzALlL9E34V4/sDtUd/KlBSS0s6U1/+WPE1B4sj9CXHJpL1M6rhNJnc9Wbal9w==", + "dev": true, + "requires": { + "jake": "^10.6.1" + } + }, "electron-to-chromium": { "version": "1.3.567", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.567.tgz", @@ -6243,6 +6268,15 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "optional": true }, + "filelist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.1.tgz", + "integrity": "sha512-8zSK6Nu0DQIC08mUC46sWGXi+q3GGpKydAG36k+JDba6VRpkevvOWUW5a/PhShij4+vHT9M+ghgG7eM+a9JDUQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, "filesize": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.0.1.tgz", @@ -7651,6 +7685,26 @@ "html-escaper": "^2.0.0" } }, + "jake": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", + "integrity": "sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==", + "dev": true, + "requires": { + "async": "0.9.x", + "chalk": "^2.4.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", + "dev": true + } + } + }, "jest": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", @@ -8429,6 +8483,11 @@ "strip-bom": "^3.0.0" } }, + "load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/load-script/-/load-script-1.0.0.tgz", + "integrity": "sha1-BJGTngvuVkPuSUp+PaPSuscMbKQ=" + }, "loader-fs-cache": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz", @@ -8660,6 +8719,11 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" + }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -11308,14 +11372,6 @@ } } }, - "react-device-detect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/react-device-detect/-/react-device-detect-1.13.1.tgz", - "integrity": "sha512-XTPgAMsUVHC5lMNUGiAeO2UfAfhMfjq0CBUM67eHnc9XfO7iESh6h/cffKV8VGgrZBX+dyuqJl23bLLHoav5Ig==", - "requires": { - "ua-parser-js": "^0.7.21" - } - }, "react-dom": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", @@ -11368,6 +11424,18 @@ "warning": "^4.0.3" } }, + "react-player": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-player/-/react-player-2.7.2.tgz", + "integrity": "sha512-4MqikwtVAxjDlgYp5Kgnz6Uptj/VpYE4VKvg2hOXlgz4zxorsxjBUE8Y3GUY6vpolEMPjpYIxSr1CP4TNrD1WA==", + "requires": { + "deepmerge": "^4.0.0", + "load-script": "^1.0.0", + "memoize-one": "^5.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.0.1" + } + }, "react-router": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", @@ -12883,6 +12951,175 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, + "source-map-explorer": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/source-map-explorer/-/source-map-explorer-2.5.1.tgz", + "integrity": "sha512-sg60PIva7pOkvTqbLRGOB3Cp84I1pial0J6tUNs/bNGIz+0+WNw37oLXEDFHTDJhFh24rto8rESv4wCB7w9HVQ==", + "dev": true, + "requires": { + "btoa": "^1.2.1", + "chalk": "^4.1.0", + "convert-source-map": "^1.7.0", + "ejs": "^3.1.5", + "escape-html": "^1.0.3", + "glob": "^7.1.6", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "open": "^7.3.0", + "source-map": "^0.7.3", + "temp": "^0.9.4", + "yargs": "^16.1.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dev": true, + "requires": { + "duplexer": "^0.1.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "open": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/open/-/open-7.3.0.tgz", + "integrity": "sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", + "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + } + } + }, "source-map-resolve": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", @@ -13468,6 +13705,16 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" }, + "temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" + } + }, "terser": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", @@ -13843,11 +14090,6 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, - "ua-parser-js": { - "version": "0.7.22", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.22.tgz", - "integrity": "sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q==" - }, "unfetch": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", diff --git a/package.json b/package.json index ce86b5863423cefb4b5ce769bed4bff74a06846e..529a9a8761eacc1995254bea7984b1b356e395a5 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@react-keycloak/web": "^2.1.4", + "@rooks/use-window-size": "^4.5.0", "@sentry/react": "^5.23.0", "classnames": "^2.2.6", "date-fns": "^2.16.1", @@ -12,11 +13,11 @@ "lodash": "^4.17.20", "pullstate": "^1.20.5", "react": "^16.13.1", - "react-device-detect": "^1.13.1", "react-dom": "^16.13.1", "react-intersection-observer": "^8.31.0", "react-mde": "^11.0.0", "react-modal": "^3.12.1", + "react-player": "^2.7.2", "react-router-dom": "^5.2.0", "react-scripts": "3.4.3", "showdown": "^1.9.1", @@ -30,7 +31,8 @@ "test": "react-scripts test --env=jsdom-fourteen", "eject": "react-scripts eject", "lint": "eslint --cache 'src/**/*.{js,jsx}'", - "lint:fix": "eslint --cache --fix 'src/**/*.{js,jsx}'" + "lint:fix": "eslint --cache --fix 'src/**/*.{js,jsx}'", + "analyze": "source-map-explorer 'build/static/js/*.js'" }, "eslintConfig": { "extends": [ @@ -120,6 +122,7 @@ "eslint-plugin-react": "^7.20.6", "eslint-plugin-simple-import-sort": "^5.0.3", "eslint-plugin-testing-library": "^3.8.0", - "prettier": "^2.1.1" + "prettier": "^2.1.1", + "source-map-explorer": "^2.5.1" } } diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx index 1a1542bb44abe3c27af04fc8a569e0cdf1065a3a..da09c99fd64e2601fc3dfcb637328020bdef763d 100644 --- a/src/components/Navbar.jsx +++ b/src/components/Navbar.jsx @@ -1,15 +1,18 @@ import React, { useCallback, useState } from "react"; -import { isBrowser } from "react-device-detect"; import { NavLink } from "react-router-dom"; import { useKeycloak } from "@react-keycloak/web"; +import useWindowSize from "@rooks/use-window-size"; +import classNames from "classnames"; import Button from "components/Button"; -import { AuthStore } from "stores"; +import { AuthStore, GlobalInfoStore } from "stores"; const Navbar = () => { - const [showMenu, setShowMenu] = useState(isBrowser); + const { innerWidth } = useWindowSize(); + const [showMenu, setShowMenu] = useState(); const { keycloak } = useKeycloak(); const { isAuthenticated, user } = AuthStore.useState(); + const { connectionState } = GlobalInfoStore.useState(); const login = useCallback(() => { keycloak.login(); @@ -18,6 +21,40 @@ const Navbar = () => { keycloak.logout(); }, [keycloak]); + const connectionStateCaption = { + connected: "Jste online", + offline: "Jste offline", + connecting: "Probíhá připojování", + }[connectionState]; + + const isLg = innerWidth >= 1024; + + const indicatorClass = { + "bg-green-400": connectionState === "connected", + "bg-red-600": connectionState === "offline", + "bg-yellow-200": connectionState === "connecting", + }; + + const connectionIndicator = ( + <span + className="relative inline-flex h-4 w-4 mr-4" + title={connectionStateCaption} + > + <span + className={classNames( + "animate-ping absolute inline-flex h-full w-full rounded-full opacity-75", + indicatorClass + )} + /> + <span + className={classNames( + "inline-flex rounded-full w-4 h-4", + indicatorClass + )} + /> + </span> + ); + return ( <nav className="navbar navbar--simple"> <div className="container container--wide navbar__content navbar__content--initialized"> @@ -44,7 +81,7 @@ const Navbar = () => { <i className="ico--menu text-3xl"></i> </button> </div> - {showMenu && ( + {(showMenu || isLg) && ( <> <div className="navbar__main navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto"> <ul className="navbar-menu text-white"> @@ -60,7 +97,8 @@ const Navbar = () => { </li> </ul> </div> - <div className="navbar__actions navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto self-start flex flex-col sm:flex-row lg:flex-col sm:space-x-4 space-y-2 sm:space-y-0 lg:space-y-2 xl:flex-row xl:space-x-2 xl:space-y-0"> + <div className="navbar__actions navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto self-start flex flex-row items-center"> + {connectionIndicator} {!isAuthenticated && ( <Button className="btn--white" onClick={login}> Přihlásit se diff --git a/src/containers/GlobalStats.jsx b/src/containers/GlobalStats.jsx new file mode 100644 index 0000000000000000000000000000000000000000..012399eddfd2c0f5ca3b9594861afaa437388f92 --- /dev/null +++ b/src/containers/GlobalStats.jsx @@ -0,0 +1,45 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import { format, isToday } from "date-fns"; + +import { GlobalInfoStore, ProgramStore } from "stores"; + +const GlobalStats = () => { + const { onlineUsers, onlineMembers } = GlobalInfoStore.useState(); + const { currentId, scheduleIds, items } = ProgramStore.useState(); + + const nextProgramEntryId = scheduleIds + ? scheduleIds[currentId ? scheduleIds.indexOf(currentId) + 1 : 0] + : null; + + const nextProgramEntry = nextProgramEntryId + ? items[nextProgramEntryId] + : null; + + return ( + <div className="bg-grey-50 flex space-x-4 leading-normal px-4 py-2 text-xs md:text-sm text-grey-300"> + <div> + <strong>{onlineMembers}</strong> <span>členů online</span> + </div> + <div> + <strong>{onlineUsers}</strong> <span>online celkem</span> + </div> + {nextProgramEntry && ( + <div className="flex-grow text-right hidden sm:block"> + Následuje:{" "} + <Link to="/program" className="font-bold"> + {nextProgramEntry.title} @{" "} + {format( + nextProgramEntry.expectedStartAt, + isToday(nextProgramEntry.expectedStartAt) + ? "H:mm" + : "dd. MM. H:mm" + )} + </Link> + </div> + )} + </div> + ); +}; + +export default GlobalStats; diff --git a/src/containers/StatsCard.jsx b/src/containers/StatsCard.jsx deleted file mode 100644 index 5b74d8923c8c97cae5f8e61446645846ddee6f2d..0000000000000000000000000000000000000000 --- a/src/containers/StatsCard.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from "react"; -import classNames from "classnames"; - -import { Card, CardBody } from "components/cards"; -import { GlobalInfoStore } from "stores"; - -const StatsCard = () => { - const { connectionState, onlineUsers } = GlobalInfoStore.useState(); - - const connectionIndicator = ( - <div - className={classNames("inline-block rounded-full w-4 h-4 mr-2", { - "bg-green-400": connectionState === "connected", - "bg-red-600": connectionState === "offline", - "bg-yellow-200": connectionState === "connecting", - })} - /> - ); - - return ( - <Card> - <CardBody className="leading-normal"> - <div className="flex justify-between"> - <span>Stav vašeho připojení</span> - <div className="flex items-center"> - {connectionIndicator} - <strong> - {connectionState === "connected" && "on-line"} - {connectionState === "offline" && "off-line"} - {connectionState === "connecting" && "připojování"} - </strong> - </div> - </div> - <div className="flex justify-between"> - <span>Počet on-line účastníků</span> - <strong>{onlineUsers}</strong> - </div> - </CardBody> - </Card> - ); -}; - -export default StatsCard; diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index a623482b352436e79122c249736f20f7c2ae857a..cfe27a0a051cf6d54fa38d8a78a576f87f738b12 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -1,4 +1,5 @@ import React, { useState } from "react"; +import ReactPlayer from "react-player/lazy"; import { format } from "date-fns"; import { @@ -15,9 +16,9 @@ import ProgramEntryEditModal from "components/program/ProgramEntryEditModal"; import AddAnnouncementForm from "containers/AddAnnouncementForm"; import AddPostForm from "containers/AddPostForm"; import AnnouncementsContainer from "containers/AnnoucementsContainer"; +import GlobalStats from "containers/GlobalStats"; import PostFilters from "containers/PostFilters"; import PostsContainer from "containers/PostsContainer"; -import StatsCard from "containers/StatsCard"; import { useActionConfirm } from "hooks"; import { AuthStore, GlobalInfoStore, ProgramStore } from "stores"; @@ -153,13 +154,13 @@ const Home = () => { return ( <> <article className="container container--wide pt-8 lg:py-24 cf2021"> - <section className="cf2021__video space-y-8"> - <div className="flex items-center justify-between mb-4 lg:mb-8"> - <h1 className="head-alt-md lg:head-alt-lg mb-0"> + <section className="cf2021__video"> + <div className="flex justify-between mb-4 lg:mb-8"> + <h1 className="head-alt-md lg:head-alt-lg"> Bod č. {programEntry.number}: {programEntry.title} </h1> {displayActions && ( - <DropdownMenu right triggerSize="lg" className="pl-4"> + <DropdownMenu right triggerSize="lg" className="pl-4 pt-5"> <DropdownMenuItem onClick={() => setShowProgramEditModal(true)} icon="ico--edit-pencil" @@ -196,29 +197,26 @@ const Home = () => { )} </div> - {streamUrl && ( - <div className="iframe-container"> - <iframe - width="560" - height="315" - src={streamUrl} - frameBorder="0" - allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" - allowFullScreen="" - title="Video stream" - /> - </div> - )} - {!streamUrl && ( - <p> - Server neposlal informaci o aktuálním streamu. Vyčkejte na - aktualizaci. - </p> - )} - </section> - - <section className="cf2021__stats"> - <StatsCard /> + <div className="container-padding--zero md:container-padding--auto"> + {streamUrl && ( + <div className="iframe-container"> + <ReactPlayer + url={streamUrl} + title="Video stream" + controls={true} + playing={false} + width="100%" + /> + </div> + )} + {!streamUrl && ( + <p> + Server neposlal informaci o aktuálním streamu. Vyčkejte na + aktualizaci. + </p> + )} + <GlobalStats /> + </div> </section> <section className="cf2021__notifications"> diff --git a/src/pages/Program.jsx b/src/pages/Program.jsx index fadf116e8971aeb1f29116255c924d2ea759844c..84451e314b2db31ab7d57987bba5ec7153bd2c6e 100644 --- a/src/pages/Program.jsx +++ b/src/pages/Program.jsx @@ -60,7 +60,8 @@ const Schedule = () => { </div> <div className="flex-grow w-full"> <h2 className="head-heavy-xs md:head-heavy-base mb-1"> - <Link to="/">{entry.title}</Link> + {isCurrent && <Link to="/">{entry.title}</Link>} + {!isCurrent && entry.title} </h2> <div className="flex space-x-2"> <strong>Navrhovatel:</strong> diff --git a/src/stores.js b/src/stores.js index a82900a7fedf27ac448a1e23dc70df70f8806ab6..9f2111855f3744271bad746c610ce0a04380d8a6 100644 --- a/src/stores.js +++ b/src/stores.js @@ -4,6 +4,7 @@ import { Store } from "pullstate"; /** @type {CF2021.GlobalInfoStorePayload} */ const globalInfoStoreInitial = { connectionState: "connecting", + onlineMembers: 0, onlineUsers: 0, streamUrl: null, };