From e566424738067c50015cf1af6e250e2612bdd133 Mon Sep 17 00:00:00 2001 From: cv <34649812+cpvalente@users.noreply.github.com> Date: Tue, 20 Apr 2021 12:09:06 +0200 Subject: [PATCH] feat: schedule view --- client/src/app/hooks/useInterval.js | 19 +++++ client/src/app/hooks/useTimeout.js | 38 +++++++++ .../features/viewers/backstage/Paginator.jsx | 82 +++++++++++++++++++ .../viewers/backstage/Paginator.module.css | 25 ++++++ .../viewers/backstage/StageManager.jsx | 29 +------ .../viewers/backstage/StageManager.module.css | 34 ++------ 6 files changed, 176 insertions(+), 51 deletions(-) create mode 100644 client/src/app/hooks/useInterval.js create mode 100644 client/src/app/hooks/useTimeout.js create mode 100644 client/src/features/viewers/backstage/Paginator.jsx create mode 100644 client/src/features/viewers/backstage/Paginator.module.css diff --git a/client/src/app/hooks/useInterval.js b/client/src/app/hooks/useInterval.js new file mode 100644 index 000000000..6807be455 --- /dev/null +++ b/client/src/app/hooks/useInterval.js @@ -0,0 +1,19 @@ +import { useEffect, useRef } from "react"; + +export const useInterval = (callback, delay) => { + const savedCallback = useRef(); + + useEffect(() => { + savedCallback.current = callback; + }, [callback]); + + useEffect(() => { + function tick() { + savedCallback.current(); + } + if (delay !== null) { + let id = setInterval(tick, delay); + return () => clearInterval(id); + } + }, [delay]); +}; diff --git a/client/src/app/hooks/useTimeout.js b/client/src/app/hooks/useTimeout.js new file mode 100644 index 000000000..ae5681d5f --- /dev/null +++ b/client/src/app/hooks/useTimeout.js @@ -0,0 +1,38 @@ +import { useEffect } from 'react'; + +// +// useTimeout React Hook +// +// React hook for delaying calls with time +export const useTimeout = ( + callback, // function to call. No args passed. + timeout = 0, // delay, ms (default: immediately put into JS Event Queue) + { + // manage re-render behavior. + // by default, a re-render in your component will re-define the callback, + // which will cause this timeout to cancel itself. + // to avoid cancelling on re-renders (but still cancel on unmounts), + // set `persistRenders: true,`. + persistRenders = false, + } = {}, + // These dependencies are injected for testing purposes. + // (pure functions - where all dependencies are arguments - is often easier to test) + _setTimeout = setTimeout, + _clearTimeout = clearTimeout, + _useEffect = useEffect +) => { + let timeoutId; + const cancel = () => timeoutId && _clearTimeout(timeoutId); + + _useEffect( + () => { + timeoutId = _setTimeout(callback, timeout); + return cancel; + }, + persistRenders + ? [_setTimeout, _clearTimeout] + : [callback, timeout, _setTimeout, _clearTimeout] + ); + + return cancel; +}; diff --git a/client/src/features/viewers/backstage/Paginator.jsx b/client/src/features/viewers/backstage/Paginator.jsx new file mode 100644 index 000000000..ccf7ab0ad --- /dev/null +++ b/client/src/features/viewers/backstage/Paginator.jsx @@ -0,0 +1,82 @@ +import TodayItem from './TodayItem'; +import style from './Paginator.module.css'; +import { useEffect, useState } from 'react'; +import { useTimeout } from '../../../app/hooks/useTimeout'; +import { useInterval } from '../../../app/hooks/useInterval'; + +export default function Paginator(props) { + const { events, selectedId } = props; + const LIMIT_PER_PAGE = 3; + const SCROLL_TIME = 5000; + const SCROLL_PAST = false; + const [numEvents, setNumEvents] = useState(0); + const [page, setPage] = useState([]); + const [pages, setPages] = useState(0); + const [selPage, setSelPage] = useState(0); + + // Keep track of order + // 0 - events before + // 1 - running event + // 2 - future event + let selectedYet = 0; + + useEffect(() => { + if (events == null) return; + + console.log('debug events', events); + + // how many events in list + let n = events.length; + setNumEvents(n); + console.log('debug ne', n); + + // how many paginated views + let p = Math.ceil(n / LIMIT_PER_PAGE); + setPages(p); + console.log('debug np', p); + + // divide events in parts of LIMIT_PER_PAGE + const eventStart = LIMIT_PER_PAGE * selPage; + const eventEnd = LIMIT_PER_PAGE * (selPage + 1); + let e = events.slice(eventStart, eventEnd); + setPage(e); + console.log('debug show', e); + + // if array is completely in past, show depending on SCROLL_PAST + }, [events, selPage]); + + // every SCROLL_TIME go to the next array + useInterval(() => { + if (numEvents > LIMIT_PER_PAGE) { + const next = (selPage + 1) % pages; + setSelPage(next); + } + }, SCROLL_TIME); + + return ( + <> +
+ {[...Array(pages)].map((p, i) => ( +
+ ))} +
+ +
+ {page.map((e) => { + if (e.id === selectedId) selectedYet = 1; + else if (selectedYet === 1) selectedYet = 2; + return ( + + ); + })} +
+ + ); +} diff --git a/client/src/features/viewers/backstage/Paginator.module.css b/client/src/features/viewers/backstage/Paginator.module.css new file mode 100644 index 000000000..3e2f9380d --- /dev/null +++ b/client/src/features/viewers/backstage/Paginator.module.css @@ -0,0 +1,25 @@ +.entries { + display: flex; + flex-direction: column; + gap: 1.5vh; + padding-top: 2vh; +} + +.navItem, +.navItemSelected { + background-color: rgba(255, 255, 255, 0.39); + width: 0.7vw; + height: 0.7vw; + border-radius: 0.35vw; +} + +.navItemSelected { + background-color: rgba(255, 255, 255, 0.79); +} + +.nav { + align-self: center; + justify-content: flex-end; + display: flex; + gap: 1vw; +} diff --git a/client/src/features/viewers/backstage/StageManager.jsx b/client/src/features/viewers/backstage/StageManager.jsx index ce36a83ea..996df1577 100644 --- a/client/src/features/viewers/backstage/StageManager.jsx +++ b/client/src/features/viewers/backstage/StageManager.jsx @@ -1,7 +1,7 @@ import QRCode from 'react-qr-code'; import { formatDisplay } from '../../../common/dateConfig'; import style from './StageManager.module.css'; -import TodayItem from './TodayItem'; +import Paginator from './Paginator'; export default function StageManager(props) { const { pres, publ, lower, title, time, events, selectedId, general } = props; @@ -13,12 +13,6 @@ export default function StageManager(props) { ? formatDisplay(time.currentSeconds, true) : ''; - // Keep track of order - // 0 - events before - // 1 - running event - // 2 - future event - let selectedYet = 0; - return (
{general.title}
@@ -36,25 +30,8 @@ export default function StageManager(props) {
Today
-
- {events.map((e) => { - if (e.id === selectedId) selectedYet = 1; - else if (selectedYet === 1) selectedYet = 2; - return ( - - ); - })} -
- -
-
-
-
+
+
diff --git a/client/src/features/viewers/backstage/StageManager.module.css b/client/src/features/viewers/backstage/StageManager.module.css index c12b1d2ff..2e6a790f3 100644 --- a/client/src/features/viewers/backstage/StageManager.module.css +++ b/client/src/features/viewers/backstage/StageManager.module.css @@ -134,6 +134,15 @@ font-size: 1.5vw; } +.nowTitle:after, +.nowSubtitle:after, +.nowPresenter:after, +.nextTitle:after, +.nextSubtitle:after, +.nextPresenter:after { + content: '\200b'; +} + /* =================== SCHEDULE ===================*/ .todayContainer { @@ -144,23 +153,6 @@ height: 95%; } -.entries { - display: flex; - flex-direction: column; - gap: 1.5vh; -} - -.navItem, -.navItemSelected { - background-color: rgba(255, 255, 255, 0.39); - width: 0.7vw; - height: 0.7vw; - border-radius: 0.35vw; -} - -.navItemSelected { - background-color: rgba(255, 255, 255, 0.79); -} .entryStart, .entryEnd { @@ -249,11 +241,3 @@ letter-spacing: 0.25vw; color: #ddd; } - -.nav { - align-self: center; - justify-self: flex-end; - display: flex; - gap: 1vw; - margin-bottom: auto; -}