mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-11 10:23:54 +00:00
59bff62f76
- Revert the automation action labels back to "Aux N: action" (no functional reason to change this wording). - Simplify the aux timers header in the playback control: the flex container now only handles layout, and the label text reuses the same font-size/color as the per-timer labels below it, instead of a bespoke style block duplicating those values. - Replace the generic, length-driven normaliser with an explicit sanitiseAuxTimerNames() that always deals with exactly three timers, and rename it away from "normalise" (which didn't convey that it trims, caps length and fills in missing entries). Static defaults now use a plain ['', '', ''] literal instead of calling the sanitiser with no input to sanitise. - Settings.type.ts and AuxTimerSettings.tsx no longer generate their three fields from a loop; the form mirrors the same explicit, one-field-per-row style already used by GeneralSettings.tsx. - Trim comments that only restated what the following line already says, keeping the ones that explain non-obvious behaviour (why AuxTimerService needs to be resynced separately from the data provider, why SimpleTimer.reset() preserves the name). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WCZejVTzuAY3tHTE6nB3JH
181 lines
5.3 KiB
TypeScript
181 lines
5.3 KiB
TypeScript
import { RuntimeStore, SimpleDirection, SimplePlayback } from 'ontime-types';
|
|
import { sanitiseAuxTimerNames } from 'ontime-utils';
|
|
|
|
import { SimpleTimer } from '../../classes/simple-timer/SimpleTimer.js';
|
|
import { timerConfig } from '../../setup/config.js';
|
|
import { eventStore } from '../../stores/EventStore.js';
|
|
|
|
type AuxTimerStateUpdate = Partial<Pick<RuntimeStore, 'auxtimer1' | 'auxtimer2' | 'auxtimer3'>>;
|
|
type EmitFn = (state: AuxTimerStateUpdate) => void;
|
|
type GetTimeFn = () => number;
|
|
|
|
export class AuxTimerService {
|
|
private aux1: SimpleTimer;
|
|
private aux2: SimpleTimer;
|
|
private aux3: SimpleTimer;
|
|
private interval: NodeJS.Timeout | null = null;
|
|
protected emit: EmitFn;
|
|
private getTime: GetTimeFn;
|
|
|
|
constructor(emit: EmitFn, getTime: GetTimeFn) {
|
|
this.aux1 = new SimpleTimer(timerConfig.auxTimerDefault);
|
|
this.aux2 = new SimpleTimer(timerConfig.auxTimerDefault);
|
|
this.aux3 = new SimpleTimer(timerConfig.auxTimerDefault);
|
|
this.emit = emit;
|
|
this.getTime = getTime;
|
|
}
|
|
|
|
/**
|
|
* Called at bootstrap and whenever the loaded project's settings change,
|
|
* so the running timers reflect the current project's aux timer names.
|
|
*/
|
|
loadNames(names?: string[]) {
|
|
const [name1, name2, name3] = sanitiseAuxTimerNames(names);
|
|
const patch: AuxTimerStateUpdate = {
|
|
auxtimer1: this.aux1.setName(name1),
|
|
auxtimer2: this.aux2.setName(name2),
|
|
auxtimer3: this.aux3.setName(name3),
|
|
};
|
|
this.emit(patch);
|
|
}
|
|
|
|
/**
|
|
* Whether any of the aux timers are currently running
|
|
*/
|
|
private hasActiveTimers(): boolean {
|
|
return (
|
|
this.aux1.state.playback === SimplePlayback.Start ||
|
|
this.aux2.state.playback === SimplePlayback.Start ||
|
|
this.aux3.state.playback === SimplePlayback.Start
|
|
);
|
|
}
|
|
|
|
private startInterval() {
|
|
if (!this.interval) {
|
|
this.interval = setInterval(this.update.bind(this), 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility simplifies guarding against multiple intervals being set
|
|
*/
|
|
private stopInterval() {
|
|
if (this.interval && !this.hasActiveTimers()) {
|
|
clearInterval(this.interval);
|
|
this.interval = null;
|
|
}
|
|
}
|
|
|
|
@broadcastReturn
|
|
setDirection(direction: SimpleDirection, index: number) {
|
|
if (index === 1) return this.aux1.setDirection(direction, this.getTime());
|
|
if (index === 2) return this.aux2.setDirection(direction, this.getTime());
|
|
return this.aux3.setDirection(direction, this.getTime());
|
|
}
|
|
|
|
@broadcastReturn
|
|
start(index: number) {
|
|
this.startInterval();
|
|
if (index === 1) return this.aux1.start(this.getTime());
|
|
if (index === 2) return this.aux2.start(this.getTime());
|
|
return this.aux3.start(this.getTime());
|
|
}
|
|
|
|
@broadcastReturn
|
|
pause(index: number) {
|
|
// First pause the timer
|
|
let result;
|
|
if (index === 1) result = this.aux1.pause(this.getTime());
|
|
else if (index === 2) result = this.aux2.pause(this.getTime());
|
|
else result = this.aux3.pause(this.getTime());
|
|
|
|
// Then check if we need to keep the interval running
|
|
if (!this.hasActiveTimers()) {
|
|
this.stopInterval();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
@broadcastReturn
|
|
stop(index: number) {
|
|
// First stop the timer
|
|
let result;
|
|
if (index === 1) result = this.aux1.stop();
|
|
else if (index === 2) result = this.aux2.stop();
|
|
else result = this.aux3.stop();
|
|
|
|
// Then check if we need to keep the interval running
|
|
if (!this.hasActiveTimers()) {
|
|
this.stopInterval();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
@broadcastReturn
|
|
setTime(duration: number, index: number) {
|
|
if (index === 1) return this.aux1.setTime(duration);
|
|
if (index === 2) return this.aux2.setTime(duration);
|
|
return this.aux3.setTime(duration);
|
|
}
|
|
|
|
@broadcastReturn
|
|
addTime(millis: number, index: number) {
|
|
const aux = index === 1 ? this.aux1 : index === 2 ? this.aux2 : this.aux3;
|
|
if (aux.state.playback === SimplePlayback.Start) {
|
|
aux.addTime(millis);
|
|
return aux.update(this.getTime());
|
|
}
|
|
return aux.addTime(millis);
|
|
}
|
|
|
|
private update() {
|
|
/**
|
|
* The update function affects any running timers,
|
|
* so we decide to emit a patch object rather
|
|
* than using the decorator which would emit individual updates.
|
|
*/
|
|
const patch: AuxTimerStateUpdate = {};
|
|
|
|
const timeNow = this.getTime();
|
|
if (this.aux1.state.playback === SimplePlayback.Start) {
|
|
patch.auxtimer1 = this.aux1.update(timeNow);
|
|
}
|
|
|
|
if (this.aux2.state.playback === SimplePlayback.Start) {
|
|
patch.auxtimer2 = this.aux2.update(timeNow);
|
|
}
|
|
|
|
if (this.aux3.state.playback === SimplePlayback.Start) {
|
|
patch.auxtimer3 = this.aux3.update(timeNow);
|
|
}
|
|
|
|
if (Object.keys(patch).length > 0) {
|
|
this.emit(patch);
|
|
}
|
|
}
|
|
}
|
|
|
|
function broadcastReturn(_target: object, _propertyKey: string, descriptor: PropertyDescriptor) {
|
|
const originalMethod = descriptor.value;
|
|
|
|
descriptor.value = function (this: AuxTimerService, ...args: unknown[]) {
|
|
const result = originalMethod.apply(this, args);
|
|
const index = args[args.length - 1] as number;
|
|
this.emit({ [`auxtimer${index}`]: result });
|
|
return result;
|
|
};
|
|
|
|
return descriptor;
|
|
}
|
|
|
|
const emit = (state: AuxTimerStateUpdate) => {
|
|
for (const [key, value] of Object.entries(state)) {
|
|
eventStore.set(key as keyof RuntimeStore, value);
|
|
}
|
|
};
|
|
const timeNow = () => Date.now();
|
|
|
|
export const auxTimerService = new AuxTimerService(emit, timeNow);
|