feat: duplicate groups

This commit is contained in:
Carlos Valente
2025-05-19 20:01:03 +02:00
committed by arc-alex
parent 11e0215530
commit 2f26f20db5
20 changed files with 448 additions and 88 deletions
@@ -21,7 +21,7 @@ import { updateRundownData } from '../../stores/runtimeState.js';
import { runtimeService } from '../runtime-service/RuntimeService.js';
import * as cache from './rundownCache.js';
import { getInsertionPosition } from './rundownUtils.js';
import { getPreviousId } from './rundownUtils.js';
type CompleteEntry<T> =
T extends Partial<OntimeEvent>
@@ -70,11 +70,12 @@ export async function addEvent(eventData: EventPostPayload): Promise<OntimeEntry
throw new Error(`Event with ID ${eventData.id} already exists`);
}
// 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 and it is a group
let parent: EntryId | null = null;
if ('parent' in eventData && eventData.parent != null) {
if (!cache.hasId(eventData.parent)) {
throw new Error(`Parent event with ID ${eventData.parent} not found`);
const maybeParent = cache.getCurrentRundown().entries[eventData.parent];
if (!maybeParent || !isOntimeBlock(maybeParent)) {
throw new Error(`Invalid parent event with ID ${eventData.parent}`);
}
parent = eventData.parent;
}
@@ -91,14 +92,14 @@ export async function addEvent(eventData: EventPostPayload): Promise<OntimeEntry
}
}
const { afterId, atIndex } = getInsertionPosition(parent, eventData?.after, eventData?.before);
const afterId = getPreviousId(eventData?.after, eventData?.before);
// generate a fully formed entry from the patch
const sanitisedEntry = generateEvent(eventData, afterId);
// modify rundown
const scopedMutation = cache.mutateCache(cache.add);
const { newEvent } = await scopedMutation({ atIndex, parent, entry: sanitisedEntry });
const { newEvent } = await scopedMutation({ afterId, parent, entry: sanitisedEntry });
// notify runtime that rundown has changed
updateRuntimeOnChange();
@@ -113,9 +114,9 @@ export async function addEvent(eventData: EventPostPayload): Promise<OntimeEntry
/**
* deletes event by its ID
*/
export async function deleteEvent(eventIds: string[]) {
export async function deleteEvent(eventIds: EntryId[]) {
const scopedMutation = cache.mutateCache(cache.remove);
const { didMutate } = await scopedMutation({ eventIds });
const { didMutate, changeList } = await scopedMutation({ eventIds });
if (!didMutate) {
return;
@@ -125,7 +126,7 @@ export async function deleteEvent(eventIds: string[]) {
updateRuntimeOnChange();
// notify timer and external services of change
notifyChanges({ timer: eventIds, external: true });
notifyChanges({ timer: changeList, external: true });
}
/**
@@ -219,6 +220,27 @@ export async function applyDelay(delayId: EntryId) {
notifyChanges({ timer: true, external: true });
}
/**
* Clones an entry, ensuring that all dependencies are preserved
*/
export async function cloneEntry(entryId: EntryId) {
const scopedMutation = cache.mutateCache(cache.clone);
const { newRundown, newEvent } = await scopedMutation({ entryId });
// notify runtime that rundown has changed
updateRuntimeOnChange();
if (isOntimeBlock(newEvent)) {
notifyChanges({ timer: newEvent.events, external: true });
} else if (isOntimeEvent(newEvent)) {
notifyChanges({ timer: [newEvent.id], external: true });
} else if (isOntimeDelay(newEvent)) {
notifyChanges({ external: true });
}
return newRundown;
}
/**
* Deletes a block from the rundown and moves all its children to the top level
*/