diff --git a/client/src/features/editors/list/Block.module.css b/client/src/features/editors/list/Block.module.css index eb704ae02..716257a98 100644 --- a/client/src/features/editors/list/Block.module.css +++ b/client/src/features/editors/list/Block.module.css @@ -91,14 +91,20 @@ justify-content: center; } -.next { - color: #4bffabcc; +.next, +.nextDisabled { width: max-content; padding: 0 0.2em; } +.next { + color: #4bffabcc; + transition: 0.3s; +} + .nextDisabled { - color: rgba(255, 255, 255, 0.17); + color: rgba(255, 255, 255, 0.07); + transition: 0.3s; } .delayValue { diff --git a/client/src/features/editors/list/EventList.jsx b/client/src/features/editors/list/EventList.jsx index b30853fb9..edfe79a44 100644 --- a/client/src/features/editors/list/EventList.jsx +++ b/client/src/features/editors/list/EventList.jsx @@ -40,11 +40,6 @@ export default function EventList(props) { if (cursor == null) return; eventsHandler('add', { type: 'block', order: cursor + 1 }); }, - 'Alt+KeyN': (event) => { - event.preventDefault(); - if (cursor == null) return; - if (events[cursor].type === 'event') setNext(cursor); - }, }); if (cursor > events.length - 1) setCursor(events.length - 1); return () => { diff --git a/client/src/features/viewers/ViewWrapper.jsx b/client/src/features/viewers/ViewWrapper.jsx index 660bed68b..c701da609 100644 --- a/client/src/features/viewers/ViewWrapper.jsx +++ b/client/src/features/viewers/ViewWrapper.jsx @@ -48,7 +48,16 @@ const withSocket = (Component) => { subtitleNext: '', presenterNext: '', }); + const [publicTitles, setPublicTitles] = useState({ + titleNow: '', + subtitleNow: '', + presenterNow: '', + titleNext: '', + subtitleNext: '', + presenterNext: '', + }); const [selectedId, setSelectedId] = useState(null); + const [publicSelectedId, setPublicSelectedId] = useState(null); const [general, setGeneral] = useState({ title: '', url: '', @@ -90,11 +99,17 @@ const withSocket = (Component) => { socket.on('titles', (data) => { setTitles({ ...data }); }); + socket.on('publictitles', (data) => { + setPublicTitles({ ...data }); + }); // Handle selected event socket.on('selected-id', (data) => { setSelectedId(data); }); + socket.on('publicselected-id', (data) => { + setPublicSelectedId(data); + }); // Ask for up to date data socket.emit('get-messages'); @@ -110,6 +125,7 @@ const withSocket = (Component) => { // Ask for up titles socket.emit('get-titles'); + socket.emit('get-publictitles'); // Ask for up selected socket.emit('get-selected-id'); @@ -122,6 +138,7 @@ const withSocket = (Component) => { socket.off('timer'); socket.off('playstate'); socket.off('titles'); + socket.off('publictitles'); socket.off('selected-id'); }; }, [socket]); @@ -131,7 +148,9 @@ const withSocket = (Component) => { if (eventsData == null) return; // filter just events with title - const pe = eventsData.filter((d) => d.type === 'event' && d.title !== '' && d.isPublic === true); + const pe = eventsData.filter( + (d) => d.type === 'event' && d.title !== '' && d.isPublic === true + ); setPublicEvents(pe); // everything goes backstage @@ -161,6 +180,35 @@ const withSocket = (Component) => { const titleManager = { ...titles, showNow: showNow, showNext: showNext }; + /********************************************/ + /*** + publicTitleManager ***/ + /*** WRAP INFORMATION RELATED TO TITLES ***/ + /*** ---------------------------------- ***/ + /********************************************/ + // is there a now field? + let showPublicNow = true; + if ( + !publicTitles.titleNow && + !publicTitles.subtitleNow && + !publicTitles.presenterNow + ) + showPublicNow = false; + + // is there a next field? + let showPublicNext = true; + if ( + !publicTitles.titleNext && + !publicTitles.subtitleNext && + !publicTitles.presenterNext + ) + showPublicNext = false; + + const publicTitleManager = { + ...publicTitles, + showNow: showPublicNow, + showNext: showPublicNext, + }; + /******************************************/ /*** + timeManager ***/ /*** WRAP INFORMATION RELATED TO TIME ***/ @@ -187,10 +235,12 @@ const withSocket = (Component) => { publ={publ} lower={lower} title={titleManager} + publicTitle={publicTitleManager} time={timeManager} events={publicEvents} backstageEvents={backstageEvents} selectedId={selectedId} + publicSelectedId={publicSelectedId} general={general} /> ); diff --git a/client/src/features/viewers/foh/Public.jsx b/client/src/features/viewers/foh/Public.jsx index 916cd4a68..2aa256cc0 100644 --- a/client/src/features/viewers/foh/Public.jsx +++ b/client/src/features/viewers/foh/Public.jsx @@ -6,7 +6,7 @@ import { AnimatePresence, motion } from 'framer-motion'; import TitleSide from '../../../common/components/views/TitleSide'; export default function StageManager(props) { - const { publ, title, time, events, selectedId, general } = props; + const { publ, publicTitle, time, events, publicSelectedId, general } = props; // Format messages const showPubl = publ.text !== '' && publ.visible; @@ -34,7 +34,7 @@ export default function StageManager(props) {
{general.title}
- {title.showNow && ( + {publicTitle.showNow && ( )} - {title.showNext && ( + {publicTitle.showNext && ( )} @@ -78,7 +78,7 @@ export default function StageManager(props) {
Today
- +
diff --git a/server/classes/EventTimer.js b/server/classes/EventTimer.js index adae127cd..ed11b285f 100644 --- a/server/classes/EventTimer.js +++ b/server/classes/EventTimer.js @@ -10,10 +10,11 @@ const socketIo = require('socket.io'); */ class EventTimer extends Timer { + // AUX + DAYMS = 86400000; + // Socket IO Object io = null; - - // Socket IO helpers _numClients = 0; _interval = null; @@ -30,6 +31,15 @@ class EventTimer extends Timer { visible: false, }; + titlesPublic = { + titleNow: null, + subtitleNow: null, + presenterNow: null, + titleNext: null, + subtitleNext: null, + presenterNext: null, + }; + titles = { titleNow: null, subtitleNow: null, @@ -42,6 +52,8 @@ class EventTimer extends Timer { selectedEvent = null; selectedEventId = null; nextEventId = null; + selectedPublicEventId = null; + nextPublicEventId = null; numEvents = null; _eventlist = null; @@ -80,7 +92,10 @@ class EventTimer extends Timer { this.io.emit('playstate', this.state); this.io.emit('selected-id', this.selectedEventId); this.io.emit('next-id', this.nextEventId); + this.io.emit('publicselected-id', this.selectedPublicEventId); + this.io.emit('publicnext-id', this.nextPublicEventId); this.io.emit('titles', this.titles); + this.io.emit('publictitles', this.titlesPublic); } // broadcast message @@ -183,7 +198,9 @@ class EventTimer extends Timer { socket.emit('timer', this.getObject()); socket.emit('playstate', this.state); socket.emit('selected-id', this.selectedEventId); - socket.emit('titles', this.titles); + socket.emit('next-id', this.nextEventId); + socket.emit('publicselected-id', this.selectedPublicEventId); + socket.emit('publicnext-id', this.nextPublicEventId); /********************************/ /*** HANDLE DISCONNECT USER ***/ @@ -208,7 +225,8 @@ class EventTimer extends Timer { socket.emit('playstate', this.state); socket.emit('selected-id', this.selectedEventId); socket.emit('next-id', this.nextEventId); - socket.emit('titles', this.titles); + socket.emit('publicselected-id', this.selectedPublicEventId); + socket.emit('publicnext-id', this.this.nextPublicEventId); }); /*******************************************/ @@ -232,7 +250,7 @@ class EventTimer extends Timer { }); /*******************************************/ - // titles data + // selection data socket.on('get-selected-id', () => { socket.emit('selected-id', this.selectedEventId); }); @@ -241,23 +259,22 @@ class EventTimer extends Timer { socket.emit('next-id', this.nextEventId); }); + socket.on('get-publicselected-id', () => { + socket.emit('publicselected-id', this.selectedPublicEventId); + }); + + socket.on('get-publicnext-id', () => { + socket.emit('publicnext-id', this.nextPublicEventId); + }); + + // title data socket.on('get-titles', () => { socket.emit('titles', this.titles); }); - /*****************************/ - /*** BROADCAST ***/ - /*** TIMER STATE GETTERS ***/ - /*** ------------------- ***/ - /*****************************/ - - /*******************************************/ - // playback API - // ? should i change the address tokeep convention? - socket.on('get-messages', () => { - this.broadcastThis('messages-presenter', this.presenter); - this.broadcastThis('messages-public', this.public); - this.broadcastThis('messages-lower', this.lower); + // title data + socket.on('get-publictitles', () => { + socket.emit('publictitles', this.titlesPublic); }); /***********************************/ @@ -266,6 +283,14 @@ class EventTimer extends Timer { /***********************************/ /*******************************************/ + + // Messages + socket.on('get-messages', () => { + this.broadcastThis('messages-presenter', this.presenter); + this.broadcastThis('messages-public', this.public); + this.broadcastThis('messages-lower', this.lower); + }); + // Presenter message socket.on('set-presenter-text', (data) => { this._setterManager('set-presenter-text', data); @@ -327,11 +352,10 @@ class EventTimer extends Timer { // filter only events const events = eventlist.filter((e) => e.type === 'event'); - const numEvents = events.length; - // set general this._eventList = events; - this.numEvents = numEvents; + this.numEvents = events.length; + // handle reload selected if (this.selectedEventId != null) { // Look for event (order might have changed) @@ -347,90 +371,123 @@ class EventTimer extends Timer { } // Reload data if running - if (this._startedAt != null) this.reloadRunning(eventIndex); - else if (this.selectedEventId != null) this.loadEvent(eventIndex); + let type = this._startedAt != null ? 'reload' : 'load'; + this.loadEvent(eventIndex, type); + + this.broadcastState(); } } - // Reloads changed info in current event - reloadRunning(eventIndex) { + // Loads a given event + loadEvent(eventIndex, type = 'load') { const e = this._eventList[eventIndex]; + const start = e.timeStart == null || e.timeStart === '' ? 0 : e.timeStart; let end = e.timeEnd == null || e.timeEnd === '' ? 0 : e.timeEnd; - // in case the end is earlier than start, we assume is the day after - if (end < start) end += 86400000; + if (end < start) end += this.DAYMS; - // time stuff - const now = this._getCurrentTime(); - const elapsed = this.getElapsed(); + // time stuff changes on wheter we keep the running clock + if (type === 'load') { + this._resetTimers(); + this.duration = end - start; + this.current = this.duration; + this.selectedEvent = eventIndex; + this.selectedEventId = e.id; + } else if (type === 'reload') { + const now = this._getCurrentTime(); + const elapsed = this.getElapsed(); - this.duration = end - start; - this.selectedEvent = eventIndex; - this._finishAt = now + (this.duration - elapsed); + this.duration = end - start; + this.selectedEvent = eventIndex; + this._finishAt = now + (this.duration - elapsed); + } - // event titles + // load current titles + this._loadTitlesNow(e, eventIndex); + + // look for event after + this._loadTitlesNext(eventIndex); + } + + _loadTitlesNow(e, eventIndex) { + // private title is always current 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; - this.nextEventId = null; - - // look for event after - this._loadNext(eventIndex); - - this.broadcastState(); - } - - // Loads a given event - loadEvent(eventIndex) { - // set event specific - const e = this._eventList[eventIndex]; - const start = e.timeStart == null || e.timeStart === '' ? 0 : e.timeStart; - let end = e.timeEnd == null || e.timeEnd === '' ? 0 : e.timeEnd; - - // in case the end is earlier than start, we assume is the day after - if (end < start) end += 86400000; - - // time stuff - this._resetTimers(); - this.duration = end - start; - this.current = this.duration; - this.selectedEvent = eventIndex; this.selectedEventId = e.id; - // event titles - this.titles.titleNow = e.title; - this.titles.subtitleNow = e.subtitle; - this.titles.presenterNow = e.presenter; + // check if current is also public + if (e.isPublic) { + this.titlesPublic.titleNow = e.title; + this.titlesPublic.subtitleNow = e.subtitle; + this.titlesPublic.presenterNow = e.presenter; + this.selectedPublicEventId = e.id; + } else { + // assume there is no public event + this.titlesPublic.titleNow = null; + this.titlesPublic.subtitleNow = null; + this.titlesPublic.presenterNow = null; + this.selectedPublicEventId = null; - // assume tere is no next event + // if there is nothing before, return + if (eventIndex === 0) return; + + // iterate backwards to find it + for (let i = eventIndex - 1; i >= 0; i--) { + if ( + this._eventList[i].type === 'event' && + this._eventList[i].isPublic + ) { + this.titlesPublic.titleNow = this._eventList[i].title; + this.titlesPublic.subtitleNow = this._eventList[i].subtitle; + this.titlesPublic.presenterNow = this._eventList[i].presenter; + this.selectedPublicEventId = this._eventList[i].id; + } + } + } + } + + _loadTitlesNext(eventIndex) { + // assume there is no next event this.titles.titleNext = null; this.titles.subtitleNext = null; this.titles.presenterNext = null; this.nextEventId = null; - // look for event after - this._loadNext(eventIndex); + this.titlesPublic.titleNext = null; + this.titlesPublic.subtitleNext = null; + this.titlesPublic.presenterNext = null; + this.nextPublicEventId = null; - this.broadcastState(); - } - - _loadNext(eventIndex) { if (eventIndex < this.numEvents - 1) { + let nextPublic = false; + let nextPrivate = false; + for (let i = eventIndex + 1; i < this.numEvents; i++) { // check that is the right type 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; - this.nextEventId = this._eventList[i].id; - break; + // if we have not set private + if (!nextPrivate) { + this.titles.titleNext = this._eventList[i].title; + this.titles.subtitleNext = this._eventList[i].subtitle; + this.titles.presenterNext = this._eventList[i].presenter; + this.nextEventId = this._eventList[i].id; + nextPrivate = true; + } + + // if event is public + if (this._eventList[i].isPublic) { + this.titlesPublic.titleNext = this._eventList[i].title; + this.titlesPublic.subtitleNext = this._eventList[i].subtitle; + this.titlesPublic.presenterNext = this._eventList[i].presenter; + this.nextPublicEventId = this._eventList[i].id; + nextPublic = true; + } } + + // Stop if both are set + if (nextPublic && nextPrivate) break; } } } @@ -445,8 +502,20 @@ class EventTimer extends Timer { presenterNext: null, }; + this.publicTitles = { + titleNow: null, + subtitleNow: null, + presenterNow: null, + titleNext: null, + subtitleNext: null, + presenterNext: null, + }; + this.selectedEvent = null; this.selectedEventId = null; + this.nextEventId = null; + this.selectedPublicEventId = null; + this.nextPublicEventId = null; } print() { @@ -468,10 +537,29 @@ class EventTimer extends Timer { ------------------------------ numEvents = ${this.numEvents} selectedEvent = ${this.selectedEvent} - selectedEventId = ${this.selectedEventId} - title = ${this.titles.title} - subtitle = ${this.titles.subtitle} - presenter = ${this.titles.presenter} + + Private Titles + ------------------------------ + NowID = ${this.selectedEventId} + NextID = ${this.nextEventId} + Title Now = ${this.titles.titleNow} + Subtitle Now = ${this.titles.subtitleNow} + Presenter Now = ${this.titles.presenterNow} + Title Next = ${this.titles.titleNext} + Subtitle Next = ${this.titles.subtitleNext} + Presenter Next = ${this.titles.presenterNext} + + Public Titles + ------------------------------ + NowID = ${this.selectedPublicEventId} + NextID = ${this.nextPublicEventId} + Title Now = ${this.titlesPublic.titleNow} + Subtitle Now = ${this.titlesPublic.subtitleNow} + Presenter Now = ${this.titlesPublic.presenterNow} + Title Next = ${this.titlesPublic.titleNext} + Subtitle Next = ${this.titlesPublic.subtitleNext} + Presenter Next = ${this.titlesPublic.presenterNext} + Messages ------------------------------ @@ -533,6 +621,9 @@ class EventTimer extends Timer { // check that we have events to run if (this.numEvents < 1) return; + // change playstate + this.state = 'pause'; + // if there is no event running, go to first if (this.selectedEvent == null) { this.goto(0); @@ -551,6 +642,9 @@ class EventTimer extends Timer { // check that we have events to run if (this.numEvents < 1) return; + // change playstate + this.state = 'pause'; + // if there is no event running, go to first if (this.selectedEvent == null) { this.goto(0);