diff --git a/apps/client/src/common/hooks-query/useRundown.ts b/apps/client/src/common/hooks-query/useRundown.ts index 9d360df0a..595cf1161 100644 --- a/apps/client/src/common/hooks-query/useRundown.ts +++ b/apps/client/src/common/hooks-query/useRundown.ts @@ -13,6 +13,7 @@ const cachedRundownPlaceholder: Rundown = { id: 'default', title: '', order: [], + flatOrder: [], entries: {}, revision: -1, }; diff --git a/apps/client/src/common/utils/__tests__/csv.test.ts b/apps/client/src/common/utils/__tests__/csv.test.ts index 837b0a0cb..2b5fbc562 100644 --- a/apps/client/src/common/utils/__tests__/csv.test.ts +++ b/apps/client/src/common/utils/__tests__/csv.test.ts @@ -22,6 +22,7 @@ describe('aggregateRundowns()', () => { title: '', revision: 0, order: ['1', '2'], + flatOrder: ['1', '2'], entries: { '1': { id: '1' } as OntimeEntry, '2': { id: '2' } as OntimeEntry, @@ -32,6 +33,7 @@ describe('aggregateRundowns()', () => { title: '', revision: 0, order: ['3', '4'], + flatOrder: ['3', '4'], entries: { '3': { id: '3' } as OntimeEntry, '4': { id: '4' } as OntimeEntry, diff --git a/apps/server/src/models/dataModel.ts b/apps/server/src/models/dataModel.ts index c7157cb2f..6a46e2cb0 100644 --- a/apps/server/src/models/dataModel.ts +++ b/apps/server/src/models/dataModel.ts @@ -5,6 +5,7 @@ export const defaultRundown: Rundown = { id: 'default', title: 'Default', order: [], + flatOrder: [], entries: {}, revision: 0, }; diff --git a/apps/server/src/models/demoProject.ts b/apps/server/src/models/demoProject.ts index 6a42eb790..9939e4ad2 100644 --- a/apps/server/src/models/demoProject.ts +++ b/apps/server/src/models/demoProject.ts @@ -19,6 +19,25 @@ export const demoDb: DatabaseModel = { 'bab4a', 'd3eb1', ], + flatOrder: [ + 'block', + '32d31', + '21cd2', + '0b371', + '3cd28', + 'e457f', + '01e85', + '1c420', + 'b7737', + 'd3a80', + '8276c', + '2340b', + 'cb90b', + '503c4', + '5e965', + 'bab4a', + 'd3eb1', + ], entries: { block: { type: SupportedEvent.Block, diff --git a/apps/server/src/services/rundown-service/RundownService.ts b/apps/server/src/services/rundown-service/RundownService.ts index 004042da6..8b4921362 100644 --- a/apps/server/src/services/rundown-service/RundownService.ts +++ b/apps/server/src/services/rundown-service/RundownService.ts @@ -71,7 +71,7 @@ export async function addEvent(eventData: EventPostPayload): Promise { ); }); + it('generates metadata from given rundown', () => { + const initResult = generate(demoDb.rundowns.default, demoDb.customFields); + + expect(initResult.order.length).toBe(12); + expect(initResult.flatEntryOrder.length).toBe(17); + expect(initResult.timedEventOrder.length).toBe(14); + expect(initResult.playableEventOrder.length).toBe(14); + expect(initResult.firstStart).toBe(36000000); + expect(initResult.lastEnd).toBe(61800000); + expect(initResult.totalDays).toBe(0); + expect(initResult.totalDelay).toBe(0); + }); + it('creates normalised versions of a given rundown', () => { const rundown = makeRundown({ order: ['1', '2', '3'], @@ -613,7 +626,7 @@ describe('add() mutation', () => { test('adds an event to the rundown', () => { const mockEvent = makeOntimeEvent({ id: 'mock', cue: 'mock' }); const rundown = makeRundown({}); - const { newRundown } = add({ atIndex: 0, event: mockEvent, rundown }); + const { newRundown } = add({ atIndex: 0, entry: mockEvent, parent: null, rundown }); expect(newRundown.order.length).toBe(1); expect(newRundown.entries['mock']).toMatchObject(mockEvent); }); diff --git a/apps/server/src/services/rundown-service/rundown.types.ts b/apps/server/src/services/rundown-service/rundown.types.ts index 5cc22809b..16b8930d1 100644 --- a/apps/server/src/services/rundown-service/rundown.types.ts +++ b/apps/server/src/services/rundown-service/rundown.types.ts @@ -7,9 +7,9 @@ export type RundownMetadata = { firstStart: MaybeNumber; lastEnd: MaybeNumber; - playableEventOrder: EntryId[]; - timedEventOrder: EntryId[]; - flatEventOrder: EntryId[]; + playableEventOrder: EntryId[]; // flat order of playable events + timedEventOrder: EntryId[]; // flat order of timed events + flatEntryOrder: EntryId[]; // flat order of entries /** * Keep track of which custom fields are used. diff --git a/apps/server/src/services/rundown-service/rundownCache.ts b/apps/server/src/services/rundown-service/rundownCache.ts index c30a2cef6..c541ef0fe 100644 --- a/apps/server/src/services/rundown-service/rundownCache.ts +++ b/apps/server/src/services/rundown-service/rundownCache.ts @@ -40,7 +40,7 @@ let rundownMetadata: RundownMetadata = { playableEventOrder: [], timedEventOrder: [], - flatEventOrder: [], + flatEntryOrder: [], assignedCustomFields: {}, }; @@ -172,7 +172,7 @@ export function updateCache() { const { previousEvent, latestEvent, ...metadata } = processedData; currentRundown.entries = metadata.entries; currentRundown.order = metadata.order; - currentRundown.flatOrder = metadata.flatEventOrder; + currentRundown.flatOrder = metadata.flatEntryOrder; rundownMetadata = metadata; clearIsStale(); customFieldChangelog = {}; @@ -320,7 +320,7 @@ export function mutateCache(mutation: MutatingFn) { return scopedMutation; } -type AddArgs = MutationParams<{ atIndex: number; parent?: EntryId; entry: OntimeEntry }>; +type AddArgs = MutationParams<{ atIndex: number; parent: EntryId | null; entry: OntimeEntry }>; /** * Add entry to rundown */ diff --git a/apps/server/src/services/rundown-service/rundownCache.utils.ts b/apps/server/src/services/rundown-service/rundownCache.utils.ts index 6397edf5b..cebe7d979 100644 --- a/apps/server/src/services/rundown-service/rundownCache.utils.ts +++ b/apps/server/src/services/rundown-service/rundownCache.utils.ts @@ -158,7 +158,7 @@ export function makeRundownMetadata(customFields: CustomFields, customFieldChang assignedCustomFields: {}, playableEventOrder: [], timedEventOrder: [], - flatEventOrder: [], + flatEntryOrder: [], entries: {}, order: [], @@ -192,12 +192,10 @@ function processEntry( ): { processedData: ProcessedRundownMetadata; processedEntry: T } { const processedData = { ...rundownMetadata }; const currentEntry = structuredClone(entry); - processedData.flatEventOrder.push(currentEntry.id); + processedData.flatEntryOrder.push(currentEntry.id); if (isOntimeEvent(currentEntry)) { - if (!childOfBlock) { - processedData.timedEventOrder.push(currentEntry.id); - } + processedData.timedEventOrder.push(currentEntry.id); /** * 1.Checks that link can be established (ie, events exist and are valid) @@ -227,9 +225,7 @@ function processEntry( // update rundown metadata, it only concerns playable events if (isPlayableEvent(currentEntry)) { - if (!childOfBlock) { - processedData.playableEventOrder.push(currentEntry.id); - } + processedData.playableEventOrder.push(currentEntry.id); // first start is always the first event if (processedData.firstStart === null) { diff --git a/apps/server/src/services/rundown-service/rundownUtils.ts b/apps/server/src/services/rundown-service/rundownUtils.ts index 3e2dc0142..3e4531d66 100644 --- a/apps/server/src/services/rundown-service/rundownUtils.ts +++ b/apps/server/src/services/rundown-service/rundownUtils.ts @@ -122,23 +122,23 @@ export function findPrevious(currentEventId?: string): OntimeEvent | undefined { * finds the next event */ export function findNext(currentEventId?: string): PlayableEvent | undefined { - const { playableEventsOrder } = cache.getEventOrder(); + const { playableEventOrder } = cache.getMetadata(); - if (!playableEventsOrder.length) { + if (!playableEventOrder.length) { return; } // if there is no event running, go to first if (!currentEventId) { - return getFirstPlayable(playableEventsOrder); + return getFirstPlayable(playableEventOrder); } - const currentIndex = playableEventsOrder.findIndex((eventId) => eventId === currentEventId); - const newIndex = Math.min(currentIndex + 1, playableEventsOrder.length - 1); - const nextEventId = playableEventsOrder.at(newIndex); + const currentIndex = playableEventOrder.findIndex((eventId) => eventId === currentEventId); + const newIndex = Math.min(currentIndex + 1, playableEventOrder.length - 1); + const nextEventId = playableEventOrder.at(newIndex); if (!nextEventId) { - return getFirstPlayable(playableEventsOrder); + return getFirstPlayable(playableEventOrder); } return getEntryWithId(nextEventId) as PlayableEvent | undefined; @@ -176,7 +176,7 @@ export function getRundownOrThrow(rundowns: ProjectRundowns, rundownId: string): } export function getInsertionPosition( - parentId?: EntryId, + parentId: EntryId | null, afterId?: EntryId, beforeId?: EntryId, ): { atIndex: number; afterId: EntryId | undefined } { @@ -202,7 +202,7 @@ export function getInsertionPosition( afterId: undefined, }; - function selectOrderList(parentId?: EntryId) { + function selectOrderList(parentId: EntryId | null) { if (parentId) { return (getEntryWithId(parentId) as OntimeBlock).events; } diff --git a/apps/server/src/utils/__tests__/parserFunctions.test.ts b/apps/server/src/utils/__tests__/parserFunctions.test.ts index 223d0425a..475328d31 100644 --- a/apps/server/src/utils/__tests__/parserFunctions.test.ts +++ b/apps/server/src/utils/__tests__/parserFunctions.test.ts @@ -54,6 +54,7 @@ describe('parseRundown()', () => { id: '', title: '', order: ['1', '2', '3', '4'], + flatOrder: ['1', '2', '3', '4'], entries: { '1': { id: '1', type: SupportedEvent.Event, title: 'test', skip: false } as OntimeEvent, // OK '2': { id: '1', type: SupportedEvent.Block, title: 'test 2', skip: false } as OntimeBlock, // duplicate ID @@ -84,6 +85,7 @@ describe('parseRundown()', () => { id: '', title: '', order: ['1', '2'], + flatOrder: ['1', '2'], entries: { // @ts-expect-error -- testing external data which could be incorrect '1': { id: '1', type: SupportedEvent.Event, cue: 101 } as OntimeEvent, @@ -110,6 +112,7 @@ describe('parseRundown()', () => { id: '', title: '', order: ['1', '1'], + flatOrder: ['1', '1'], entries: { '1': { id: '1', type: SupportedEvent.Event } as OntimeEvent, '2': { id: '2', type: SupportedEvent.Event } as OntimeEvent, @@ -127,6 +130,7 @@ describe('parseRundown()', () => { id: 'test', title: '', order: ['1', '2'], + flatOrder: ['1', '2'], entries: { '1': { id: '1', type: SupportedEvent.Event } as OntimeEvent, '2': { id: '2', type: SupportedEvent.Event } as OntimeEvent, @@ -155,6 +159,7 @@ describe('parseRundown()', () => { id: 'test', title: '', order: ['1', '2', '3', '4'], + flatOrder: ['1', '2', '3', '4'], entries: { '1': { id: '1', type: SupportedEvent.Event } as OntimeEvent, '2': { id: '2', type: SupportedEvent.Event } as OntimeEvent, @@ -173,6 +178,7 @@ describe('parseRundown()', () => { id: 'test', title: '', order: ['1', '2', '3', '4'], + flatOrder: ['1', '2', '3', '4'], entries: { '1': { id: '1', type: SupportedEvent.Event } as OntimeEvent, '2': { id: '2', type: SupportedEvent.Event } as OntimeEvent, @@ -191,6 +197,7 @@ describe('parseRundown()', () => { id: 'test', title: '', order: ['block'], + flatOrder: ['block'], entries: { block: makeOntimeBlock({ id: 'block', events: ['1', '2'] }), '1': makeOntimeEvent({ id: '1' }), diff --git a/apps/server/src/utils/parser.ts b/apps/server/src/utils/parser.ts index d752162dc..d7ca96381 100644 --- a/apps/server/src/utils/parser.ts +++ b/apps/server/src/utils/parser.ts @@ -109,6 +109,7 @@ export const parseExcel = ( id: generateId(), title: sheetName, order: [], + flatOrder: [], entries: {}, revision: 0, }; @@ -326,6 +327,7 @@ export const parseExcel = ( event.timerType = TimerType.CountDown; } rundown.order.push(id); + rundown.flatOrder.push(id); rundown.entries[id] = event; }); diff --git a/apps/server/src/utils/parserFunctions.ts b/apps/server/src/utils/parserFunctions.ts index 84b1cbd0e..25ec9bcab 100644 --- a/apps/server/src/utils/parserFunctions.ts +++ b/apps/server/src/utils/parserFunctions.ts @@ -74,6 +74,7 @@ export function parseRundown( title: rundown.title ?? '', entries: {}, order: [], + flatOrder: [], revision: rundown.revision ?? 1, }; @@ -95,6 +96,7 @@ export function parseRundown( const id = entryId; let newEvent: OntimeEvent | OntimeDelay | OntimeBlock | null; + const nestedEntryIds: string[] = []; if (isOntimeEvent(event)) { newEvent = createEvent(event, eventIndex); @@ -139,6 +141,7 @@ export function parseRundown( eventIndex += 1; if (newNestedEvent) { + nestedEntryIds.push(nestedEventId); parsedRundown.entries[nestedEventId] = newNestedEvent; } } @@ -162,6 +165,8 @@ export function parseRundown( if (newEvent) { parsedRundown.entries[id] = newEvent; parsedRundown.order.push(id); + parsedRundown.flatOrder.push(id); + parsedRundown.flatOrder.push(...nestedEntryIds); } }