diff --git a/client/src/app/api/playbackApi.js b/client/src/app/api/playbackApi.js index a3a6ac2db..09edcc516 100644 --- a/client/src/app/api/playbackApi.js +++ b/client/src/app/api/playbackApi.js @@ -34,3 +34,13 @@ export const getNext = async () => { const res = axios.get(playbackURL + '/next'); return res; }; + +export const getUnload = async () => { + const res = axios.get(playbackURL + '/unload'); + return res; +}; + +export const getReload = async () => { + const res = axios.get(playbackURL + '/reload'); + return res; +}; diff --git a/client/src/common/components/buttons/AddIconBtn.jsx b/client/src/common/components/buttons/AddIconBtn.jsx index e8983d3a0..460f785a6 100644 --- a/client/src/common/components/buttons/AddIconBtn.jsx +++ b/client/src/common/components/buttons/AddIconBtn.jsx @@ -9,6 +9,7 @@ export default function AddIconBtn(props) { colorScheme='blue' onClick={props.clickHandler} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/BlockIconBtn.jsx b/client/src/common/components/buttons/BlockIconBtn.jsx index 3852aff0b..9ac6118b7 100644 --- a/client/src/common/components/buttons/BlockIconBtn.jsx +++ b/client/src/common/components/buttons/BlockIconBtn.jsx @@ -9,6 +9,7 @@ export default function BlockIconBtn(props) { colorScheme='purple' onClick={props.clickHandler} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/CollapseIconBtn.jsx b/client/src/common/components/buttons/CollapseIconBtn.jsx index 13da7b490..b44f56412 100644 --- a/client/src/common/components/buttons/CollapseIconBtn.jsx +++ b/client/src/common/components/buttons/CollapseIconBtn.jsx @@ -10,6 +10,7 @@ export default function CollapseIconBtn(props) { variant={props.active ? 'solid' : 'outline'} onClick={props.clickHandler} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/DelayIconBtn.jsx b/client/src/common/components/buttons/DelayIconBtn.jsx index 9f0121736..f63ff3b83 100644 --- a/client/src/common/components/buttons/DelayIconBtn.jsx +++ b/client/src/common/components/buttons/DelayIconBtn.jsx @@ -9,6 +9,7 @@ export default function DelayIconBtn(props) { colorScheme='yellow' onClick={props.clickHandler} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/DeleteIconBtn.jsx b/client/src/common/components/buttons/DeleteIconBtn.jsx index 3e8a0fc7d..94fb543c9 100644 --- a/client/src/common/components/buttons/DeleteIconBtn.jsx +++ b/client/src/common/components/buttons/DeleteIconBtn.jsx @@ -9,6 +9,7 @@ export default function DeleteIconBtn(props) { colorScheme='red' onClick={props.clickHandler} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/NextIconBtn.jsx b/client/src/common/components/buttons/NextIconBtn.jsx index 35ac1a361..c12cacfa7 100644 --- a/client/src/common/components/buttons/NextIconBtn.jsx +++ b/client/src/common/components/buttons/NextIconBtn.jsx @@ -6,11 +6,12 @@ export default function NextIconBtn(props) { } colorScheme='whiteAlpha' - backgroundColor='#ffffff05' + backgroundColor='#ffffff11' variant='outline' onClick={props.clickHandler} width={90} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/PauseIconBtn.jsx b/client/src/common/components/buttons/PauseIconBtn.jsx index d619af46c..0f841e43b 100644 --- a/client/src/common/components/buttons/PauseIconBtn.jsx +++ b/client/src/common/components/buttons/PauseIconBtn.jsx @@ -10,6 +10,7 @@ export default function PauseIconBtn(props) { onClick={props.clickHandler} width={120} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/PrevIconBtn.jsx b/client/src/common/components/buttons/PrevIconBtn.jsx index 59bdc955e..f7aff2a33 100644 --- a/client/src/common/components/buttons/PrevIconBtn.jsx +++ b/client/src/common/components/buttons/PrevIconBtn.jsx @@ -6,11 +6,12 @@ export default function PrevIconBtn(props) { } colorScheme='whiteAlpha' - backgroundColor='#ffffff05' + backgroundColor='#ffffff11' variant='outline' onClick={props.clickHandler} width={90} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/ReloadIconBtn.jsx b/client/src/common/components/buttons/ReloadIconBtn.jsx index 52106f739..c590af16d 100644 --- a/client/src/common/components/buttons/ReloadIconBtn.jsx +++ b/client/src/common/components/buttons/ReloadIconBtn.jsx @@ -11,6 +11,7 @@ export default function ReloadIconButton(props) { onClick={props.clickHandler} width={90} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/RollIconBtn.jsx b/client/src/common/components/buttons/RollIconBtn.jsx index 1d7376ea0..122611fe7 100644 --- a/client/src/common/components/buttons/RollIconBtn.jsx +++ b/client/src/common/components/buttons/RollIconBtn.jsx @@ -9,8 +9,9 @@ export default function RollIconBtn(props) { variant={props.active ? 'solid' : 'outline'} onClick={props.clickHandler} width={120} - disabled _focus={{ boxShadow: 'none' }} + {...props} + disabled /> ); } diff --git a/client/src/common/components/buttons/SettingsIconBtn.jsx b/client/src/common/components/buttons/SettingsIconBtn.jsx index f7fcf63b3..77aeb4801 100644 --- a/client/src/common/components/buttons/SettingsIconBtn.jsx +++ b/client/src/common/components/buttons/SettingsIconBtn.jsx @@ -10,6 +10,7 @@ export default function SettingsIconBtn(props) { variant='outline' onClick={props.clickHandler} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/StartIconBtn.jsx b/client/src/common/components/buttons/StartIconBtn.jsx index d015135ef..785b283f0 100644 --- a/client/src/common/components/buttons/StartIconBtn.jsx +++ b/client/src/common/components/buttons/StartIconBtn.jsx @@ -10,6 +10,7 @@ export default function StartIconBtn(props) { onClick={props.clickHandler} width={120} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/UnloadIconBtn.jsx b/client/src/common/components/buttons/UnloadIconBtn.jsx index eb09e3c6c..30560c051 100644 --- a/client/src/common/components/buttons/UnloadIconBtn.jsx +++ b/client/src/common/components/buttons/UnloadIconBtn.jsx @@ -11,6 +11,7 @@ export default function UnloadIconBtn(props) { onClick={props.clickHandler} width={90} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/common/components/buttons/VisibleIconBtn.jsx b/client/src/common/components/buttons/VisibleIconBtn.jsx index ba7c8001e..775bc2e6b 100644 --- a/client/src/common/components/buttons/VisibleIconBtn.jsx +++ b/client/src/common/components/buttons/VisibleIconBtn.jsx @@ -10,6 +10,7 @@ export default function VisibleIconBtn(props) { variant={props.active ? 'solid' : 'outline'} onClick={props.clickHandler} _focus={{ boxShadow: 'none' }} + {...props} /> ); } diff --git a/client/src/features/control/PlaybackControl.jsx b/client/src/features/control/PlaybackControl.jsx index 9da86ca5c..19cf24bb4 100644 --- a/client/src/features/control/PlaybackControl.jsx +++ b/client/src/features/control/PlaybackControl.jsx @@ -20,6 +20,7 @@ export default function PlaybackControl() { startedAt: null, expectedFinish: null, }); + const [selectedId, setSelectedId] = useState(null); const resetTimer = () => { setTimer({ @@ -35,6 +36,7 @@ export default function PlaybackControl() { socket.emit('get-timer'); socket.emit('get-playstate'); + socket.emit('get-selected-id'); // Handle playstate socket.on('playstate', (data) => { @@ -46,10 +48,16 @@ export default function PlaybackControl() { setTimer({ ...data }); }); + // Handle selected event + socket.on('selected-id', (data) => { + setSelectedId(data); + }); + // Clear listener return () => { socket.off('playstate'); socket.off('timer'); + socket.off('selected-id'); }; }, [socket]); @@ -72,6 +80,14 @@ export default function PlaybackControl() { socket.emit('set-playstate', 'next'); resetTimer(); break; + case 'unload': + socket.emit('set-playstate', 'unload'); + resetTimer(); + break; + case 'reload': + socket.emit('set-playstate', 'reload'); + resetTimer(); + break; default: break; } @@ -100,10 +116,12 @@ export default function PlaybackControl() { playbackControl('start')} + disabled={!selectedId} /> playbackControl('pause')} + disabled={!selectedId} /> playbackControl('previous')} /> playbackControl('next')} /> - playbackControl('unload')} /> - playbackControl('reload')} /> + playbackControl('unload')} + disabled={!selectedId} + /> + playbackControl('reload')} + disabled={!selectedId} + /> ); diff --git a/client/src/features/viewers/ViewWrapper.jsx b/client/src/features/viewers/ViewWrapper.jsx index f8b1c0d10..873642602 100644 --- a/client/src/features/viewers/ViewWrapper.jsx +++ b/client/src/features/viewers/ViewWrapper.jsx @@ -48,7 +48,7 @@ const withSocket = (Component) => { subtitleNext: '', presenterNext: '', }); - const [selectedId, setSelectedId] = useState({}); + const [selectedId, setSelectedId] = useState(null); const [general, setGeneral] = useState({ title: '', url: '', diff --git a/server/classes/EventTimer.js b/server/classes/EventTimer.js index 8815783e5..b5fb6b4c5 100644 --- a/server/classes/EventTimer.js +++ b/server/classes/EventTimer.js @@ -78,6 +78,24 @@ class EventTimer extends Timer { this.io.emit(address, payload); } + update() { + // if there is nothing selected, no nothing + if (!this.selectedEventId) return; + super.update(); + } + + start() { + // if there is nothing selected, no nothing + if (!this.selectedEventId) return; + super.start() + } + + pause() { + // if there is nothing selected, no nothing + if (!this.selectedEventId) return; + super.pause() + } + _setterManager(action, payload) { switch (action) { /*******************************************/ @@ -89,9 +107,10 @@ class EventTimer extends Timer { else if (payload === 'stop') this.stop(); else if (payload === 'previous') this.previous(); else if (payload === 'next') this.next(); + else if (payload === 'reload') this.reload(); + else if (payload === 'unload') this.unload(); // Not yet implemented // else if (payload === 'roll') this.roll(); - // else if (payload === 'release') this.roll(); this.broadcastThis('playstate', this.playState); this.broadcastThis('selected-id', this.selectedEventId); this.broadcastThis('titles', this.titles); @@ -155,7 +174,7 @@ class EventTimer extends Timer { console.log( `EventTimer: Client disconnected, total now: ${this._numClients}` ); - }) + }); /***************************************/ /*** TIMER STATE GETTERS / SETTERS ***/ @@ -333,6 +352,20 @@ class EventTimer extends Timer { } } + _resetSelection() { + this.titles = { + titleNow: null, + subtitleNow: null, + presenterNow: null, + titleNext: null, + subtitleNext: null, + presenterNext: null, + }; + + this.selectedEvent = null; + this.selectedEventId = null; + } + print() { return ` Timer @@ -438,6 +471,14 @@ class EventTimer extends Timer { } previous() { + // check that we have events to run + if (this.numEvents < 1) return; + + // if there is no event running, go to first + if (!this.selectedEvent) { + this.goto(0); + return; + } const gotoEvent = this.selectedEvent > 0 ? this.selectedEvent - 1 : 0; if (gotoEvent === this.selectedEvent) return; @@ -445,6 +486,15 @@ class EventTimer extends Timer { } next() { + // check that we have events to run + if (this.numEvents < 1) return; + + // if there is no event running, go to first + if (!this.selectedEvent) { + this.goto(0); + return; + } + const gotoEvent = this.selectedEvent < this.numEvents - 1 ? this.selectedEvent + 1 @@ -453,6 +503,25 @@ class EventTimer extends Timer { if (gotoEvent === this.selectedEvent) return; this.goto(gotoEvent); } + + unload() { + // reset duration + this.duration = null; + + // reset timers + this._resetTimers(); + + // reset playstate + this.state = 'stop'; + + // reset selected + this._resetSelection(); + } + + reload() { + this._resetTimers(); + this.state = 'pause'; + } } module.exports = EventTimer; diff --git a/server/classes/Timer.js b/server/classes/Timer.js index f436ed561..f1740cf9b 100644 --- a/server/classes/Timer.js +++ b/server/classes/Timer.js @@ -91,12 +91,12 @@ class Timer { } _resetTimers() { + this.current = this.duration; this._finishAt = null; this._startedAt = null; this._pausedAt = null; this._pausedInterval = null; this._pausedTotal = null; - this.state = 'stop'; } // getObject @@ -127,7 +127,7 @@ class Timer { start() { // do we need to change if (this.state === 'start') return; - else if (this.state === 'stop') { + else if (this.startedAt == null) { const now = this._getCurrentTime(); this._startedAt = now; this._finishAt = now + this.duration; @@ -157,14 +157,7 @@ class Timer { if (this.state === 'stop') return; // clear all timers - this.current = duration; - this._finishAt = null; - this._startedAt = null; - this._pausedAt = null; - this._pausedInterval = null; - this._pausedTotal = null; - - // change state + this._resetTimers(); this.state = 'stop'; } } diff --git a/server/controllers/playbackController.js b/server/controllers/playbackController.js index 32ef50ee3..4dcc45cb3 100644 --- a/server/controllers/playbackController.js +++ b/server/controllers/playbackController.js @@ -45,3 +45,20 @@ exports.pbNext = async (req, res) => { global.timer.next(); res.sendStatus(200); }; + +// Create controller for GET request to '/playback/unload' +// Unloads any events +exports.pbUnload = async (req, res) => { + global.timer.unload(); + console.log('debug: unload called') + + res.sendStatus(200); +}; + +// Create controller for GET request to '/playback/reload' +// Reloads current event +exports.pbReload = async (req, res) => { + global.timer.reload(); + console.log('debug: reload called') + res.sendStatus(200); +}; diff --git a/server/routes/playbackRouter.js b/server/routes/playbackRouter.js index d2c3d7d19..e767a3156 100644 --- a/server/routes/playbackRouter.js +++ b/server/routes/playbackRouter.js @@ -25,4 +25,10 @@ router.get('/previous', playbackController.pbPrevious); // create route between controller and '/playback/next' endpoint router.get('/next', playbackController.pbNext); +// create route between controller and '/playback/unload' endpoint +router.get('/unload', playbackController.pbUnload); + +// create route between controller and '/playback/reload' endpoint +router.get('/reload', playbackController.pbReload); + module.exports = router;