From f708dc0bc4f942c73027199309e81c0ab2059e4b Mon Sep 17 00:00:00 2001 From: Alex Christoffer Rasmussen Date: Thu, 15 Aug 2024 16:25:29 +0200 Subject: [PATCH] Fix current block (#1170) * pass full rundown to loadNow * remove prevBlock from state * refactor: simplify load logic (#1174) * send clock to client on block start to avoid it showing -1 in the client --------- Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com> --- .../runtime-service/RuntimeService.ts | 7 ++- apps/server/src/stores/runtimeState.ts | 48 +++++++++++++------ .../utils/src/rundown-utils/rundownUtils.ts | 15 +++--- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/apps/server/src/services/runtime-service/RuntimeService.ts b/apps/server/src/services/runtime-service/RuntimeService.ts index 12b03ee44..ebec7c298 100644 --- a/apps/server/src/services/runtime-service/RuntimeService.ts +++ b/apps/server/src/services/runtime-service/RuntimeService.ts @@ -69,7 +69,6 @@ class RuntimeService { @broadcastResult private checkTimerUpdate({ hasTimerFinished, hasSecondaryTimerFinished }: runtimeState.UpdateResult) { const newState = runtimeState.getState(); - // 1. find if we need to dispatch integrations related to the phase const timerPhaseChanged = RuntimeService.previousState.timer?.phase !== newState.timer.phase; if (timerPhaseChanged) { @@ -664,7 +663,6 @@ function broadcastResult(_target: any, _propertyKey: string, descriptor: Propert // we do the comparison by explicitly for each property // to apply custom logic for different datasets - const shouldUpdateClock = getShouldClockUpdate(RuntimeService.previousClockUpdate, state.clock); const shouldForceTimerUpdate = getForceUpdate(RuntimeService.previousTimerUpdate, state.clock); const shouldUpdateTimer = shouldForceTimerUpdate || getShouldTimerUpdate(RuntimeService.previousTimerValue, state.timer.current); @@ -698,11 +696,16 @@ function broadcastResult(_target: any, _propertyKey: string, descriptor: Propert updateEventIfChanged('eventNext', state); updateEventIfChanged('publicEventNext', state); + let syncBlockStartAt = false; + if (!deepEqual(RuntimeService?.previousState.currentBlock, state.currentBlock)) { eventStore.set('currentBlock', state.currentBlock); RuntimeService.previousState.currentBlock = { ...state.currentBlock }; + syncBlockStartAt = true; } + const shouldUpdateClock = syncBlockStartAt || getShouldClockUpdate(RuntimeService.previousClockUpdate, state.clock); + if (shouldUpdateClock) { RuntimeService.previousClockUpdate = state.clock; eventStore.set('clock', state.clock); diff --git a/apps/server/src/stores/runtimeState.ts b/apps/server/src/stores/runtimeState.ts index 3802f605a..5eba5af9a 100644 --- a/apps/server/src/stores/runtimeState.ts +++ b/apps/server/src/stores/runtimeState.ts @@ -65,7 +65,6 @@ export type RuntimeState = { pausedAt: MaybeNumber; secondaryTarget: MaybeNumber; }; - _prevCurrentBlock: CurrentBlockState; }; const runtimeState: RuntimeState = { @@ -86,10 +85,6 @@ const runtimeState: RuntimeState = { pausedAt: null, secondaryTarget: null, }, - _prevCurrentBlock: { - block: null, - startedAt: null, - }, }; export function getState(): Readonly { @@ -111,7 +106,6 @@ export function clear() { runtimeState.publicEventNow = null; runtimeState.eventNext = null; - runtimeState._prevCurrentBlock = { ...runtimeState.currentBlock }; runtimeState.currentBlock.block = null; runtimeState.currentBlock.startedAt = null; @@ -172,7 +166,10 @@ export function load( rundown: OntimeRundown, initialData?: Partial, ): boolean { + // we need to persist the current block state across loads + const prevCurrentBlock = { ...runtimeState.currentBlock }; clear(); + runtimeState.currentBlock = prevCurrentBlock; // filter rundown const timedEvents = filterTimedEvents(rundown); @@ -185,6 +182,7 @@ export function load( // load events in memory along with their data loadNow(timedEvents, eventIndex); loadNext(timedEvents, eventIndex); + loadBlock(rundown); // update state runtimeState.timer.playback = Playback.Armed; @@ -215,20 +213,12 @@ export function loadNow(timedEvents: OntimeEvent[], eventIndex: MaybeNumber = ru // reset the state to indicate there is no selection runtimeState.runtime.selectedEventIndex = null; runtimeState.eventNow = null; - runtimeState.currentBlock.block = null; - runtimeState.currentBlock.startedAt = null; return; } const event = timedEvents[eventIndex] as PlayableEvent; runtimeState.runtime.selectedEventIndex = eventIndex; runtimeState.eventNow = event; - runtimeState.currentBlock.block = getRelevantBlock(timedEvents, event.id); - - // if we are still in the same block keep the startedAt time - if (runtimeState._prevCurrentBlock.block?.id === runtimeState.currentBlock.block?.id) { - runtimeState.currentBlock.startedAt = runtimeState._prevCurrentBlock.startedAt; - } // check if current is also public if (event.isPublic) { @@ -356,7 +346,6 @@ export function updateLoaded(event?: PlayableEvent): string | undefined { runtimeState.timer.elapsed = null; runtimeState.timer.expectedFinish = getExpectedFinish(runtimeState); - runtimeState.currentBlock.startedAt = null; return runtimeState.eventNow.id; } @@ -368,6 +357,7 @@ export function updateAll(rundown: OntimeRundown) { loadNow(timedEvents); loadNext(timedEvents); updateLoaded(runtimeState.eventNow ?? undefined); + loadBlock(rundown); } export function start(state: RuntimeState = runtimeState): boolean { @@ -391,6 +381,7 @@ export function start(state: RuntimeState = runtimeState): boolean { state.timer.startedAt = state.clock; } + // update block start time if (state.currentBlock.startedAt === null) { state.currentBlock.startedAt = state.clock; } @@ -582,6 +573,9 @@ export function roll(rundown: OntimeRundown): { eventId: MaybeString; didStart: runtimeState.timer.startedAt = runtimeState.clock; // update runtime + if (runtimeState.currentBlock.startedAt === null) { + runtimeState.currentBlock.startedAt = runtimeState.clock; + } if (!runtimeState.runtime.actualStart) { runtimeState.runtime.actualStart = runtimeState.clock; } @@ -600,12 +594,17 @@ export function roll(rundown: OntimeRundown): { eventId: MaybeString; didStart: throw new Error('No playable events found'); } + // we need to persist the current block state across loads + const prevCurrentBlock = { ...runtimeState.currentBlock }; clear(); + runtimeState.currentBlock = prevCurrentBlock; + const { index, isPending } = loadRoll(timedEvents, runtimeState.clock); // load events in memory along with their data loadNow(timedEvents, index); loadNext(timedEvents, index); + loadBlock(rundown); // update roll state runtimeState.timer.playback = Playback.Roll; @@ -656,3 +655,22 @@ export function roll(rundown: OntimeRundown): { eventId: MaybeString; didStart: runtimeState.runtime.actualStart = runtimeState.clock; return { eventId: runtimeState.eventNow.id, didStart: true }; } + +function loadBlock(rundown: OntimeRundown) { + if (runtimeState.eventNow === null) { + // we need a loaded event to have a block + runtimeState.currentBlock.block = null; + runtimeState.currentBlock.startedAt = null; + return; + } + + const newCurrentBlock = getRelevantBlock(rundown, runtimeState.eventNow.id); + + // update time only if the block has changed + if (newCurrentBlock === null || newCurrentBlock.id !== runtimeState.currentBlock.block?.id) { + runtimeState.currentBlock.startedAt = null; + } + + // update the block anyway + runtimeState.currentBlock.block = newCurrentBlock === null ? null : { ...newCurrentBlock }; +} diff --git a/packages/utils/src/rundown-utils/rundownUtils.ts b/packages/utils/src/rundown-utils/rundownUtils.ts index 05e8935ad..8dc4b7056 100644 --- a/packages/utils/src/rundown-utils/rundownUtils.ts +++ b/packages/utils/src/rundown-utils/rundownUtils.ts @@ -303,20 +303,21 @@ export function getEventWithId(rundown: OntimeRundown, id: string): OntimeRundow * Gets relevant block element for a given ID */ export function getRelevantBlock(rundown: OntimeRundown, currentId: string): OntimeBlock | null { - let inBlock = false; + let foundCurrentEvent = false; // Iterate backwards through the rundown to find the current event for (let i = rundown.length - 1; i >= 0; i--) { const entry = rundown[i]; - if (entry.id === currentId) { - //set the flag when the current event is found - inBlock = true; + if (!foundCurrentEvent && entry.id === currentId) { + // set the flag when the current event is found + foundCurrentEvent = true; + continue; } - //the first block before the current event is the relevant one - if (inBlock && isOntimeBlock(entry)) { + // the first block before the current event is the relevant one + if (foundCurrentEvent && isOntimeBlock(entry)) { return entry; } } - //no blocks exist before current event + // no blocks exist before current event return null; }