mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-17 13:23:35 +00:00
feat: create group from entry selection
This commit is contained in:
@@ -14,9 +14,9 @@ import {
|
||||
} from 'ontime-types';
|
||||
import { getCueCandidate } from 'ontime-utils';
|
||||
|
||||
import { block as blockDef, delay as delayDef } from '../../models/eventsDefinition.js';
|
||||
import { delay as delayDef } from '../../models/eventsDefinition.js';
|
||||
import { sendRefetch } from '../../adapters/websocketAux.js';
|
||||
import { createEvent } from '../../api-data/rundown/rundown.utils.js';
|
||||
import { createBlock, createEvent } from '../../api-data/rundown/rundown.utils.js';
|
||||
import { updateRundownData } from '../../stores/runtimeState.js';
|
||||
import { runtimeService } from '../runtime-service/RuntimeService.js';
|
||||
|
||||
@@ -55,7 +55,7 @@ function generateEvent<T extends Partial<OntimeEvent> | Partial<OntimeDelay> | P
|
||||
|
||||
// TODO(v4): allow user to provide a larger patch of the block entry
|
||||
if (isOntimeBlock(eventData)) {
|
||||
return { ...blockDef, title: eventData?.title ?? '', id } as CompleteEntry<T>;
|
||||
return createBlock({ id, title: eventData.title ?? '' }) as CompleteEntry<T>;
|
||||
}
|
||||
|
||||
throw new Error('Invalid event type');
|
||||
@@ -235,6 +235,22 @@ export async function dissolveBlock(blockId: EntryId) {
|
||||
return newRundown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups a list of entries into a block
|
||||
*/
|
||||
export async function groupEntries(entryIds: EntryId[]) {
|
||||
const scopedMutation = cache.mutateCache(cache.groupEntries);
|
||||
const { newRundown } = await scopedMutation({ entryIds });
|
||||
|
||||
// notify runtime that rundown has changed
|
||||
updateRuntimeOnChange();
|
||||
|
||||
// we dont need to modify the timer since the grouping does not affect the runtime
|
||||
notifyChanges({ external: true });
|
||||
|
||||
return newRundown;
|
||||
}
|
||||
|
||||
/**
|
||||
* swaps two events
|
||||
* @param {string} from - id of event from
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
removeCustomField,
|
||||
customFieldChangelog,
|
||||
dissolveBlock,
|
||||
groupEntries,
|
||||
} from '../rundownCache.js';
|
||||
import { makeOntimeBlock, makeOntimeDelay, makeOntimeEvent, makeRundown } from '../__mocks__/rundown.mocks.js';
|
||||
import { ProcessedRundownMetadata } from '../rundownCache.utils.js';
|
||||
@@ -795,6 +796,39 @@ describe('dissolveBlock() mutation', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('groupEntries() mutation', () => {
|
||||
it('groups a list of existing events into a new block', () => {
|
||||
const rundown = makeRundown({
|
||||
order: ['1', '2', '3'],
|
||||
flatOrder: ['1', '2', '3'],
|
||||
entries: {
|
||||
'1': makeOntimeEvent({ id: '1', parent: null }),
|
||||
'2': makeOntimeEvent({ id: '2', parent: null }),
|
||||
'3': makeOntimeEvent({ id: '3', parent: null }),
|
||||
},
|
||||
});
|
||||
|
||||
const { newRundown } = groupEntries({
|
||||
rundown,
|
||||
entryIds: ['1', '2'],
|
||||
});
|
||||
|
||||
const blockId = newRundown.order[0];
|
||||
expect(blockId).toStrictEqual(expect.any(String));
|
||||
expect(newRundown.order).toStrictEqual([expect.any(String), '3']);
|
||||
expect(newRundown.flatOrder).toStrictEqual([expect.any(String), '1', '2', '3']);
|
||||
expect(newRundown.entries).toMatchObject({
|
||||
[blockId]: {
|
||||
type: SupportedEntry.Block,
|
||||
events: ['1', '2'],
|
||||
},
|
||||
'1': { id: '1', type: SupportedEntry.Event, parent: blockId },
|
||||
'2': { id: '2', type: SupportedEntry.Event, parent: blockId },
|
||||
'3': { id: '3', type: SupportedEntry.Event, parent: null },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('swap() mutation', () => {
|
||||
it('should correctly swap data between events', () => {
|
||||
const rundown = makeRundown({
|
||||
|
||||
@@ -11,11 +11,19 @@ import {
|
||||
OntimeEntry,
|
||||
Rundown,
|
||||
RundownEntries,
|
||||
OntimeDelay,
|
||||
} from 'ontime-types';
|
||||
import { generateId, insertAtIndex, reorderArray, swapEventData, customFieldLabelToKey } from 'ontime-utils';
|
||||
import {
|
||||
generateId,
|
||||
insertAtIndex,
|
||||
reorderArray,
|
||||
swapEventData,
|
||||
customFieldLabelToKey,
|
||||
mergeAtIndex,
|
||||
} from 'ontime-utils';
|
||||
|
||||
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
|
||||
import { createPatch } from '../../api-data/rundown/rundown.utils.js';
|
||||
import { createBlock, createPatch } from '../../api-data/rundown/rundown.utils.js';
|
||||
|
||||
import type { RundownMetadata } from './rundown.types.js';
|
||||
import { apply } from './delayUtils.js';
|
||||
@@ -541,6 +549,59 @@ export function dissolveBlock({ rundown, blockId }: DissolveBlockArgs): Mutating
|
||||
return { newRundown: rundown, didMutate: true };
|
||||
}
|
||||
|
||||
type GroupArgs = MutationParams<{ entryIds: EntryId[] }>;
|
||||
/**
|
||||
* Groups a list of entries into a block
|
||||
* It ensures that the entries get reassigned parent and the block gets a list of events
|
||||
* The block will be created at the index of the first event in the order, not at the lowest index
|
||||
* Mutates the given rundown
|
||||
* @throws if any of the entries is a block
|
||||
* @throws if any of the entries is not found
|
||||
*/
|
||||
export function groupEntries({ rundown, entryIds }: GroupArgs): MutatingReturn {
|
||||
const block = createBlock({ id: getUniqueId() });
|
||||
|
||||
const nestedEvents: EntryId[] = [];
|
||||
let firstIndex = -1;
|
||||
for (let i = 0; i < entryIds.length; i++) {
|
||||
const entryId = entryIds[i];
|
||||
const entry = rundown.entries[entryId];
|
||||
if (!entry) {
|
||||
throw new Error('Entry not found');
|
||||
}
|
||||
|
||||
if (isOntimeBlock(entry)) {
|
||||
throw new Error('Cannot group a block');
|
||||
}
|
||||
|
||||
if (entry.parent !== null) {
|
||||
throw new Error('Entry already has a parent');
|
||||
}
|
||||
|
||||
// the block will be created at the first selected event position
|
||||
// note that this is not the lowest index
|
||||
if (firstIndex === -1) {
|
||||
firstIndex = rundown.flatOrder.indexOf(entryId);
|
||||
}
|
||||
|
||||
nestedEvents.push(entryId);
|
||||
entry.parent = block.id;
|
||||
rundown.flatOrder = rundown.flatOrder.filter((id) => id !== entryId);
|
||||
rundown.order = rundown.order.filter((id) => id !== entryId);
|
||||
}
|
||||
|
||||
block.events = nestedEvents;
|
||||
const insertIndex = Math.max(0, firstIndex);
|
||||
// we have filtered the items from the order
|
||||
// we will insert them now, with only the block at top level ...
|
||||
rundown.order = insertAtIndex(insertIndex, block.id, rundown.order);
|
||||
/// ... and the nested elements after the block in the flat order
|
||||
rundown.flatOrder = mergeAtIndex(insertIndex, [block.id, ...nestedEvents], rundown.flatOrder);
|
||||
rundown.entries[block.id] = block;
|
||||
|
||||
return { newRundown: rundown, didMutate: true };
|
||||
}
|
||||
|
||||
type SwapArgs = MutationParams<{ fromId: EntryId; toId: EntryId }>;
|
||||
/**
|
||||
* Swap two entries
|
||||
|
||||
Reference in New Issue
Block a user