mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-12 02:43:50 +00:00
feat: allow naming aux timers
Adds custom, persisted names for the three aux timers, editable in the editor settings and surfaced everywhere an aux title is shown. - Persist `auxTimerNames` on the Settings object (default empty, falls back to the stock "Aux N" label when unset). Reuses the existing settings pipeline: validation sanitises to a fixed-length trimmed array, the parser normalises loaded/legacy data, and migrations carry the field forward. - New "Aux timers" settings section to name each timer, plus a shortcut button next to the aux timers in the playback control that jumps straight to it. - Resolve names via a shared getAuxTimerLabel helper in the aux timer control, Studio view, secondary-source selector and preview, and the automation action labels. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCZejVTzuAY3tHTE6nB3JH
This commit is contained in:
@@ -74,7 +74,7 @@ export function migrateSettings(jsonData: object): (Settings & { serverPort: num
|
||||
const { serverPort, editorKey, operatorKey, timeFormat, language } = structuredClone(
|
||||
jsonData.settings,
|
||||
) as old_Settings;
|
||||
return { version: '4.0.0', serverPort, editorKey, operatorKey, timeFormat, language };
|
||||
return { version: '4.0.0', serverPort, editorKey, operatorKey, timeFormat, language, auxTimerNames: ['', '', ''] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ export function migrateServerPort(jsonData: Partial<DatabaseModel>): {
|
||||
const operatorKey = settings?.operatorKey;
|
||||
const timeFormat = settings?.timeFormat;
|
||||
const language = settings?.language;
|
||||
const auxTimerNames = settings?.auxTimerNames ?? ['', '', ''];
|
||||
const version = '4.5.0';
|
||||
db.settings = {
|
||||
version,
|
||||
@@ -30,6 +31,7 @@ export function migrateServerPort(jsonData: Partial<DatabaseModel>): {
|
||||
operatorKey,
|
||||
timeFormat,
|
||||
language,
|
||||
auxTimerNames,
|
||||
app: 'ontime',
|
||||
} as Settings;
|
||||
return { db, serverPort: settings?.serverPort };
|
||||
|
||||
@@ -184,6 +184,7 @@ describe('v3 to v4', () => {
|
||||
operatorKey: null,
|
||||
timeFormat: '24',
|
||||
language: 'en',
|
||||
auxTimerNames: ['', '', ''],
|
||||
};
|
||||
const newSettings = v3.migrateSettings(oldDb);
|
||||
expect(newSettings).toEqual(expectSettings);
|
||||
|
||||
@@ -16,6 +16,21 @@ describe('parseSettings()', () => {
|
||||
operatorKey: null,
|
||||
timeFormat: '24',
|
||||
language: 'en',
|
||||
auxTimerNames: ['', '', ''],
|
||||
});
|
||||
});
|
||||
|
||||
it('carries custom aux timer names through and normalises to a length-3 array', () => {
|
||||
const result = parseSettings({
|
||||
settings: { version: '1', auxTimerNames: ['Speaker'] } as unknown as Settings,
|
||||
});
|
||||
expect(result.auxTimerNames).toStrictEqual(['Speaker', '', '']);
|
||||
});
|
||||
|
||||
it('falls back to defaults when aux timer names are missing or malformed', () => {
|
||||
const result = parseSettings({
|
||||
settings: { version: '1', auxTimerNames: 'not-an-array' } as unknown as Settings,
|
||||
});
|
||||
expect(result.auxTimerNames).toStrictEqual(['', '', '']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,5 +22,18 @@ export function parseSettings(data: Partial<DatabaseModel>): Settings {
|
||||
operatorKey: data.settings.operatorKey ?? defaultSettings.operatorKey,
|
||||
timeFormat: data.settings.timeFormat ?? defaultSettings.timeFormat,
|
||||
language: data.settings.language ?? defaultSettings.language,
|
||||
auxTimerNames: sanitiseAuxTimerNames(data.settings.auxTimerNames, defaultSettings.auxTimerNames),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the aux timer names are a fixed-length array of strings
|
||||
* regardless of what is found in the file
|
||||
*/
|
||||
function sanitiseAuxTimerNames(maybeNames: unknown, fallback: string[]): string[] {
|
||||
const source = Array.isArray(maybeNames) ? maybeNames : [];
|
||||
return fallback.map((defaultName, index) => {
|
||||
const value = source[index];
|
||||
return typeof value === 'string' ? value : defaultName;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,6 +27,14 @@ export const validateSettings = [
|
||||
pinValidator('operatorKey'),
|
||||
body('timeFormat').isString().isIn(['12', '24']).withMessage('Time format can only be "12" or "24"'),
|
||||
body('language').isString().trim().notEmpty(),
|
||||
body('auxTimerNames')
|
||||
.isArray()
|
||||
.withMessage('auxTimerNames must be an array')
|
||||
.customSanitizer((value: unknown) => {
|
||||
// normalise to a fixed-length array of trimmed strings
|
||||
const source = Array.isArray(value) ? value : [];
|
||||
return [0, 1, 2].map((index) => (typeof source[index] === 'string' ? source[index].trim() : ''));
|
||||
}),
|
||||
|
||||
requestValidationFunction,
|
||||
];
|
||||
|
||||
@@ -30,6 +30,7 @@ const dbModel: DatabaseModel = {
|
||||
operatorKey: null,
|
||||
timeFormat: '24',
|
||||
language: 'en',
|
||||
auxTimerNames: ['', '', ''],
|
||||
},
|
||||
viewSettings: {
|
||||
overrideStyles: false,
|
||||
|
||||
@@ -29,6 +29,7 @@ export const demoDb: DatabaseModel = {
|
||||
operatorKey: null,
|
||||
timeFormat: '24',
|
||||
language: 'en',
|
||||
auxTimerNames: ['', '', ''],
|
||||
},
|
||||
viewSettings: {
|
||||
dangerColor: '#ff7300',
|
||||
|
||||
Reference in New Issue
Block a user