mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-10 09:53:48 +00:00
refactor: entry actions in cuesheet
fix: insert entry before fix: avoid double submit on enter fix: move events in the rundown fix: clone groups
This commit is contained in:
committed by
Carlos Valente
parent
8ad260d28a
commit
24a3823d3b
@@ -699,7 +699,7 @@ describe('rundownMutation.add()', () => {
|
||||
},
|
||||
});
|
||||
|
||||
rundownMutation.add(rundown, mockEvent, null, '1');
|
||||
rundownMutation.add(rundown, mockEvent, null, rundown.entries['1'] as OntimeBlock);
|
||||
|
||||
expect(rundown.order).toStrictEqual(['1']);
|
||||
expect(rundown.flatOrder).toStrictEqual(['1', 'mock', '1a']);
|
||||
@@ -717,7 +717,7 @@ describe('rundownMutation.add()', () => {
|
||||
},
|
||||
});
|
||||
|
||||
rundownMutation.add(rundown, mockEvent, '1a', '1');
|
||||
rundownMutation.add(rundown, mockEvent, '1a', rundown.entries['1'] as OntimeBlock);
|
||||
|
||||
expect(rundown.order).toStrictEqual(['1']);
|
||||
expect(rundown.flatOrder).toStrictEqual(['1', '1a', 'mock']);
|
||||
@@ -1058,6 +1058,40 @@ describe('rundownMutation.reorder()', () => {
|
||||
parent: '2',
|
||||
});
|
||||
});
|
||||
|
||||
it('moves a block (up)', () => {
|
||||
const rundown = makeRundown({
|
||||
order: ['1', '2'],
|
||||
flatOrder: ['1', '11', '2', '22'],
|
||||
entries: {
|
||||
'1': makeOntimeBlock({ id: '1', entries: ['11'] }),
|
||||
'11': makeOntimeEvent({ id: '11', parent: '1' }),
|
||||
'2': makeOntimeBlock({ id: '2', entries: ['22'] }),
|
||||
'22': makeOntimeEvent({ id: '22', parent: '2' }),
|
||||
},
|
||||
});
|
||||
|
||||
rundownMutation.reorder(rundown, rundown.entries['2'], rundown.entries['1'], 'before');
|
||||
|
||||
expect(rundown.order).toStrictEqual(['2', '1']);
|
||||
});
|
||||
|
||||
it('moves a block (down)', () => {
|
||||
const rundown = makeRundown({
|
||||
order: ['1', '2'],
|
||||
flatOrder: ['1', '11', '2', '22'],
|
||||
entries: {
|
||||
'1': makeOntimeBlock({ id: '1', entries: ['11'] }),
|
||||
'11': makeOntimeEvent({ id: '11', parent: '1' }),
|
||||
'2': makeOntimeBlock({ id: '2', entries: ['22'] }),
|
||||
'22': makeOntimeEvent({ id: '22', parent: '2' }),
|
||||
},
|
||||
});
|
||||
|
||||
rundownMutation.reorder(rundown, rundown.entries['1'], rundown.entries['2'], 'after');
|
||||
|
||||
expect(rundown.order).toStrictEqual(['2', '1']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rundownMutation.applyDelay()', () => {
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import { TimeStrategy, EndAction, TimerType, OntimeEvent } from 'ontime-types';
|
||||
import { TimeStrategy, EndAction, TimerType, OntimeEvent, OntimeBlock } from 'ontime-types';
|
||||
import { MILLIS_PER_HOUR } from 'ontime-utils';
|
||||
|
||||
import { assertType } from 'vitest';
|
||||
|
||||
import { calculateDayOffset, createEvent, deleteById, doesInvalidateMetadata, getInsertAfterId, hasChanges } from '../rundown.utils.js';
|
||||
import { makeRundown } from '../__mocks__/rundown.mocks.js';
|
||||
import {
|
||||
calculateDayOffset,
|
||||
createEvent,
|
||||
deleteById,
|
||||
doesInvalidateMetadata,
|
||||
getInsertAfterId,
|
||||
hasChanges,
|
||||
} from '../rundown.utils.js';
|
||||
import { makeOntimeBlock, makeOntimeEvent, makeRundown } from '../__mocks__/rundown.mocks.js';
|
||||
|
||||
describe('test event validator', () => {
|
||||
it('validates a good object', () => {
|
||||
@@ -217,22 +224,39 @@ describe('calculateDayOffset()', () => {
|
||||
|
||||
describe('getInsertAfterId()', () => {
|
||||
const rundown = makeRundown({
|
||||
flatOrder: ['a', 'b', 'c', 'd'],
|
||||
entries: {
|
||||
'1': makeOntimeEvent({ id: '1', parent: null }),
|
||||
'2': makeOntimeEvent({ id: '2', parent: null }),
|
||||
block: makeOntimeBlock({ id: 'block', entries: ['31', '32'] }),
|
||||
'31': makeOntimeEvent({ id: '31', parent: 'block' }),
|
||||
'32': makeOntimeEvent({ id: '32', parent: 'block' }),
|
||||
'4': makeOntimeEvent({ id: '31', parent: null }),
|
||||
},
|
||||
order: ['1', '2', 'block', '4'],
|
||||
flatOrder: ['1', '2', 'block', '31', '32', '4'],
|
||||
});
|
||||
|
||||
it('returns afterId if provided', () => {
|
||||
expect(getInsertAfterId(rundown, 'b')).toBe('b');
|
||||
expect(getInsertAfterId(rundown, null, 'b')).toBe('b');
|
||||
});
|
||||
|
||||
it('returns the previous id before beforeId if provided', () => {
|
||||
expect(getInsertAfterId(rundown, undefined, 'c')).toBe('b');
|
||||
it('returns null if neither afterId nor beforeId is provided', () => {
|
||||
expect(getInsertAfterId(rundown, null)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns undefined if neither afterId nor beforeId is provided', () => {
|
||||
expect(getInsertAfterId(rundown)).toBeNull();
|
||||
it('returns null if beforeId is not found', () => {
|
||||
expect(getInsertAfterId(rundown, null, undefined, 'z')).toBeNull();
|
||||
expect(getInsertAfterId(rundown, null, undefined, '1')).toBeNull();
|
||||
});
|
||||
|
||||
it('returns undefined if beforeId is not found', () => {
|
||||
expect(getInsertAfterId(rundown, undefined, 'z')).toBeNull();
|
||||
it('returns the previous id of an entry in the rundown', () => {
|
||||
expect(getInsertAfterId(rundown, null, undefined, '2')).toBe('1');
|
||||
expect(getInsertAfterId(rundown, null, undefined, '4')).toBe('block');
|
||||
expect(getInsertAfterId(rundown, null, undefined, 'block')).toBe('2');
|
||||
});
|
||||
|
||||
it('returns the previous id of an event in a block', () => {
|
||||
expect(getInsertAfterId(rundown, rundown.entries.block as OntimeBlock, undefined, '31')).toBeNull();
|
||||
expect(getInsertAfterId(rundown, rundown.entries.block as OntimeBlock, undefined, '32')).toBe('31');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -189,18 +189,17 @@ export function createTransaction(options: TransactionOptions): Transaction {
|
||||
* - 2a. add entry to the rundown, after a given entry
|
||||
* - 2b. add entry to the rundown, at the beginning
|
||||
*/
|
||||
function add(rundown: Rundown, entry: OntimeEntry, afterId: EntryId | null, parentId: EntryId | null): OntimeEntry {
|
||||
if (parentId) {
|
||||
function add(rundown: Rundown, entry: OntimeEntry, afterId: EntryId | null, parent: OntimeBlock | null): OntimeEntry {
|
||||
if (parent) {
|
||||
// 1. inserting an entry inside a block
|
||||
const parentBlock = rundown.entries[parentId] as OntimeBlock;
|
||||
if (afterId) {
|
||||
const atEventsIndex = parentBlock.entries.indexOf(afterId) + 1;
|
||||
const atEventsIndex = parent.entries.indexOf(afterId) + 1;
|
||||
const atFlatIndex = rundown.flatOrder.indexOf(afterId) + 1;
|
||||
parentBlock.entries = insertAtIndex(atEventsIndex, entry.id, parentBlock.entries);
|
||||
parent.entries = insertAtIndex(atEventsIndex, entry.id, parent.entries);
|
||||
rundown.flatOrder = insertAtIndex(atFlatIndex, entry.id, rundown.flatOrder);
|
||||
} else {
|
||||
parentBlock.entries = insertAtIndex(0, entry.id, parentBlock.entries);
|
||||
const atFlatIndex = rundown.flatOrder.indexOf(parentId) + 1;
|
||||
parent.entries = insertAtIndex(0, entry.id, parent.entries);
|
||||
const atFlatIndex = rundown.flatOrder.indexOf(parent.id) + 1;
|
||||
rundown.flatOrder = insertAtIndex(atFlatIndex, entry.id, rundown.flatOrder);
|
||||
}
|
||||
} else {
|
||||
@@ -282,6 +281,8 @@ function removeAll(rundown: Rundown): Rundown {
|
||||
/**
|
||||
* Reorders an entry in the rundown
|
||||
* Handle moving across order lists
|
||||
* @param order - 'before' | 'after' | 'insert' - where to add the entry, insert serves to add the entry into an empty block
|
||||
* @throws if we insert a block inside another
|
||||
*/
|
||||
function reorder(rundown: Rundown, eventFrom: OntimeEntry, eventTo: OntimeEntry, order: 'before' | 'after' | 'insert') {
|
||||
// handle moving across parents
|
||||
@@ -289,6 +290,10 @@ function reorder(rundown: Rundown, eventFrom: OntimeEntry, eventTo: OntimeEntry,
|
||||
const toParent = (() => {
|
||||
if (isOntimeBlock(eventTo)) {
|
||||
if (order === 'insert') {
|
||||
// prevent blocks from being inserted into other blocks
|
||||
if (isOntimeBlock(eventFrom)) {
|
||||
throw new Error('Cannot insert a block into another block');
|
||||
}
|
||||
return eventTo.id;
|
||||
}
|
||||
return null;
|
||||
@@ -296,7 +301,8 @@ function reorder(rundown: Rundown, eventFrom: OntimeEntry, eventTo: OntimeEntry,
|
||||
return eventTo.parent ?? null;
|
||||
})();
|
||||
|
||||
if (!isOntimeBlock(eventFrom)) {
|
||||
// always update the parent when moving entries
|
||||
if ('parent' in eventFrom) {
|
||||
eventFrom.parent = toParent;
|
||||
}
|
||||
|
||||
@@ -469,7 +475,8 @@ function clone(rundown: Rundown, entry: OntimeEntry): OntimeEntry {
|
||||
|
||||
return newBlock;
|
||||
} else {
|
||||
return add(rundown, cloneEntry(entry, getUniqueId(rundown)), entry.id, entry.parent);
|
||||
const parent: OntimeBlock | null = entry.parent ? (rundown.entries[entry.parent] as OntimeBlock) : null;
|
||||
return add(rundown, cloneEntry(entry, getUniqueId(rundown)), entry.id, parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
isOntimeBlock,
|
||||
isOntimeDelay,
|
||||
isOntimeEvent,
|
||||
OntimeBlock,
|
||||
OntimeEntry,
|
||||
OntimeEvent,
|
||||
PatchWithId,
|
||||
@@ -35,23 +36,24 @@ export async function addEntry(eventData: EventPostPayload): Promise<OntimeEntry
|
||||
}
|
||||
|
||||
// if the user provides a parent (inside a group), we make sure it exists and it is a group
|
||||
let parent: EntryId | null = null;
|
||||
let parent: OntimeBlock | null = null;
|
||||
if ('parent' in eventData && eventData.parent != null) {
|
||||
const maybeParent = rundown.entries[eventData.parent];
|
||||
if (!maybeParent || !isOntimeBlock(maybeParent)) {
|
||||
throw new Error(`Invalid parent event with ID ${eventData.parent}`);
|
||||
}
|
||||
parent = eventData.parent;
|
||||
parent = maybeParent;
|
||||
}
|
||||
|
||||
// normalise the position of the event in the rundown order
|
||||
const afterId = getInsertAfterId(rundown, eventData?.after, eventData?.before);
|
||||
const afterId = getInsertAfterId(rundown, parent, eventData?.after, eventData?.before);
|
||||
|
||||
// generate a fully formed entry from the patch
|
||||
const newEntry = generateEvent(rundown, eventData, afterId);
|
||||
|
||||
// make mutations to rundown
|
||||
rundownMutation.add(rundown, newEntry, afterId, parent);
|
||||
|
||||
const { rundownMetadata, revision } = commit();
|
||||
|
||||
// schedule the side effects
|
||||
|
||||
@@ -381,18 +381,25 @@ export function calculateDayOffset(
|
||||
* Receives an insertion order and returns the reference to an event ID
|
||||
* after which we will insert the new event
|
||||
*/
|
||||
export function getInsertAfterId(rundown: Rundown, afterId?: EntryId, beforeId?: EntryId): EntryId | null {
|
||||
if (afterId) {
|
||||
return afterId;
|
||||
}
|
||||
export function getInsertAfterId(
|
||||
rundown: Rundown,
|
||||
parent: OntimeBlock | null,
|
||||
afterId?: EntryId,
|
||||
beforeId?: EntryId,
|
||||
): EntryId | null {
|
||||
if (afterId) return afterId;
|
||||
if (!beforeId) return null;
|
||||
|
||||
if (beforeId) {
|
||||
const atIndex = rundown.flatOrder.findIndex((id) => id === beforeId);
|
||||
if (atIndex < 1) return null;
|
||||
return rundown.flatOrder[atIndex - 1];
|
||||
}
|
||||
/**
|
||||
* At this point we know we want to insert before a given ID
|
||||
* We need to check which list we should use to insert and find the event there
|
||||
*/
|
||||
const insertionList = parent ? parent.entries : rundown.order;
|
||||
if (!insertionList || insertionList.length === 0) return null;
|
||||
|
||||
return null;
|
||||
const atIndex = insertionList.findIndex((id) => id === beforeId);
|
||||
if (atIndex < 1) return null;
|
||||
return insertionList[atIndex - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user