diff --git a/apps/server/src/stores/__tests__/runtimeState.test.ts b/apps/server/src/stores/__tests__/runtimeState.test.ts index f607ddcb0..2abae6b89 100644 --- a/apps/server/src/stores/__tests__/runtimeState.test.ts +++ b/apps/server/src/stores/__tests__/runtimeState.test.ts @@ -1,7 +1,18 @@ -import { PlayableEvent, Playback, TimerPhase } from 'ontime-types'; +import { OntimeRundown, PlayableEvent, Playback, SupportedEvent, TimerPhase } from 'ontime-types'; import { deepmerge } from 'ontime-utils'; -import { type RuntimeState, addTime, clear, getState, load, pause, roll, start, stop } from '../runtimeState.js'; +import { + type RuntimeState, + addTime, + clear, + getState, + load, + loadBlock, + pause, + roll, + start, + stop, +} from '../runtimeState.js'; import { initRundown } from '../../services/rundown-service/RundownService.js'; const mockEvent = { @@ -328,3 +339,125 @@ describe('roll mode', () => { }); }); }); + +describe('loadBlock', () => { + test('from no-block to a block will clear startedAt', () => { + const rundown = [ + { id: '0', type: SupportedEvent.Event }, + { id: '1', type: SupportedEvent.Block }, + { id: '2', type: SupportedEvent.Event }, + { id: '3', type: SupportedEvent.Block }, + { id: '4', type: SupportedEvent.Event }, + ] as OntimeRundown; + + const state = { + currentBlock: { + block: null, + startedAt: 123, + }, + eventNow: rundown[2], + } as RuntimeState; + + loadBlock(rundown, state); + + expect(state).toMatchObject({ + currentBlock: { block: rundown[1], startedAt: null }, + eventNow: rundown[2], + }); + }); + + test('from block to a different block will clear startedAt', () => { + const rundown = [ + { id: '0', type: SupportedEvent.Event }, + { id: '1', type: SupportedEvent.Block }, + { id: '2', type: SupportedEvent.Event }, + { id: '3', type: SupportedEvent.Block }, + { id: '4', type: SupportedEvent.Event }, + ] as OntimeRundown; + + const state = { + currentBlock: { + block: rundown[1], + startedAt: 123, + }, + eventNow: rundown[4], + } as RuntimeState; + + loadBlock(rundown, state); + + expect(state).toMatchObject({ + currentBlock: { block: rundown[3], startedAt: null }, + eventNow: rundown[4], + }); + }); + + test('from block to a no-block will clear startedAt', () => { + const rundown = [ + { id: '0', type: SupportedEvent.Event }, + { id: '1', type: SupportedEvent.Block }, + { id: '2', type: SupportedEvent.Event }, + { id: '3', type: SupportedEvent.Block }, + { id: '4', type: SupportedEvent.Event }, + ] as OntimeRundown; + + const state = { + currentBlock: { + block: rundown[1], + startedAt: 123, + }, + eventNow: rundown[0], + } as RuntimeState; + + loadBlock(rundown, state); + + expect(state).toMatchObject({ + currentBlock: { block: null, startedAt: null }, + eventNow: rundown[0], + }); + }); + + test('from block to same block will keep startedAt', () => { + const rundown = [ + { id: '0', type: SupportedEvent.Block }, + { id: '1', type: SupportedEvent.Event }, + { id: '2', type: SupportedEvent.Event }, + ] as OntimeRundown; + + const state = { + currentBlock: { + block: rundown[0], + startedAt: 123, + }, + eventNow: rundown[2], + } as RuntimeState; + + loadBlock(rundown, state); + + expect(state).toMatchObject({ + currentBlock: { block: rundown[0], startedAt: 123 }, + eventNow: rundown[2], + }); + }); + + test('from no-block to no-block will keep startedAt', () => { + const rundown = [ + { id: '0', type: SupportedEvent.Event }, + { id: '1', type: SupportedEvent.Event }, + ] as OntimeRundown; + + const state = { + currentBlock: { + block: null, + startedAt: 123, + }, + eventNow: rundown[0], + } as RuntimeState; + + loadBlock(rundown, state); + + expect(state).toMatchObject({ + currentBlock: { block: null, startedAt: 123 }, + eventNow: rundown[0], + }); + }); +}); diff --git a/apps/server/src/stores/runtimeState.ts b/apps/server/src/stores/runtimeState.ts index 4db8dd88a..2173b7538 100644 --- a/apps/server/src/stores/runtimeState.ts +++ b/apps/server/src/stores/runtimeState.ts @@ -674,6 +674,11 @@ export function roll(rundown: OntimeRundown, offset = 0): { eventId: MaybeString // there is something to run, load event + // update runtime + if (runtimeState.currentBlock.startedAt === null) { + runtimeState.currentBlock.startedAt = runtimeState.clock; + } + // event will finish on time // account for event that finishes the day after const endTime = @@ -696,26 +701,25 @@ export function roll(rundown: OntimeRundown, offset = 0): { eventId: MaybeString return { eventId: runtimeState.eventNow.id, didStart: true }; } -function loadBlock(rundown: OntimeRundown) { - if (runtimeState.eventNow === null) { +/** + * handle block loading, not for use outside of runtimeState + * @param rundown + */ +export function loadBlock(rundown: OntimeRundown, state = runtimeState) { + if (state.eventNow === null) { // we need a loaded event to have a block - runtimeState.currentBlock.block = null; - runtimeState.currentBlock.startedAt = null; + state.currentBlock.block = null; + state.currentBlock.startedAt = null; return; } - const newCurrentBlock = getPreviousBlock(rundown, runtimeState.eventNow.id); - - // test all block change posibiletys - const formNoBlockToBlock = runtimeState.currentBlock.block === null && newCurrentBlock !== null; - const formBlockToNoBlock = runtimeState.currentBlock.block !== null && newCurrentBlock === null; - const formBlockToNewBlock = runtimeState.currentBlock.block?.id !== newCurrentBlock?.id; + const newCurrentBlock = getPreviousBlock(rundown, state.eventNow.id); // update time only if the block has changed - if (formNoBlockToBlock || formBlockToNoBlock || formBlockToNewBlock) { - runtimeState.currentBlock.startedAt = null; + if (state.currentBlock.block?.id !== newCurrentBlock?.id) { + state.currentBlock.startedAt = null; } // update the block anyway - runtimeState.currentBlock.block = newCurrentBlock === null ? null : { ...newCurrentBlock }; + state.currentBlock.block = newCurrentBlock === null ? null : { ...newCurrentBlock }; }