mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-20 14:39:06 +00:00
refactor: extract parent resolution
This commit is contained in:
committed by
Carlos Valente
parent
80a3b04493
commit
5777ef3532
@@ -16,7 +16,7 @@ import {
|
|||||||
ProjectRundowns,
|
ProjectRundowns,
|
||||||
InsertOptions,
|
InsertOptions,
|
||||||
} from 'ontime-types';
|
} from 'ontime-types';
|
||||||
import { customFieldLabelToKey, getInsertAfterId } from 'ontime-utils';
|
import { customFieldLabelToKey, getInsertAfterId, resolveInsertParent } from 'ontime-utils';
|
||||||
|
|
||||||
import { updateRundownData } from '../../stores/runtimeState.js';
|
import { updateRundownData } from '../../stores/runtimeState.js';
|
||||||
import { runtimeService } from '../../services/runtime-service/runtime.service.js';
|
import { runtimeService } from '../../services/runtime-service/runtime.service.js';
|
||||||
@@ -47,28 +47,15 @@ export async function addEntry(eventData: EventPostPayload): Promise<OntimeEntry
|
|||||||
throw new Error(`Event with ID ${eventData.id} already exists`);
|
throw new Error(`Event with ID ${eventData.id} already exists`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// the parent can be provided or inferred from position
|
// resolve the parent, either from the payload or inferred from sibling position
|
||||||
|
const parentId = resolveInsertParent(rundown, eventData);
|
||||||
let parent: OntimeGroup | null = null;
|
let parent: OntimeGroup | null = null;
|
||||||
|
if (parentId) {
|
||||||
if ('parent' in eventData && eventData.parent != null) {
|
const maybeParent = rundown.entries[parentId];
|
||||||
// if the user provides a parent (inside a group), we make sure it exists and it is a group
|
|
||||||
const maybeParent = rundown.entries[eventData.parent];
|
|
||||||
if (!maybeParent || !isOntimeGroup(maybeParent)) {
|
if (!maybeParent || !isOntimeGroup(maybeParent)) {
|
||||||
throw new Error(`Invalid parent event with ID ${eventData.parent}`);
|
throw new Error(`Invalid parent event with ID ${parentId}`);
|
||||||
}
|
}
|
||||||
parent = maybeParent;
|
parent = maybeParent;
|
||||||
} else {
|
|
||||||
// otherwise, we may infer the parent from relative positioning (after/before)
|
|
||||||
const referenceId = eventData?.after ?? eventData?.before;
|
|
||||||
if (referenceId) {
|
|
||||||
const maybeSibling = rundown.entries[referenceId];
|
|
||||||
if (maybeSibling && 'parent' in maybeSibling && maybeSibling.parent) {
|
|
||||||
const maybeParent = rundown.entries[maybeSibling.parent];
|
|
||||||
if (maybeParent && isOntimeGroup(maybeParent)) {
|
|
||||||
parent = maybeParent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalise the position of the event in the rundown order
|
// normalise the position of the event in the rundown order
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export {
|
|||||||
getPreviousEventNormal,
|
getPreviousEventNormal,
|
||||||
getPreviousNormal,
|
getPreviousNormal,
|
||||||
getPreviousGroupNormal,
|
getPreviousGroupNormal,
|
||||||
|
resolveInsertParent,
|
||||||
swapEventData,
|
swapEventData,
|
||||||
} from './src/rundown-utils/rundownUtils.js';
|
} from './src/rundown-utils/rundownUtils.js';
|
||||||
export { getFirstRundown } from './src/rundown/rundown.utils.js';
|
export { getFirstRundown } from './src/rundown/rundown.utils.js';
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
getNextNormal,
|
getNextNormal,
|
||||||
getPreviousGroupNormal,
|
getPreviousGroupNormal,
|
||||||
getPreviousNormal,
|
getPreviousNormal,
|
||||||
|
resolveInsertParent,
|
||||||
swapEventData,
|
swapEventData,
|
||||||
} from './rundownUtils';
|
} from './rundownUtils';
|
||||||
import { demoDb } from './rundownUtils.mock';
|
import { demoDb } from './rundownUtils.mock';
|
||||||
@@ -272,6 +273,43 @@ describe('getInsertAfterId()', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('resolveInsertParent()', () => {
|
||||||
|
const rundown = {
|
||||||
|
id: 'test',
|
||||||
|
title: 'test',
|
||||||
|
entries: {
|
||||||
|
top: { id: 'top', type: SupportedEntry.Event, parent: null } as OntimeEvent,
|
||||||
|
group: { id: 'group', type: SupportedEntry.Group, entries: ['31', '32'] } as unknown as OntimeGroup,
|
||||||
|
'31': { id: '31', type: SupportedEntry.Event, parent: 'group' } as OntimeEvent,
|
||||||
|
'32': { id: '32', type: SupportedEntry.Event, parent: 'group' } as OntimeEvent,
|
||||||
|
},
|
||||||
|
order: ['top', 'group'],
|
||||||
|
flatOrder: ['top', 'group', '31', '32'],
|
||||||
|
revision: 1,
|
||||||
|
} as Rundown;
|
||||||
|
|
||||||
|
it('returns explicit parent id', () => {
|
||||||
|
expect(resolveInsertParent(rundown, { parent: 'group' })).toBe('group');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns non-existent explicit parent id as-is', () => {
|
||||||
|
expect(resolveInsertParent(rundown, { parent: 'missing' })).toBe('missing');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('infers parent id from sibling when parent is omitted', () => {
|
||||||
|
expect(resolveInsertParent(rundown, { after: '31' })).toBe('group');
|
||||||
|
expect(resolveInsertParent(rundown, { before: '32' })).toBe('group');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null when no grouped sibling reference exists', () => {
|
||||||
|
expect(resolveInsertParent(rundown, { after: 'top' })).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null when no references are provided', () => {
|
||||||
|
expect(resolveInsertParent(rundown, {})).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('addToRundown()', () => {
|
describe('addToRundown()', () => {
|
||||||
const makeTestRundown = (): Rundown =>
|
const makeTestRundown = (): Rundown =>
|
||||||
({
|
({
|
||||||
|
|||||||
@@ -382,6 +382,32 @@ export function getInsertAfterId(
|
|||||||
return insertionList[atIndex - 1];
|
return insertionList[atIndex - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResolveInsertParentOptions = {
|
||||||
|
parent?: EntryId | null;
|
||||||
|
after?: EntryId;
|
||||||
|
before?: EntryId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the parent ID for an insertion.
|
||||||
|
* Uses explicit parent first, then infers from sibling references.
|
||||||
|
*/
|
||||||
|
export function resolveInsertParent(rundown: Rundown, options: ResolveInsertParentOptions): EntryId | null {
|
||||||
|
if (options.parent) {
|
||||||
|
return options.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
const referenceId = options.after ?? options.before;
|
||||||
|
if (!referenceId) return null;
|
||||||
|
|
||||||
|
const maybeSibling = rundown.entries[referenceId];
|
||||||
|
if (maybeSibling && 'parent' in maybeSibling && maybeSibling.parent) {
|
||||||
|
return maybeSibling.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add entry to rundown, mutates the rundown in place.
|
* Add entry to rundown, mutates the rundown in place.
|
||||||
* Handles the following cases:
|
* Handles the following cases:
|
||||||
|
|||||||
Reference in New Issue
Block a user