From 9708f0bfc6b054b8c0b833fe3ca1fdbd6590d92d Mon Sep 17 00:00:00 2001 From: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Date: Thu, 14 Sep 2023 22:30:23 +0200 Subject: [PATCH] refactor: pass events instead of derived data to titles (#493) * refactor: pass events instead of derived data to titles --- apps/client/src/common/hooks/useSocket.ts | 5 +- apps/client/src/common/stores/runtime.ts | 24 +- apps/client/src/common/utils/socket.ts | 12 - apps/client/src/features/info/Info.tsx | 16 +- .../src/features/viewers/ViewWrapper.tsx | 66 ++--- .../features/viewers/backstage/Backstage.tsx | 22 +- .../viewers/lower-thirds/LowerLines.tsx | 15 +- .../viewers/lower-thirds/LowerWrapper.tsx | 54 ++-- .../src/features/viewers/public/Public.tsx | 23 +- .../features/viewers/studio/StudioClock.tsx | 7 +- .../src/features/viewers/timer/Timer.tsx | 20 +- .../src/classes/event-loader/EventLoader.ts | 266 ++++-------------- .../rundown-service/RundownService.ts | 14 +- apps/server/src/stores/EventStore.ts | 12 +- .../definitions/runtime/RuntimeStore.type.ts | 8 +- .../definitions/runtime/TitleBlock.type.ts | 10 - packages/types/src/index.ts | 3 +- 17 files changed, 178 insertions(+), 399 deletions(-) delete mode 100644 packages/types/src/definitions/runtime/TitleBlock.type.ts diff --git a/apps/client/src/common/hooks/useSocket.ts b/apps/client/src/common/hooks/useSocket.ts index 00b1f2054..f2d5fbb5f 100644 --- a/apps/client/src/common/hooks/useSocket.ts +++ b/apps/client/src/common/hooks/useSocket.ts @@ -79,7 +79,8 @@ export const setPlayback = { export const useInfoPanel = () => { const featureSelector = (state: RuntimeStore) => ({ - titles: state.titles, + eventNow: state.eventNow, + eventNext: state.eventNext, playback: state.playback, selectedEventIndex: state.loaded.selectedEventIndex, numEvents: state.loaded.numEvents, @@ -94,7 +95,7 @@ export const useCuesheet = () => { selectedEventId: state.loaded.selectedEventId, selectedEventIndex: state.loaded.selectedEventIndex, numEvents: state.loaded.numEvents, - titleNow: state.titles.titleNow, + titleNow: state.eventNow?.title || '', }); return useRuntimeStore(featureSelector, deepCompare); diff --git a/apps/client/src/common/stores/runtime.ts b/apps/client/src/common/stores/runtime.ts index 321e3b5f2..3f503308d 100644 --- a/apps/client/src/common/stores/runtime.ts +++ b/apps/client/src/common/stores/runtime.ts @@ -42,26 +42,10 @@ export const runtimeStorePlaceholder = { nextEventId: null, nextPublicEventId: null, }, - titles: { - titleNow: null, - subtitleNow: null, - presenterNow: null, - noteNow: null, - titleNext: null, - subtitleNext: null, - presenterNext: null, - noteNext: null, - }, - titlesPublic: { - titleNow: null, - subtitleNow: null, - presenterNow: null, - noteNow: null, - titleNext: null, - subtitleNext: null, - presenterNext: null, - noteNext: null, - }, + eventNow: null, + eventNext: null, + publicEventNow: null, + publicEventNext: null, }; export const runtime = createStore(() => ({ diff --git a/apps/client/src/common/utils/socket.ts b/apps/client/src/common/utils/socket.ts index 17cee8699..58cb68c84 100644 --- a/apps/client/src/common/utils/socket.ts +++ b/apps/client/src/common/utils/socket.ts @@ -87,18 +87,6 @@ export const connectSocket = (preferredClientName?: string) => { runtime.setState(state); break; } - case 'ontime-titles': { - const state = runtime.getState(); - state.titles = payload; - runtime.setState(state); - break; - } - case 'ontime-titlesPublic': { - const state = runtime.getState(); - state.titlesPublic = payload; - runtime.setState(state); - break; - } case 'ontime-timerMessage': { const state = runtime.getState(); state.timerMessage = payload; diff --git a/apps/client/src/features/info/Info.tsx b/apps/client/src/features/info/Info.tsx index d8807ce73..7e462c5b4 100644 --- a/apps/client/src/features/info/Info.tsx +++ b/apps/client/src/features/info/Info.tsx @@ -12,17 +12,17 @@ export default function Info() { const showNif = useEditorSettings((state) => state.eventSettings.showNif); const titlesNow = { - title: data.titles.titleNow || '', - subtitle: data.titles.subtitleNow || '', - presenter: data.titles.presenterNow || '', - note: data.titles.noteNow || '', + title: data.eventNow?.title || '', + subtitle: data.eventNow?.subtitle || '', + presenter: data.eventNow?.presenter || '', + note: data.eventNow?.note || '', }; const titlesNext = { - title: data.titles.titleNext || '', - subtitle: data.titles.subtitleNext || '', - presenter: data.titles.presenterNext || '', - note: data.titles.noteNext || '', + title: data.eventNext?.title || '', + subtitle: data.eventNext?.subtitle || '', + presenter: data.eventNext?.presenter || '', + note: data.eventNext?.note || '', }; const selected = !data.numEvents diff --git a/apps/client/src/features/viewers/ViewWrapper.tsx b/apps/client/src/features/viewers/ViewWrapper.tsx index 41d9aa4a7..1775e5217 100644 --- a/apps/client/src/features/viewers/ViewWrapper.tsx +++ b/apps/client/src/features/viewers/ViewWrapper.tsx @@ -1,6 +1,5 @@ /* eslint-disable react/display-name */ import { ComponentType, useMemo } from 'react'; -import { TitleBlock } from 'ontime-types'; import { useStore } from 'zustand'; import useEventData from '../../common/hooks-query/useEventData'; @@ -9,8 +8,6 @@ import useViewSettings from '../../common/hooks-query/useViewSettings'; import { runtime } from '../../common/stores/runtime'; import { useViewOptionsStore } from '../../common/stores/viewOptions'; -export type TitleManager = TitleBlock & { showNow: boolean; showNext: boolean }; - const withData =

(Component: ComponentType

) => { return (props: Partial

) => { // persisted app state @@ -30,44 +27,22 @@ const withData =

(Component: ComponentType

) => { // websocket data const data = useStore(runtime); - const { timer, titles, titlesPublic, publicMessage, timerMessage, lowerMessage, playback, onAir } = data; - const publicSelectedId = data.loaded.selectedPublicEventId; - const selectedId = data.loaded.selectedEventId; - const nextId = data.loaded.nextEventId; - - /********************************************/ - /*** + titleManager ***/ - /*** WRAP INFORMATION RELATED TO TITLES ***/ - /*** ---------------------------------- ***/ - /********************************************/ - // is there a now field? - let showNow = true; - if (!titles.titleNow && !titles.subtitleNow && !titles.presenterNow) showNow = false; - - // is there a next field? - let showNext = true; - if (!titles.titleNext && !titles.subtitleNext && !titles.presenterNext) showNext = false; - - const titleManager: TitleManager = { ...titles, showNow: showNow, showNext: showNext }; - - /********************************************/ - /*** + publicTitleManager ***/ - /*** WRAP INFORMATION RELATED TO TITLES ***/ - /*** ---------------------------------- ***/ - /********************************************/ - // is there a now field? - let showPublicNow = true; - if (!titlesPublic.titleNow && !titlesPublic.subtitleNow && !titlesPublic.presenterNow) showPublicNow = false; - - // is there a next field? - let showPublicNext = true; - if (!titlesPublic.titleNext && !titlesPublic.subtitleNext && !titlesPublic.presenterNext) showPublicNext = false; - - const publicTitleManager: TitleManager = { - ...titlesPublic, - showNow: showPublicNow, - showNext: showPublicNext, - }; + const { + timer, + publicMessage, + timerMessage, + lowerMessage, + playback, + onAir, + eventNext, + publicEventNext, + publicEventNow, + eventNow, + loaded, + } = data; + const publicSelectedId = loaded.selectedPublicEventId; + const selectedId = loaded.selectedEventId; + const nextId = loaded.nextEventId; /******************************************/ /*** + TimeManagerType ***/ @@ -75,9 +50,6 @@ const withData =

(Component: ComponentType

) => { /*** -------------------------------- ***/ /******************************************/ - // inject info: - // is timer finished - // get clock string const TimeManagerType = { ...timer, playback, @@ -95,8 +67,10 @@ const withData =

(Component: ComponentType

) => { pres={timerMessage} publ={publicMessage} lower={lowerMessage} - title={titleManager} - publicTitle={publicTitleManager} + eventNow={eventNow} + publicEventNow={publicEventNow} + eventNext={eventNext} + publicEventNext={publicEventNext} time={TimeManagerType} events={publicEvents} backstageEvents={rundownData} diff --git a/apps/client/src/features/viewers/backstage/Backstage.tsx b/apps/client/src/features/viewers/backstage/Backstage.tsx index 54ca836ae..950ee6d7f 100644 --- a/apps/client/src/features/viewers/backstage/Backstage.tsx +++ b/apps/client/src/features/viewers/backstage/Backstage.tsx @@ -18,7 +18,6 @@ import { TimeManagerType } from '../../../common/models/TimeManager.type'; import { formatTime } from '../../../common/utils/time'; import { useTranslation } from '../../../translation/TranslationProvider'; import { titleVariants } from '../common/animation'; -import { TitleManager } from '../ViewWrapper'; import './Backstage.scss'; @@ -30,7 +29,8 @@ const formatOptions = { interface BackstageProps { isMirrored: boolean; publ: Message; - title: TitleManager; + eventNow: OntimeEvent | null; + eventNext: OntimeEvent | null; time: TimeManagerType; backstageEvents: OntimeEvent[]; selectedId: string | null; @@ -39,7 +39,7 @@ interface BackstageProps { } export default function Backstage(props: BackstageProps) { - const { isMirrored, publ, title, time, backstageEvents, selectedId, general, viewSettings } = props; + const { isMirrored, publ, eventNow, eventNext, time, backstageEvents, selectedId, general, viewSettings } = props; const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const { getLocalizedString } = useTranslation(); const [blinkClass, setBlinkClass] = useState(false); @@ -110,7 +110,7 @@ export default function Backstage(props: BackstageProps) {

- {title.showNow && ( + {eventNow && (
@@ -144,7 +144,7 @@ export default function Backstage(props: BackstageProps) { - {title.showNext && ( + {eventNext && ( )} diff --git a/apps/client/src/features/viewers/lower-thirds/LowerLines.tsx b/apps/client/src/features/viewers/lower-thirds/LowerLines.tsx index 1b0b84f52..af2fa8f2e 100644 --- a/apps/client/src/features/viewers/lower-thirds/LowerLines.tsx +++ b/apps/client/src/features/viewers/lower-thirds/LowerLines.tsx @@ -5,7 +5,6 @@ import { Message } from 'ontime-types'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import { LOWER_THIRDS_OPTIONS } from '../../../common/components/view-params-editor/constants'; import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; -import { TitleManager } from '../ViewWrapper'; import { LowerOptions } from './LowerWrapper'; @@ -13,12 +12,14 @@ import './LowerLines.scss'; interface LowerLinesProps { lower: Message; - title: TitleManager; + heading: string; + subheading: string; options: LowerOptions; + doShow: boolean; } export default function LowerLines(props: LowerLinesProps) { - const { lower, title, options } = props; + const { lower, heading, subheading, options, doShow } = props; const [showLower, setShowLower] = useState(true); // Unmount if fadeOut @@ -36,8 +37,8 @@ export default function LowerLines(props: LowerLinesProps) { }, [options.fadeOut, options.transitionIn]); useEffect(() => { - setShowLower(title.showNow); - }, [title.showNow]); + setShowLower(doShow); + }, [doShow]); // Format messages const showLowerMessage = lower.text !== '' && lower.visible; @@ -146,14 +147,14 @@ export default function LowerLines(props: LowerLinesProps) { > - {title.titleNow} + {heading}
- {title.presenterNow} + {subheading} diff --git a/apps/client/src/features/viewers/lower-thirds/LowerWrapper.tsx b/apps/client/src/features/viewers/lower-thirds/LowerWrapper.tsx index 8dd2a4d96..1540e5bb2 100644 --- a/apps/client/src/features/viewers/lower-thirds/LowerWrapper.tsx +++ b/apps/client/src/features/viewers/lower-thirds/LowerWrapper.tsx @@ -1,11 +1,10 @@ import { memo, useEffect, useState } from 'react'; import isEqual from 'react-fast-compare'; import { useSearchParams } from 'react-router-dom'; -import { Message, ViewSettings } from 'ontime-types'; +import { Message, OntimeEvent, ViewSettings } from 'ontime-types'; import { overrideStylesURL } from '../../../common/api/apiConstants'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; -import { TitleManager } from '../ViewWrapper'; import LowerLines from './LowerLines'; @@ -17,33 +16,26 @@ export type LowerOptions = { keyColour?: string; fadeOut: number; }; + interface LowerProps { - title: TitleManager; + eventNow: OntimeEvent | null; lower: Message; viewSettings: ViewSettings; } // prevent triggering animation without a content change const areEqual = (prevProps: LowerProps, nextProps: LowerProps) => { - return isEqual(prevProps.title, nextProps.title) && isEqual(prevProps.lower, nextProps.lower); + return isEqual(prevProps.eventNow?.title, nextProps.eventNow?.title) && isEqual(prevProps.lower, nextProps.lower); }; const Lower = (props: LowerProps) => { - const { title, lower, viewSettings } = props; + const { eventNow, lower, viewSettings } = props; const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const [searchParams] = useSearchParams(); - const [titles, setTitles] = useState({ - titleNow: '', - titleNext: '', - subtitleNow: '', - subtitleNext: '', - presenterNow: '', - presenterNext: '', - noteNow: '', - noteNext: '', - showNow: false, - showNext: false, - }); + + const [heading, setHeading] = useState(''); + const [subheading, setSubheading] = useState(''); + const [showLower, setShowLower] = useState(false); // Set window title useEffect(() => { @@ -54,28 +46,32 @@ const Lower = (props: LowerProps) => { useEffect(() => { // clear titles if necessary // will trigger an animation out in the component - let timeout: NodeJS.Timeout | null = null; - if ( - title?.titleNow !== titles?.titleNow || - title?.subtitleNow !== titles?.subtitleNow || - title?.presenterNow !== titles?.presenterNow - ) { - setTitles((t) => ({ ...t, showNow: false })); + let timeout: NodeJS.Timeout; - const transitionTime = 2000; + const haveTitlesChanged = eventNow?.title !== heading || eventNow?.presenter !== subheading; + const areTitlesEmpty = !eventNow?.title && !eventNow?.presenter; + // we have new titles + if (haveTitlesChanged && !areTitlesEmpty) { + // show lower + setHeading(eventNow?.title ?? ''); + setSubheading(eventNow?.presenter ?? ''); + setShowLower(true); + + // schedule transition out + const transitionTime = 5000; timeout = setTimeout(() => { - setTitles(title); + setShowLower(false); }, transitionTime); } return () => { - if (timeout != null) { + if (timeout) { clearTimeout(timeout); } }; // eslint-disable-next-line -- we do this to keep animations - }, [title.titleNow, title.subtitleNow, title.presenterNow]); + }, [eventNow?.title, eventNow?.presenter]); // defer rendering until we load stylesheets if (!shouldRender) { @@ -135,7 +131,7 @@ const Lower = (props: LowerProps) => { } } - return ; + return ; }; export default memo(Lower, areEqual); diff --git a/apps/client/src/features/viewers/public/Public.tsx b/apps/client/src/features/viewers/public/Public.tsx index 8e71697e7..c2600c890 100644 --- a/apps/client/src/features/viewers/public/Public.tsx +++ b/apps/client/src/features/viewers/public/Public.tsx @@ -16,7 +16,6 @@ import { TimeManagerType } from '../../../common/models/TimeManager.type'; import { formatTime } from '../../../common/utils/time'; import { useTranslation } from '../../../translation/TranslationProvider'; import { titleVariants } from '../common/animation'; -import { TitleManager } from '../ViewWrapper'; import './Public.scss'; @@ -28,7 +27,8 @@ const formatOptions = { interface BackstageProps { isMirrored: boolean; publ: Message; - publicTitle: TitleManager; + publicEventNow: OntimeEvent | null; + publicEventNext: OntimeEvent | null; time: TimeManagerType; events: OntimeEvent[]; publicSelectedId: string | null; @@ -37,7 +37,8 @@ interface BackstageProps { } export default function Public(props: BackstageProps) { - const { isMirrored, publ, publicTitle, time, events, publicSelectedId, general, viewSettings } = props; + const { isMirrored, publ, publicEventNow, publicEventNext, time, events, publicSelectedId, general, viewSettings } = + props; const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const { getLocalizedString } = useTranslation(); @@ -68,7 +69,7 @@ export default function Public(props: BackstageProps) {
- {publicTitle.showNow && ( + {publicEventNow && ( )} - {publicTitle.showNext && ( + {publicEventNext && ( )} diff --git a/apps/client/src/features/viewers/studio/StudioClock.tsx b/apps/client/src/features/viewers/studio/StudioClock.tsx index f244b5de4..c489071cf 100644 --- a/apps/client/src/features/viewers/studio/StudioClock.tsx +++ b/apps/client/src/features/viewers/studio/StudioClock.tsx @@ -13,7 +13,6 @@ import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet import { TimeManagerType } from '../../../common/models/TimeManager.type'; import { secondsInMillis } from '../../../common/utils/dateConfig'; import { formatTime } from '../../../common/utils/time'; -import { TitleManager } from '../ViewWrapper'; import { type ScheduleEvent, formatEventList, trimRundown } from './studioClock.utils'; @@ -26,7 +25,7 @@ const formatOptions = { interface StudioClockProps { isMirrored: boolean; - title: TitleManager; + eventNext: OntimeEvent | null; time: TimeManagerType; backstageEvents: OntimeRundown; selectedId: string | null; @@ -36,7 +35,7 @@ interface StudioClockProps { } export default function StudioClock(props: StudioClockProps) { - const { isMirrored, title, time, backstageEvents, selectedId, nextId, onAir, viewSettings } = props; + const { isMirrored, eventNext, time, backstageEvents, selectedId, nextId, onAir, viewSettings } = props; // deferring rendering seems to affect styling (font and useFitText) useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); @@ -87,7 +86,7 @@ export default function StudioClock(props: StudioClockProps) { className='next-title' style={{ fontSize: titleFontSize, height: '10vh', width: '100%', maxWidth: '75%' }} > - {title.titleNext} + {eventNext?.title ?? ''}
{selectedId !== null && formatDisplay(time.current)} diff --git a/apps/client/src/features/viewers/timer/Timer.tsx b/apps/client/src/features/viewers/timer/Timer.tsx index f099f4953..6f286ab49 100644 --- a/apps/client/src/features/viewers/timer/Timer.tsx +++ b/apps/client/src/features/viewers/timer/Timer.tsx @@ -1,6 +1,6 @@ import { useEffect } from 'react'; import { AnimatePresence, motion } from 'framer-motion'; -import { EventData, Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types'; +import { EventData, OntimeEvent, Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types'; import { overrideStylesURL } from '../../../common/api/apiConstants'; import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar'; @@ -13,7 +13,6 @@ import { TimeManagerType } from '../../../common/models/TimeManager.type'; import { formatTime } from '../../../common/utils/time'; import { useTranslation } from '../../../translation/TranslationProvider'; import { formatTimerDisplay, getTimerByType } from '../common/viewerUtils'; -import { TitleManager } from '../ViewWrapper'; import './Timer.scss'; @@ -42,13 +41,14 @@ interface TimerProps { isMirrored: boolean; general: EventData; pres: TimerMessage; - title: TitleManager; + eventNow: OntimeEvent | null; + eventNext: OntimeEvent | null; time: TimeManagerType; viewSettings: ViewSettings; } export default function Timer(props: TimerProps) { - const { isMirrored, pres, title, time, viewSettings } = props; + const { isMirrored, pres, eventNow, eventNext, time, viewSettings } = props; const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const { getLocalizedString } = useTranslation(); @@ -139,7 +139,7 @@ export default function Timer(props: TimerProps) { /> - {title.showNow && !finished && ( + {eventNow && !finished && ( - + )} - {title.showNext && ( + {eventNext && ( )} diff --git a/apps/server/src/classes/event-loader/EventLoader.ts b/apps/server/src/classes/event-loader/EventLoader.ts index cc5aa1603..254040696 100644 --- a/apps/server/src/classes/event-loader/EventLoader.ts +++ b/apps/server/src/classes/event-loader/EventLoader.ts @@ -1,4 +1,4 @@ -import { Loaded, OntimeEvent, SupportedEvent, TitleBlock } from 'ontime-types'; +import { Loaded, OntimeEvent, SupportedEvent } from 'ontime-types'; import { DataProvider } from '../data-provider/DataProvider.js'; import { getRollTimers } from '../../services/rollUtils.js'; @@ -10,10 +10,11 @@ let instance; * Manages business logic around loading events */ export class EventLoader { - loadedEvent: OntimeEvent | null; loaded: Loaded; - titles: TitleBlock; - titlesPublic: TitleBlock; + eventNow: OntimeEvent | null; + publicEventNow: OntimeEvent | null; + eventNext: OntimeEvent | null; + publicEventNext: OntimeEvent | null; constructor() { if (instance) { @@ -56,7 +57,7 @@ export class EventLoader { } /** - * returns an event given its index + * returns an event given its index after filtering for OntimeEvents * @param {number} eventIndex * @return {OntimeEvent | undefined} */ @@ -65,16 +66,6 @@ export class EventLoader { return timedEvents?.[eventIndex]; } - /** - * returns an event given its index - * @param {number} eventIndex - * @return {object | undefined} - */ - static getPlayableAtIndex(eventIndex) { - const timedEvents = EventLoader.getPlayableEvents(); - return timedEvents?.[eventIndex]; - } - /** * returns an event given its id * @param {string} eventId @@ -167,16 +158,18 @@ export class EventLoader { timeNow, ); - this.loadedEvent = currentEvent; + // load events + this.eventNow = currentEvent; + this.publicEventNow = currentPublicEvent; + this.eventNext = nextEvent; + this.publicEventNext = nextPublicEvent; + + // loaded data summary this.loaded.selectedEventIndex = nowIndex; this.loaded.selectedEventId = currentEvent?.id || null; this.loaded.numEvents = timedEvents.length; - - // titles - this._loadThisTitles(currentEvent, 'now-private'); - this._loadThisTitles(currentPublicEvent, 'now-public'); - this._loadThisTitles(nextEvent, 'next-private'); - this._loadThisTitles(nextPublicEvent, 'next-public'); + this.loaded.nextEventId = nextEvent.id; + this.loaded.nextPublicEventId = nextPublicEvent.id; return { currentEvent, nextEvent, timeToNext }; } @@ -187,10 +180,11 @@ export class EventLoader { */ getLoaded() { return { - loadedEvent: this.loadedEvent, loaded: this.loaded, - titles: this.titles, - titlesPublic: this.titlesPublic, + eventNow: this.eventNow, + publicEventNow: this.publicEventNow, + eventNext: this.eventNext, + publicEventNext: this.publicEventNext, }; } @@ -206,7 +200,10 @@ export class EventLoader { * Resets instance state */ reset(emit = true) { - this.loadedEvent = null; + this.eventNow = null; + this.publicEventNow = null; + this.eventNext = null; + this.publicEventNext = null; this.loaded = { selectedEventIndex: null, selectedEventId: null, @@ -215,26 +212,6 @@ export class EventLoader { nextPublicEventId: null, numEvents: EventLoader.getPlayableEvents().length, }; - this.titles = { - titleNow: null, - subtitleNow: null, - presenterNow: null, - noteNow: null, - titleNext: null, - subtitleNext: null, - presenterNext: null, - noteNext: null, - }; - this.titlesPublic = { - titleNow: null, - subtitleNow: null, - presenterNow: null, - noteNow: null, - titleNext: null, - subtitleNext: null, - presenterNext: null, - noteNext: null, - }; // workaround for socket not being ready in constructor if (emit) { @@ -255,13 +232,12 @@ export class EventLoader { const playableEvents = EventLoader.getPlayableEvents(); // we know some stuff now - this.loadedEvent = event; this.loaded.selectedEventIndex = eventIndex; this.loaded.selectedEventId = event.id; this.loaded.numEvents = timedEvents.length; - // this.nextEventId = playableEvents[eventIndex + 1].id; - this._loadTitlesNow(event, playableEvents); - this._loadTitlesNext(playableEvents); + this.eventNow = event; + this._loadEventNow(event, playableEvents); + this._loadEventNext(playableEvents); this._loadEvent(); @@ -274,29 +250,28 @@ export class EventLoader { private _loadEvent() { eventStore.batchSet({ loaded: this.loaded, - titles: this.titles, - titlesPublic: this.titlesPublic, + eventNow: this.eventNow, + publicEventNow: this.publicEventNow, + eventNext: this.eventNext, + publicEventNext: this.publicEventNext, }); } /** - * @description loads given title (now) + * @description loads currently running events * @private * @param {object} event * @param {array} rundown */ - private _loadTitlesNow(event, rundown) { - // private title is always current + private _loadEventNow(event, rundown) { + this.eventNow = event; + // check if current is also public if (event.isPublic) { - this._loadThisTitles(event, 'now'); + this.publicEventNow = event; } else { - this._loadThisTitles(event, 'now-private'); - // assume there is no public event - this.titlesPublic.titleNow = null; - this.titlesPublic.subtitleNow = null; - this.titlesPublic.presenterNow = null; + this.publicEventNow = null; this.loaded.selectedPublicEventId = null; // if there is nothing before, return @@ -305,7 +280,8 @@ export class EventLoader { // iterate backwards to find it for (let i = this.loaded.selectedEventIndex; i >= 0; i--) { if (rundown[i].isPublic) { - this._loadThisTitles(rundown[i], 'now-public'); + this.publicEventNow = rundown[i]; + this.loaded.selectedPublicEventId = rundown[i].id; break; } } @@ -313,180 +289,44 @@ export class EventLoader { } /** - * @description look for next titles to load + * @description look for next events * @private */ - private _loadTitlesNext(rundown) { - // maybe there is nothing to load - if (this.loaded.selectedEventIndex === null) return; - - // assume there is no next event - this.titles.titleNext = null; - this.titles.subtitleNext = null; - this.titles.presenterNext = null; - this.titles.noteNext = null; + private _loadEventNext(rundown) { + // assume there are no next events + this.eventNext = null; + this.publicEventNext = null; this.loaded.nextEventId = null; - - this.titlesPublic.titleNext = null; - this.titlesPublic.subtitleNext = null; - this.titlesPublic.presenterNext = null; this.loaded.nextPublicEventId = null; + if (this.loaded.selectedEventIndex === null) return; + const numEvents = rundown.length; if (this.loaded.selectedEventIndex < numEvents - 1) { let nextPublic = false; - let nextPrivate = false; + let nextProduction = false; for (let i = this.loaded.selectedEventIndex + 1; i < numEvents; i++) { // if we have not set private - if (!nextPrivate) { - this._loadThisTitles(rundown[i], 'next-private'); - nextPrivate = true; + if (!nextProduction) { + this.eventNext = rundown[i]; + this.loaded.nextEventId = rundown[i].id; + nextProduction = true; } // if event is public if (rundown[i].isPublic) { - this._loadThisTitles(rundown[i], 'next-public'); + this.publicEventNext = rundown[i]; + this.loaded.nextPublicEventId = rundown[i].id; nextPublic = true; } // Stop if both are set - if (nextPublic && nextPrivate) break; + if (nextPublic && nextProduction) break; } } } - - /** - * @description loads given title - * @param event - * @param type - * @private - */ - private _loadThisTitles(event, type) { - if (type === 'now') { - if (event === null) { - // public - this.titlesPublic.titleNow = null; - this.titlesPublic.subtitleNow = null; - this.titlesPublic.presenterNow = null; - this.titlesPublic.noteNow = null; - this.loaded.selectedPublicEventId = null; - - // private - this.titles.titleNow = null; - this.titles.subtitleNow = null; - this.titles.presenterNow = null; - this.titles.noteNow = null; - this.loaded.selectedEventId = null; - } else { - // public - this.titlesPublic.titleNow = event.title; - this.titlesPublic.subtitleNow = event.subtitle; - this.titlesPublic.presenterNow = event.presenter; - this.titlesPublic.noteNow = event.note; - this.loaded.selectedPublicEventId = event.id; - - // private - this.titles.titleNow = event.title; - this.titles.subtitleNow = event.subtitle; - this.titles.presenterNow = event.presenter; - this.titles.noteNow = event.note; - this.loaded.selectedEventId = event.id; - } - } else if (type === 'now-public') { - if (event === null) { - this.titlesPublic.titleNow = null; - this.titlesPublic.subtitleNow = null; - this.titlesPublic.presenterNow = null; - this.titlesPublic.noteNow = null; - this.loaded.selectedPublicEventId = null; - } else { - this.titlesPublic.titleNow = event.title; - this.titlesPublic.subtitleNow = event.subtitle; - this.titlesPublic.presenterNow = event.presenter; - this.titlesPublic.noteNow = event.note; - this.loaded.selectedPublicEventId = event.id; - } - } else if (type === 'now-private') { - if (event === null) { - this.titles.titleNow = null; - this.titles.subtitleNow = null; - this.titles.presenterNow = null; - this.titles.noteNow = null; - this.loaded.selectedEventId = null; - } else { - this.titles.titleNow = event.title; - this.titles.subtitleNow = event.subtitle; - this.titles.presenterNow = event.presenter; - this.titles.noteNow = event.note; - this.loaded.selectedEventId = event.id; - } - } - - // next, load to both public and private - else if (type === 'next') { - if (event === null) { - // public - this.titlesPublic.titleNext = null; - this.titlesPublic.subtitleNext = null; - this.titlesPublic.presenterNext = null; - this.titlesPublic.noteNext = null; - this.loaded.nextPublicEventId = null; - - // private - this.titles.titleNext = null; - this.titles.subtitleNext = null; - this.titles.presenterNext = null; - this.titles.noteNext = null; - this.loaded.nextEventId = null; - } else { - // public - this.titlesPublic.titleNext = event.title; - this.titlesPublic.subtitleNext = event.subtitle; - this.titlesPublic.presenterNext = event.presenter; - this.titlesPublic.noteNext = event.note; - this.loaded.nextPublicEventId = event.id; - - // private - this.titles.titleNext = event.title; - this.titles.subtitleNext = event.subtitle; - this.titles.presenterNext = event.presenter; - this.titles.noteNext = event.note; - this.loaded.nextEventId = event.id; - } - } else if (type === 'next-public') { - if (event === null) { - this.titlesPublic.titleNext = null; - this.titlesPublic.subtitleNext = null; - this.titlesPublic.presenterNext = null; - this.titlesPublic.noteNext = null; - this.loaded.nextPublicEventId = null; - } else { - this.titlesPublic.titleNext = event.title; - this.titlesPublic.subtitleNext = event.subtitle; - this.titlesPublic.presenterNext = event.presenter; - this.titlesPublic.noteNext = event.note; - this.loaded.nextPublicEventId = event.id; - } - } else if (type === 'next-private') { - if (event === null) { - this.titles.titleNext = null; - this.titles.subtitleNext = null; - this.titles.presenterNext = null; - this.titles.noteNext = null; - this.loaded.nextEventId = null; - } else { - this.titles.titleNext = event.title; - this.titles.subtitleNext = event.subtitle; - this.titles.presenterNext = event.presenter; - this.titles.noteNext = event.note; - this.loaded.nextEventId = event.id; - } - } else { - throw new Error(`Unhandled title type: ${type}`); - } - } } export const eventLoader = new EventLoader(); diff --git a/apps/server/src/services/rundown-service/RundownService.ts b/apps/server/src/services/rundown-service/RundownService.ts index 56ee64473..7d1762506 100644 --- a/apps/server/src/services/rundown-service/RundownService.ts +++ b/apps/server/src/services/rundown-service/RundownService.ts @@ -116,8 +116,8 @@ export function updateTimer(affectedIds?: string[]) { if (safeOption) { eventLoader.reset(); - const { loadedEvent } = eventLoader.loadById(runningEventId) || {}; - eventTimer.hotReload(loadedEvent); + const { eventNow } = eventLoader.loadById(runningEventId) || {}; + eventTimer.hotReload(eventNow); return true; } @@ -133,9 +133,9 @@ export function updateTimer(affectedIds?: string[]) { eventTimer.roll(currentEvent, nextEvent); } } else { - const { loadedEvent } = eventLoader.loadById(runningEventId) || {}; - if (loadedEvent) { - eventTimer.hotReload(loadedEvent); + const { eventNow } = eventLoader.loadById(runningEventId) || {}; + if (eventNow) { + eventTimer.hotReload(eventNow); } else { eventTimer.stop(); } @@ -144,8 +144,8 @@ export function updateTimer(affectedIds?: string[]) { } if (isNext) { - const { loadedEvent } = eventLoader.loadById(runningEventId) || {}; - eventTimer.hotReload(loadedEvent); + const { eventNow } = eventLoader.loadById(runningEventId) || {}; + eventTimer.hotReload(eventNow); return true; } return false; diff --git a/apps/server/src/stores/EventStore.ts b/apps/server/src/stores/EventStore.ts index 933d8ff87..19e59ded9 100644 --- a/apps/server/src/stores/EventStore.ts +++ b/apps/server/src/stores/EventStore.ts @@ -56,8 +56,10 @@ export const eventStore = { * - Message Service lowerMessage * - Message Service onAir * - Event Loader loaded - * - Event Loader titles - * - Event Loader titlesPublic + * - Event Loader eventNow + * - Event Loader publicEventNow + * - Event Loader eventNext + * - Event Loader publicEventNext */ export const getInitialPayload = () => ({ @@ -68,6 +70,8 @@ export const getInitialPayload = () => ({ lowerMessage: messageService.lowerMessage, onAir: messageService.onAir, loaded: eventLoader.loaded, - titles: eventLoader.titles, - titlesPublic: eventLoader.titlesPublic, + eventNow: eventLoader.eventNow, + publicEventNow: eventLoader.publicEventNow, + eventNext: eventLoader.eventNext, + publicEventNext: eventLoader.publicEventNext, }); diff --git a/packages/types/src/definitions/runtime/RuntimeStore.type.ts b/packages/types/src/definitions/runtime/RuntimeStore.type.ts index 591082f0e..d9f68cbba 100644 --- a/packages/types/src/definitions/runtime/RuntimeStore.type.ts +++ b/packages/types/src/definitions/runtime/RuntimeStore.type.ts @@ -1,8 +1,8 @@ import { Playback } from './Playback.type.js'; import { Message, TimerMessage } from './MessageControl.type.js'; import { TimerState } from './TimerState.type.js'; -import { TitleBlock } from './TitleBlock.type.js'; import { Loaded } from './Playlist.type.js'; +import { OntimeEvent } from '../core/OntimeEvent.type.js'; export type RuntimeStore = { // timer service @@ -17,6 +17,8 @@ export type RuntimeStore = { // event loader loaded: Loaded; - titles: TitleBlock; - titlesPublic: TitleBlock; + eventNow: OntimeEvent | null; + publicEventNow: OntimeEvent | null; + eventNext: OntimeEvent | null; + publicEventNext: OntimeEvent | null; }; diff --git a/packages/types/src/definitions/runtime/TitleBlock.type.ts b/packages/types/src/definitions/runtime/TitleBlock.type.ts deleted file mode 100644 index bf8836f98..000000000 --- a/packages/types/src/definitions/runtime/TitleBlock.type.ts +++ /dev/null @@ -1,10 +0,0 @@ -export type TitleBlock = { - titleNow: string | null; - subtitleNow: string | null; - presenterNow: string | null; - noteNow: string | null; - titleNext: string | null; - subtitleNext: string | null; - presenterNext: string | null; - noteNext: string | null; -}; diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 65efda3ea..33a94129d 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -5,8 +5,8 @@ export type { DatabaseModel } from './definitions/DataModel.type.js'; export { EndAction } from './definitions/EndAction.type.js'; export { type OntimeBaseEvent, - type OntimeBlock, type OntimeDelay, + type OntimeBlock, type OntimeEvent, SupportedEvent, } from './definitions/core/OntimeEvent.type.js'; @@ -42,7 +42,6 @@ export type { Message, TimerMessage } from './definitions/runtime/MessageControl export type { Loaded } from './definitions/runtime/Playlist.type.js'; export type { RuntimeStore } from './definitions/runtime/RuntimeStore.type.js'; export type { TimerState } from './definitions/runtime/TimerState.type.js'; -export type { TitleBlock } from './definitions/runtime/TitleBlock.type.js'; // CLIENT