mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-17 21:24:11 +00:00
refactor: maintain flat orders
This commit is contained in:
@@ -13,6 +13,7 @@ const cachedRundownPlaceholder: Rundown = {
|
|||||||
id: 'default',
|
id: 'default',
|
||||||
title: '',
|
title: '',
|
||||||
order: [],
|
order: [],
|
||||||
|
flatOrder: [],
|
||||||
entries: {},
|
entries: {},
|
||||||
revision: -1,
|
revision: -1,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ describe('aggregateRundowns()', () => {
|
|||||||
title: '',
|
title: '',
|
||||||
revision: 0,
|
revision: 0,
|
||||||
order: ['1', '2'],
|
order: ['1', '2'],
|
||||||
|
flatOrder: ['1', '2'],
|
||||||
entries: {
|
entries: {
|
||||||
'1': { id: '1' } as OntimeEntry,
|
'1': { id: '1' } as OntimeEntry,
|
||||||
'2': { id: '2' } as OntimeEntry,
|
'2': { id: '2' } as OntimeEntry,
|
||||||
@@ -32,6 +33,7 @@ describe('aggregateRundowns()', () => {
|
|||||||
title: '',
|
title: '',
|
||||||
revision: 0,
|
revision: 0,
|
||||||
order: ['3', '4'],
|
order: ['3', '4'],
|
||||||
|
flatOrder: ['3', '4'],
|
||||||
entries: {
|
entries: {
|
||||||
'3': { id: '3' } as OntimeEntry,
|
'3': { id: '3' } as OntimeEntry,
|
||||||
'4': { id: '4' } as OntimeEntry,
|
'4': { id: '4' } as OntimeEntry,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export const defaultRundown: Rundown = {
|
|||||||
id: 'default',
|
id: 'default',
|
||||||
title: 'Default',
|
title: 'Default',
|
||||||
order: [],
|
order: [],
|
||||||
|
flatOrder: [],
|
||||||
entries: {},
|
entries: {},
|
||||||
revision: 0,
|
revision: 0,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,6 +19,25 @@ export const demoDb: DatabaseModel = {
|
|||||||
'bab4a',
|
'bab4a',
|
||||||
'd3eb1',
|
'd3eb1',
|
||||||
],
|
],
|
||||||
|
flatOrder: [
|
||||||
|
'block',
|
||||||
|
'32d31',
|
||||||
|
'21cd2',
|
||||||
|
'0b371',
|
||||||
|
'3cd28',
|
||||||
|
'e457f',
|
||||||
|
'01e85',
|
||||||
|
'1c420',
|
||||||
|
'b7737',
|
||||||
|
'd3a80',
|
||||||
|
'8276c',
|
||||||
|
'2340b',
|
||||||
|
'cb90b',
|
||||||
|
'503c4',
|
||||||
|
'5e965',
|
||||||
|
'bab4a',
|
||||||
|
'd3eb1',
|
||||||
|
],
|
||||||
entries: {
|
entries: {
|
||||||
block: {
|
block: {
|
||||||
type: SupportedEvent.Block,
|
type: SupportedEvent.Block,
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export async function addEvent(eventData: EventPostPayload): Promise<OntimeEntry
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. if the user provides a parent (inside a group), we make sure it exists
|
// 2. if the user provides a parent (inside a group), we make sure it exists
|
||||||
let parent: EntryId | undefined;
|
let parent: EntryId | null = null;
|
||||||
if ('parent' in eventData && eventData.parent != null) {
|
if ('parent' in eventData && eventData.parent != null) {
|
||||||
if (!cache.hasId(eventData.parent)) {
|
if (!cache.hasId(eventData.parent)) {
|
||||||
throw new Error(`Parent event with ID ${eventData.parent} not found`);
|
throw new Error(`Parent event with ID ${eventData.parent} not found`);
|
||||||
|
|||||||
@@ -49,6 +49,19 @@ describe('generate()', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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', () => {
|
it('creates normalised versions of a given rundown', () => {
|
||||||
const rundown = makeRundown({
|
const rundown = makeRundown({
|
||||||
order: ['1', '2', '3'],
|
order: ['1', '2', '3'],
|
||||||
@@ -613,7 +626,7 @@ describe('add() mutation', () => {
|
|||||||
test('adds an event to the rundown', () => {
|
test('adds an event to the rundown', () => {
|
||||||
const mockEvent = makeOntimeEvent({ id: 'mock', cue: 'mock' });
|
const mockEvent = makeOntimeEvent({ id: 'mock', cue: 'mock' });
|
||||||
const rundown = makeRundown({});
|
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.order.length).toBe(1);
|
||||||
expect(newRundown.entries['mock']).toMatchObject(mockEvent);
|
expect(newRundown.entries['mock']).toMatchObject(mockEvent);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ export type RundownMetadata = {
|
|||||||
firstStart: MaybeNumber;
|
firstStart: MaybeNumber;
|
||||||
lastEnd: MaybeNumber;
|
lastEnd: MaybeNumber;
|
||||||
|
|
||||||
playableEventOrder: EntryId[];
|
playableEventOrder: EntryId[]; // flat order of playable events
|
||||||
timedEventOrder: EntryId[];
|
timedEventOrder: EntryId[]; // flat order of timed events
|
||||||
flatEventOrder: EntryId[];
|
flatEntryOrder: EntryId[]; // flat order of entries
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keep track of which custom fields are used.
|
* Keep track of which custom fields are used.
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ let rundownMetadata: RundownMetadata = {
|
|||||||
|
|
||||||
playableEventOrder: [],
|
playableEventOrder: [],
|
||||||
timedEventOrder: [],
|
timedEventOrder: [],
|
||||||
flatEventOrder: [],
|
flatEntryOrder: [],
|
||||||
|
|
||||||
assignedCustomFields: {},
|
assignedCustomFields: {},
|
||||||
};
|
};
|
||||||
@@ -172,7 +172,7 @@ export function updateCache() {
|
|||||||
const { previousEvent, latestEvent, ...metadata } = processedData;
|
const { previousEvent, latestEvent, ...metadata } = processedData;
|
||||||
currentRundown.entries = metadata.entries;
|
currentRundown.entries = metadata.entries;
|
||||||
currentRundown.order = metadata.order;
|
currentRundown.order = metadata.order;
|
||||||
currentRundown.flatOrder = metadata.flatEventOrder;
|
currentRundown.flatOrder = metadata.flatEntryOrder;
|
||||||
rundownMetadata = metadata;
|
rundownMetadata = metadata;
|
||||||
clearIsStale();
|
clearIsStale();
|
||||||
customFieldChangelog = {};
|
customFieldChangelog = {};
|
||||||
@@ -320,7 +320,7 @@ export function mutateCache<T extends object>(mutation: MutatingFn<T>) {
|
|||||||
return scopedMutation;
|
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
|
* Add entry to rundown
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ export function makeRundownMetadata(customFields: CustomFields, customFieldChang
|
|||||||
assignedCustomFields: {},
|
assignedCustomFields: {},
|
||||||
playableEventOrder: [],
|
playableEventOrder: [],
|
||||||
timedEventOrder: [],
|
timedEventOrder: [],
|
||||||
flatEventOrder: [],
|
flatEntryOrder: [],
|
||||||
|
|
||||||
entries: {},
|
entries: {},
|
||||||
order: [],
|
order: [],
|
||||||
@@ -192,12 +192,10 @@ function processEntry<T extends OntimeEntry>(
|
|||||||
): { processedData: ProcessedRundownMetadata; processedEntry: T } {
|
): { processedData: ProcessedRundownMetadata; processedEntry: T } {
|
||||||
const processedData = { ...rundownMetadata };
|
const processedData = { ...rundownMetadata };
|
||||||
const currentEntry = structuredClone(entry);
|
const currentEntry = structuredClone(entry);
|
||||||
processedData.flatEventOrder.push(currentEntry.id);
|
processedData.flatEntryOrder.push(currentEntry.id);
|
||||||
|
|
||||||
if (isOntimeEvent(currentEntry)) {
|
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)
|
* 1.Checks that link can be established (ie, events exist and are valid)
|
||||||
@@ -227,9 +225,7 @@ function processEntry<T extends OntimeEntry>(
|
|||||||
|
|
||||||
// update rundown metadata, it only concerns playable events
|
// update rundown metadata, it only concerns playable events
|
||||||
if (isPlayableEvent(currentEntry)) {
|
if (isPlayableEvent(currentEntry)) {
|
||||||
if (!childOfBlock) {
|
processedData.playableEventOrder.push(currentEntry.id);
|
||||||
processedData.playableEventOrder.push(currentEntry.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// first start is always the first event
|
// first start is always the first event
|
||||||
if (processedData.firstStart === null) {
|
if (processedData.firstStart === null) {
|
||||||
|
|||||||
@@ -122,23 +122,23 @@ export function findPrevious(currentEventId?: string): OntimeEvent | undefined {
|
|||||||
* finds the next event
|
* finds the next event
|
||||||
*/
|
*/
|
||||||
export function findNext(currentEventId?: string): PlayableEvent | undefined {
|
export function findNext(currentEventId?: string): PlayableEvent | undefined {
|
||||||
const { playableEventsOrder } = cache.getEventOrder();
|
const { playableEventOrder } = cache.getMetadata();
|
||||||
|
|
||||||
if (!playableEventsOrder.length) {
|
if (!playableEventOrder.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is no event running, go to first
|
// if there is no event running, go to first
|
||||||
if (!currentEventId) {
|
if (!currentEventId) {
|
||||||
return getFirstPlayable(playableEventsOrder);
|
return getFirstPlayable(playableEventOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentIndex = playableEventsOrder.findIndex((eventId) => eventId === currentEventId);
|
const currentIndex = playableEventOrder.findIndex((eventId) => eventId === currentEventId);
|
||||||
const newIndex = Math.min(currentIndex + 1, playableEventsOrder.length - 1);
|
const newIndex = Math.min(currentIndex + 1, playableEventOrder.length - 1);
|
||||||
const nextEventId = playableEventsOrder.at(newIndex);
|
const nextEventId = playableEventOrder.at(newIndex);
|
||||||
|
|
||||||
if (!nextEventId) {
|
if (!nextEventId) {
|
||||||
return getFirstPlayable(playableEventsOrder);
|
return getFirstPlayable(playableEventOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
return getEntryWithId(nextEventId) as PlayableEvent | undefined;
|
return getEntryWithId(nextEventId) as PlayableEvent | undefined;
|
||||||
@@ -176,7 +176,7 @@ export function getRundownOrThrow(rundowns: ProjectRundowns, rundownId: string):
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getInsertionPosition(
|
export function getInsertionPosition(
|
||||||
parentId?: EntryId,
|
parentId: EntryId | null,
|
||||||
afterId?: EntryId,
|
afterId?: EntryId,
|
||||||
beforeId?: EntryId,
|
beforeId?: EntryId,
|
||||||
): { atIndex: number; afterId: EntryId | undefined } {
|
): { atIndex: number; afterId: EntryId | undefined } {
|
||||||
@@ -202,7 +202,7 @@ export function getInsertionPosition(
|
|||||||
afterId: undefined,
|
afterId: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
function selectOrderList(parentId?: EntryId) {
|
function selectOrderList(parentId: EntryId | null) {
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
return (getEntryWithId(parentId) as OntimeBlock).events;
|
return (getEntryWithId(parentId) as OntimeBlock).events;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ describe('parseRundown()', () => {
|
|||||||
id: '',
|
id: '',
|
||||||
title: '',
|
title: '',
|
||||||
order: ['1', '2', '3', '4'],
|
order: ['1', '2', '3', '4'],
|
||||||
|
flatOrder: ['1', '2', '3', '4'],
|
||||||
entries: {
|
entries: {
|
||||||
'1': { id: '1', type: SupportedEvent.Event, title: 'test', skip: false } as OntimeEvent, // OK
|
'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
|
'2': { id: '1', type: SupportedEvent.Block, title: 'test 2', skip: false } as OntimeBlock, // duplicate ID
|
||||||
@@ -84,6 +85,7 @@ describe('parseRundown()', () => {
|
|||||||
id: '',
|
id: '',
|
||||||
title: '',
|
title: '',
|
||||||
order: ['1', '2'],
|
order: ['1', '2'],
|
||||||
|
flatOrder: ['1', '2'],
|
||||||
entries: {
|
entries: {
|
||||||
// @ts-expect-error -- testing external data which could be incorrect
|
// @ts-expect-error -- testing external data which could be incorrect
|
||||||
'1': { id: '1', type: SupportedEvent.Event, cue: 101 } as OntimeEvent,
|
'1': { id: '1', type: SupportedEvent.Event, cue: 101 } as OntimeEvent,
|
||||||
@@ -110,6 +112,7 @@ describe('parseRundown()', () => {
|
|||||||
id: '',
|
id: '',
|
||||||
title: '',
|
title: '',
|
||||||
order: ['1', '1'],
|
order: ['1', '1'],
|
||||||
|
flatOrder: ['1', '1'],
|
||||||
entries: {
|
entries: {
|
||||||
'1': { id: '1', type: SupportedEvent.Event } as OntimeEvent,
|
'1': { id: '1', type: SupportedEvent.Event } as OntimeEvent,
|
||||||
'2': { id: '2', type: SupportedEvent.Event } as OntimeEvent,
|
'2': { id: '2', type: SupportedEvent.Event } as OntimeEvent,
|
||||||
@@ -127,6 +130,7 @@ describe('parseRundown()', () => {
|
|||||||
id: 'test',
|
id: 'test',
|
||||||
title: '',
|
title: '',
|
||||||
order: ['1', '2'],
|
order: ['1', '2'],
|
||||||
|
flatOrder: ['1', '2'],
|
||||||
entries: {
|
entries: {
|
||||||
'1': { id: '1', type: SupportedEvent.Event } as OntimeEvent,
|
'1': { id: '1', type: SupportedEvent.Event } as OntimeEvent,
|
||||||
'2': { id: '2', type: SupportedEvent.Event } as OntimeEvent,
|
'2': { id: '2', type: SupportedEvent.Event } as OntimeEvent,
|
||||||
@@ -155,6 +159,7 @@ describe('parseRundown()', () => {
|
|||||||
id: 'test',
|
id: 'test',
|
||||||
title: '',
|
title: '',
|
||||||
order: ['1', '2', '3', '4'],
|
order: ['1', '2', '3', '4'],
|
||||||
|
flatOrder: ['1', '2', '3', '4'],
|
||||||
entries: {
|
entries: {
|
||||||
'1': { id: '1', type: SupportedEvent.Event } as OntimeEvent,
|
'1': { id: '1', type: SupportedEvent.Event } as OntimeEvent,
|
||||||
'2': { id: '2', type: SupportedEvent.Event } as OntimeEvent,
|
'2': { id: '2', type: SupportedEvent.Event } as OntimeEvent,
|
||||||
@@ -173,6 +178,7 @@ describe('parseRundown()', () => {
|
|||||||
id: 'test',
|
id: 'test',
|
||||||
title: '',
|
title: '',
|
||||||
order: ['1', '2', '3', '4'],
|
order: ['1', '2', '3', '4'],
|
||||||
|
flatOrder: ['1', '2', '3', '4'],
|
||||||
entries: {
|
entries: {
|
||||||
'1': { id: '1', type: SupportedEvent.Event } as OntimeEvent,
|
'1': { id: '1', type: SupportedEvent.Event } as OntimeEvent,
|
||||||
'2': { id: '2', type: SupportedEvent.Event } as OntimeEvent,
|
'2': { id: '2', type: SupportedEvent.Event } as OntimeEvent,
|
||||||
@@ -191,6 +197,7 @@ describe('parseRundown()', () => {
|
|||||||
id: 'test',
|
id: 'test',
|
||||||
title: '',
|
title: '',
|
||||||
order: ['block'],
|
order: ['block'],
|
||||||
|
flatOrder: ['block'],
|
||||||
entries: {
|
entries: {
|
||||||
block: makeOntimeBlock({ id: 'block', events: ['1', '2'] }),
|
block: makeOntimeBlock({ id: 'block', events: ['1', '2'] }),
|
||||||
'1': makeOntimeEvent({ id: '1' }),
|
'1': makeOntimeEvent({ id: '1' }),
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ export const parseExcel = (
|
|||||||
id: generateId(),
|
id: generateId(),
|
||||||
title: sheetName,
|
title: sheetName,
|
||||||
order: [],
|
order: [],
|
||||||
|
flatOrder: [],
|
||||||
entries: {},
|
entries: {},
|
||||||
revision: 0,
|
revision: 0,
|
||||||
};
|
};
|
||||||
@@ -326,6 +327,7 @@ export const parseExcel = (
|
|||||||
event.timerType = TimerType.CountDown;
|
event.timerType = TimerType.CountDown;
|
||||||
}
|
}
|
||||||
rundown.order.push(id);
|
rundown.order.push(id);
|
||||||
|
rundown.flatOrder.push(id);
|
||||||
rundown.entries[id] = event;
|
rundown.entries[id] = event;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export function parseRundown(
|
|||||||
title: rundown.title ?? '',
|
title: rundown.title ?? '',
|
||||||
entries: {},
|
entries: {},
|
||||||
order: [],
|
order: [],
|
||||||
|
flatOrder: [],
|
||||||
revision: rundown.revision ?? 1,
|
revision: rundown.revision ?? 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -95,6 +96,7 @@ export function parseRundown(
|
|||||||
|
|
||||||
const id = entryId;
|
const id = entryId;
|
||||||
let newEvent: OntimeEvent | OntimeDelay | OntimeBlock | null;
|
let newEvent: OntimeEvent | OntimeDelay | OntimeBlock | null;
|
||||||
|
const nestedEntryIds: string[] = [];
|
||||||
|
|
||||||
if (isOntimeEvent(event)) {
|
if (isOntimeEvent(event)) {
|
||||||
newEvent = createEvent(event, eventIndex);
|
newEvent = createEvent(event, eventIndex);
|
||||||
@@ -139,6 +141,7 @@ export function parseRundown(
|
|||||||
eventIndex += 1;
|
eventIndex += 1;
|
||||||
|
|
||||||
if (newNestedEvent) {
|
if (newNestedEvent) {
|
||||||
|
nestedEntryIds.push(nestedEventId);
|
||||||
parsedRundown.entries[nestedEventId] = newNestedEvent;
|
parsedRundown.entries[nestedEventId] = newNestedEvent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,6 +165,8 @@ export function parseRundown(
|
|||||||
if (newEvent) {
|
if (newEvent) {
|
||||||
parsedRundown.entries[id] = newEvent;
|
parsedRundown.entries[id] = newEvent;
|
||||||
parsedRundown.order.push(id);
|
parsedRundown.order.push(id);
|
||||||
|
parsedRundown.flatOrder.push(id);
|
||||||
|
parsedRundown.flatOrder.push(...nestedEntryIds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user