From 8e95b06b8cef5edaa233891954aa0e09e7c8d3e6 Mon Sep 17 00:00:00 2001 From: cv <34649812+cpvalente@users.noreply.github.com> Date: Sat, 17 Apr 2021 23:54:00 +0200 Subject: [PATCH] feat: presenter view - connected to websockets - some styling changes - reusable components - some small bugfixes --- .../common/components/countdown/Countdown.jsx | 11 +- .../components/countdown/Countdown.module.css | 7 +- .../myProgressBar/MyProgressBar.jsx | 8 +- .../myProgressBar/MyProgressBar.module.css | 18 +-- client/src/common/dateConfig.js | 6 +- .../src/features/control/MessageControl.jsx | 6 +- .../src/features/control/PlaybackControl.jsx | 1 + .../viewers/presenter/PresenterView.jsx | 146 ++++++++++++++---- .../presenter/PresenterView.module.css | 43 ++---- server/classes/EventTimer.js | 36 ++++- server/classes/Timer.js | 5 + 11 files changed, 197 insertions(+), 90 deletions(-) diff --git a/client/src/common/components/countdown/Countdown.jsx b/client/src/common/components/countdown/Countdown.jsx index a4b77b6e2..38e5cb451 100644 --- a/client/src/common/components/countdown/Countdown.jsx +++ b/client/src/common/components/countdown/Countdown.jsx @@ -1,16 +1,18 @@ import { useEffect, useState } from 'react'; import styles from './Countdown.module.css'; -function formatDisplay(seconds) { +function formatDisplay(seconds, hideZero) { const format = (val) => `0${Math.floor(val)}`.slice(-2); const hours = seconds / 3600; const minutes = (seconds % 3600) / 60; - return [hours, minutes, seconds % 60].map(format).join(':'); + if (hideZero && hours < 1) + return [minutes, seconds % 60].map(format).join(':'); + else return [hours, minutes, seconds % 60].map(format).join(':'); } export default function Countdown(props) { - const { time, small } = props; + const { time, small, hideZeroHours } = props; const [clock, setClock] = useState(time); let display = '-- : -- : --'; @@ -19,7 +21,8 @@ export default function Countdown(props) { }, [time]); // prepare display string - if (clock != null && !isNaN(clock)) display = formatDisplay(clock); + if (clock != null && !isNaN(clock)) + display = formatDisplay(clock, hideZeroHours); return (
diff --git a/client/src/common/components/countdown/Countdown.module.css b/client/src/common/components/countdown/Countdown.module.css index 2864f685f..d95ba95de 100644 --- a/client/src/common/components/countdown/Countdown.module.css +++ b/client/src/common/components/countdown/Countdown.module.css @@ -9,11 +9,10 @@ /* Viewers */ .countdownClock { - font-size: 18vw; - margin-top: 6vh; - margin-bottom: 5vh; + font-size: 21vw; + line-height: 21vw; text-align: center; - letter-spacing: 0.125em; + letter-spacing: 1vw; } /* Editor */ diff --git a/client/src/common/components/myProgressBar/MyProgressBar.jsx b/client/src/common/components/myProgressBar/MyProgressBar.jsx index 7257e9cfb..9f0115b0d 100644 --- a/client/src/common/components/myProgressBar/MyProgressBar.jsx +++ b/client/src/common/components/myProgressBar/MyProgressBar.jsx @@ -1,15 +1,15 @@ import { clamp } from '../../../app/utils'; import styles from './MyProgressBar.module.css'; -export default function MyProgressBar({ normalisedComplete }) { - const percentComplete = clamp(100 - normalisedComplete * 100, 0, 100); - const completeWidth = `${percentComplete}%`; +export default function MyProgressBar(props) { + const { now, complete } = props; + const percentComplete = clamp((now * 100) / complete, 0, 100); return (
); diff --git a/client/src/common/components/myProgressBar/MyProgressBar.module.css b/client/src/common/components/myProgressBar/MyProgressBar.module.css index f73c6f2e1..be451f8af 100644 --- a/client/src/common/components/myProgressBar/MyProgressBar.module.css +++ b/client/src/common/components/myProgressBar/MyProgressBar.module.css @@ -1,21 +1,13 @@ .progress { - padding: 1vh; - width: 90%; - margin: 0 auto; - background: rgba(0, 0, 0, 0.25); - border-radius: 6px; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 1px rgba(255, 255, 255, 0.08); + height: 2vh; + background-color: rgba(255, 255, 255, 0.13); + border-radius: 4px; } .progressBar { + width: 30%; height: 2vh; - background: linear-gradient( - 90deg, - rgb(145, 255, 191, 1) 0%, - rgba(145, 255, 191, 0.8) 20%, - rgba(145, 255, 191, 0.5) 50%, - rgba(145, 255, 191, 0.2) 100% - ); + background-color: rgba(255, 255, 255, 0.79); border-radius: 4px; transition: 0.4s linear; transition-property: width, background-color; diff --git a/client/src/common/dateConfig.js b/client/src/common/dateConfig.js index 4978f47b3..0af90de25 100644 --- a/client/src/common/dateConfig.js +++ b/client/src/common/dateConfig.js @@ -10,11 +10,13 @@ export const stringFromMillis = ( ) => { if (ms === null) return ifNull; const showWith0 = (value) => (value < 10 ? `0${value}` : value); - const hours = showWith0(Math.floor((ms / (1000 * 60 * 60)) % 60)); + const hours = showWith0(Math.floor(((ms / (1000 * 60 * 60)) % 60) % 24)); const minutes = showWith0(Math.floor((ms / (1000 * 60)) % 60)); const seconds = showWith0(Math.floor((ms / 1000) % 60)); return showSeconds - ? `${parseInt(hours) ? `${hours}${delim}` : ''}${minutes}${delim}${seconds}` + ? `${ + parseInt(hours) ? `${hours}${delim}` : `00${delim}` + }${minutes}${delim}${seconds}` : `${parseInt(hours) ? `${hours}` : '00'}${delim}${minutes}`; }; diff --git a/client/src/features/control/MessageControl.jsx b/client/src/features/control/MessageControl.jsx index 1a3311a77..93eb169e4 100644 --- a/client/src/features/control/MessageControl.jsx +++ b/client/src/features/control/MessageControl.jsx @@ -85,7 +85,7 @@ export default function MessageControl() {
- messageControl('pres-text', event.target.value) + messageControl('pres-text', event) } placeholder='only the presenter screens see this' className={style.inline} @@ -106,7 +106,7 @@ export default function MessageControl() {
- messageControl('publ-text', event.target.value) + messageControl('publ-text', event) } placeholder='all screens will render this' className={style.inline} @@ -127,7 +127,7 @@ export default function MessageControl() {
- messageControl('lower-text', event.target.value) + messageControl('lower-text', event) } placeholder='visible in lower third screen' className={style.inline} diff --git a/client/src/features/control/PlaybackControl.jsx b/client/src/features/control/PlaybackControl.jsx index de0c53690..f5a956820 100644 --- a/client/src/features/control/PlaybackControl.jsx +++ b/client/src/features/control/PlaybackControl.jsx @@ -20,6 +20,7 @@ export default function PlaybackControl() { const socket = useSocket(); const [playback, setPlayback] = useState(null); const [timer, setTimer] = useState({ + clock: null, currentSeconds: null, startedAt: null, expectedFinish: null, diff --git a/client/src/features/viewers/presenter/PresenterView.jsx b/client/src/features/viewers/presenter/PresenterView.jsx index 9c3203298..fc8f1649b 100644 --- a/client/src/features/viewers/presenter/PresenterView.jsx +++ b/client/src/features/viewers/presenter/PresenterView.jsx @@ -1,39 +1,131 @@ +import { useEffect, useState } from 'react'; +import { useSocket } from '../../../app/context/socketContext'; +import Countdown from '../../../common/components/countdown/Countdown'; +import MyProgressBar from '../../../common/components/myProgressBar/MyProgressBar'; +import { stringFromMillis } from '../../../common/dateConfig'; import style from './PresenterView.module.css'; export default function PresenterView() { + const socket = useSocket(); + const [pres, setPres] = useState({ + text: '', + visible: false, + }); + const [timer, setTimer] = useState({ + clock: null, + currentSeconds: null, + durationSeconds: null, + startedAt: null, + expectedFinish: null, + }); + const [titles, setTitles] = useState({ + titleNow: '', + subtitleNow: '', + presenterNow: '', + titleNext: '', + subtitleNext: '', + presenterNext: '', + }); + // Ask for update on load + useEffect(() => { + if (socket == null) return; + + // Handle presenter messages + socket.on('messages-presenter', (data) => { + setPres({ ...data }); + }); + + // Handle timer + socket.on('timer', (data) => { + setTimer({ ...data }); + }); + + // Handle timer + socket.on('titles', (data) => { + setTitles({ ...data }); + }); + + // Ask for up to data + socket.emit('get-presenter'); + + // Ask for up titles + socket.emit('get-titles'); + + // Clear listeners + return () => { + socket.off('messages-presenter'); + socket.off('timer'); + socket.off('titles'); + }; + }, [socket]); + + // is there a next field? + let showNext = true; + if (!titles.titleNext && !titles.subtitleNext && !titles.presenterNext) + showNext = false; + + // should show message overlay + let showOverlay = pres.visible && pres.text !== ''; + + // is timer finished + let finished = timer.currentSeconds <= 0; + + // get clock + let clock = stringFromMillis(timer.clock); + return ( -
-
-
Now
-
Are hamburguers real
-
A day in the life....
-
Carlos Valente
-
- -
-
Up Next
-
Up to midnight
-
-
Veronica
-
- - {/*
-
Remember to smile
-
*/} - -
-
01:03
- {/*
TIME UP
*/} -
- -
-
+
+
+
{pres.text}
Time Now
-
11:00:23
+
{clock}
+ +
+ {finished ? ( +
TIME UP
+ ) : ( +
+ +
+ )} +
+ + {!finished && ( +
+ +
+ )} + +
+
Now
+
{titles.titleNow}
+
{titles.subtitleNow}
+
{titles.presenterNow}
+
+ + {showNext && ( +
+
Next
+
{titles.titleNext}
+
{titles.subtitleNext}
+
{titles.presenterNext}
+
+ )}
); } diff --git a/client/src/features/viewers/presenter/PresenterView.module.css b/client/src/features/viewers/presenter/PresenterView.module.css index 8407b51a7..ad5ff7517 100644 --- a/client/src/features/viewers/presenter/PresenterView.module.css +++ b/client/src/features/viewers/presenter/PresenterView.module.css @@ -1,5 +1,11 @@ .container__gray, .container__grayFinished { + margin: 0; + padding: 0; + box-sizing: border-box; /* reset */ + overflow: hidden; + width: 100%; /* restrict the page width to viewport */ + background: radial-gradient(circle, #202020 0%, #121212 80%); height: 100vh; color: #fffd; @@ -12,7 +18,9 @@ ' prog prog prog prog prog' ' now now .... next next'; gap: 1vw; + padding: 1vw; } + .container__graySimple, .container__grayFinishedSimple { background: radial-gradient(circle, #202020 0%, #121212 80%); @@ -76,40 +84,17 @@ } .finished { - font-size: 23vw; + font-size: 18vw; + line-height: 18vw; font-weight: 600; color: #ff5353; + padding: 0; } -.countdown, -.countdownBig { - letter-spacing: 4vw; -} - -.countdown { - font-size: 23vw; - line-height: 23vw; -} - -.countdownBig { - font-size: 30vw; - line-height: 30vw; -} - -.progress { +.progressContainer { grid-area: prog; width: 80%; margin: 0 auto; - height: 2vh; - background-color: rgba(255, 255, 255, 0.13); - border-radius: 4px; -} - -.progressed { - width: 30%; - height: 2vh; - background-color: rgba(255, 255, 255, 0.79); - border-radius: 4px; } .container__grayFinished { @@ -130,13 +115,17 @@ background-color: rgba(0, 0, 0, 0.85); z-index: 2; opacity: 0; + transition: 0.5s; } .messageOverlayActive { opacity: 1; + transition: 0.5s; } .message { + width: inherit; + padding: 2vw; position: absolute; top: 50%; left: 50%; diff --git a/server/classes/EventTimer.js b/server/classes/EventTimer.js index e4703997b..abd17911b 100644 --- a/server/classes/EventTimer.js +++ b/server/classes/EventTimer.js @@ -21,15 +21,20 @@ class EventTimer extends Timer { text: '', visible: false, }; + titles = { - title: '', - subtitle: '', - presenter: '', + titleNow: null, + subtitleNow: null, + presenterNow: null, + titleNext: null, + subtitleNext: null, + presenterNext: null, }; selectedEvent = null; selectedEventId = null; numEvents = null; + _eventlist = null; constructor() { super(); @@ -77,9 +82,28 @@ class EventTimer extends Timer { this.selectedEventId = e.id; // event titles - this.titles.title = e.title; - this.titles.subtitle = e.subtitle; - this.titles.presenter = e.presenter; + this.titles.titleNow = e.title; + this.titles.subtitleNow = e.subtitle; + this.titles.presenterNow = e.presenter; + + // assume tere is no next event + this.titles.titleNext = null; + this.titles.subtitleNext = null; + this.titles.presenterNext = null; + + // look for event after + if (eventIndex < this.numEvents - 1) { + for (let i = eventIndex + 1 ; i < this.numEvents; i++) { + // check that is the right type + console.log(this._eventList[i]); + if (this._eventList[i].type === 'event') { + this.titles.titleNext = this._eventList[i].title; + this.titles.subtitleNext = this._eventList[i].subtitle; + this.titles.presenterNext = this._eventList[i].presenter; + break; + } + } + } } print() { diff --git a/server/classes/Timer.js b/server/classes/Timer.js index 7b57f849d..f436ed561 100644 --- a/server/classes/Timer.js +++ b/server/classes/Timer.js @@ -5,6 +5,7 @@ */ class Timer { + clock = null; duration = null; current = null; _finishAt = null; @@ -21,6 +22,7 @@ class Timer { setupWithSeconds(seconds, autoStart = false) { // aux const now = this._getCurrentTime(); + this.clock = now; // populate targets this.duration = seconds * 1000; @@ -43,6 +45,7 @@ class Timer { update() { // get current time const now = this._getCurrentTime(); + this.clock = now; // check playstate switch (this.state) { @@ -101,7 +104,9 @@ class Timer { this.update(); return { + clock: this.clock, currentSeconds: Timer.toSeconds(this.current), + durationSeconds: Timer.toSeconds(this.duration), expectedFinish: this._getExpectedFinish(), startedAt: this._startedAt, };