refactor: remove finishedAt from runtime data (#1716)

This commit is contained in:
Alex Christoffer Rasmussen
2025-08-08 20:17:57 +02:00
committed by Carlos Valente
parent 94b1f63c7c
commit 486d89ecf4
8 changed files with 38 additions and 50 deletions
@@ -16,7 +16,6 @@ const staticAutocompleteOptions = [
'{{timer.duration}}', '{{timer.duration}}',
'{{timer.elapsed}}', '{{timer.elapsed}}',
'{{timer.expectedFinish}}', '{{timer.expectedFinish}}',
'{{timer.finishedAt}}',
'{{timer.secondaryTimer}}', '{{timer.secondaryTimer}}',
'{{timer.startedAt}}', '{{timer.startedAt}}',
'{{runtime.selectedEventIndex}}', '{{runtime.selectedEventIndex}}',
@@ -21,17 +21,17 @@ describe('getExpectedFinish()', () => {
timer: { timer: {
addedTime: 0, addedTime: 0,
duration: 10, duration: 10,
finishedAt: null,
startedAt: null, startedAt: null,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
const calculatedFinish = getExpectedFinish(state); const calculatedFinish = getExpectedFinish(state);
expect(calculatedFinish).toBe(null); expect(calculatedFinish).toBe(null);
}); });
it('is finishedAt if defined', () => { it('is hasFinished if defined', () => {
const state = { const state = {
eventNow: { eventNow: {
timeEnd: 20, timeEnd: 20,
@@ -40,11 +40,11 @@ describe('getExpectedFinish()', () => {
timer: { timer: {
addedTime: 0, addedTime: 0,
duration: 10, duration: 10,
finishedAt: 20, // <---- finished at
startedAt: 10, startedAt: 10,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: true,
}, },
} as RuntimeState; } as RuntimeState;
const calculatedFinish = getExpectedFinish(state); const calculatedFinish = getExpectedFinish(state);
@@ -59,11 +59,11 @@ describe('getExpectedFinish()', () => {
timer: { timer: {
addedTime: 0, addedTime: 0,
duration: 10, duration: 10,
finishedAt: null,
startedAt: 1, startedAt: 1,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
const calculatedFinish = getExpectedFinish(state); const calculatedFinish = getExpectedFinish(state);
@@ -78,11 +78,11 @@ describe('getExpectedFinish()', () => {
timer: { timer: {
addedTime: 20, addedTime: 20,
duration: 10, duration: 10,
finishedAt: null,
startedAt: 1, startedAt: 1,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -98,11 +98,11 @@ describe('getExpectedFinish()', () => {
timer: { timer: {
addedTime: -10, addedTime: -10,
duration: 10, duration: 10,
finishedAt: null,
startedAt: 1, startedAt: 1,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -118,11 +118,11 @@ describe('getExpectedFinish()', () => {
timer: { timer: {
addedTime: -100, addedTime: -100,
duration: 10, duration: 10,
finishedAt: null,
startedAt: 1, startedAt: 1,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -138,11 +138,11 @@ describe('getExpectedFinish()', () => {
timer: { timer: {
addedTime: 0, addedTime: 0,
duration: 0, duration: 0,
finishedAt: null,
startedAt: 1, startedAt: 1,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -158,11 +158,11 @@ describe('getExpectedFinish()', () => {
timer: { timer: {
addedTime: 0, addedTime: 0,
duration: dayInMs, duration: dayInMs,
finishedAt: null,
startedAt: 10, startedAt: 10,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -179,11 +179,11 @@ describe('getExpectedFinish()', () => {
timer: { timer: {
addedTime: 10, addedTime: 10,
duration: dayInMs, duration: dayInMs,
finishedAt: null,
startedAt: 10, startedAt: 10,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -198,11 +198,11 @@ describe('getExpectedFinish()', () => {
}, },
timer: { timer: {
addedTime: 0, addedTime: 0,
finishedAt: null,
startedAt: 79200000, // 22:00:00 startedAt: 79200000, // 22:00:00
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
runtime: { runtime: {
actualStart: 79200000, actualStart: 79200000,
@@ -229,10 +229,10 @@ describe('getCurrent()', () => {
addedTime: 10, addedTime: 10,
duration: 111, // <-- we take the duration value duration: 111, // <-- we take the duration value
startedAt: null, startedAt: null,
finishedAt: null,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -250,10 +250,10 @@ describe('getCurrent()', () => {
addedTime: 0, addedTime: 0,
duration: 10, duration: 10,
startedAt: 0, startedAt: 0,
finishedAt: null,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -271,10 +271,10 @@ describe('getCurrent()', () => {
addedTime: 10, addedTime: 10,
duration: 10, duration: 10,
startedAt: 0, startedAt: 0,
finishedAt: null,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -292,10 +292,10 @@ describe('getCurrent()', () => {
addedTime: 0, addedTime: 0,
duration: dayInMs + 10, duration: dayInMs + 10,
startedAt: 10, startedAt: 10,
finishedAt: null,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -313,10 +313,10 @@ describe('getCurrent()', () => {
addedTime: 0, addedTime: 0,
duration: dayInMs + 10, duration: dayInMs + 10,
startedAt: 10, startedAt: 10,
finishedAt: null,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -334,10 +334,10 @@ describe('getCurrent()', () => {
addedTime: 20, addedTime: 20,
duration: dayInMs + 10, duration: dayInMs + 10,
startedAt: 10, startedAt: 10,
finishedAt: null,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -357,13 +357,13 @@ describe('getCurrent()', () => {
addedTime: 0, addedTime: 0,
duration: 100, duration: 100,
startedAt: null, startedAt: null,
finishedAt: null,
}, },
runtime: { runtime: {
plannedEnd: null, plannedEnd: null,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -382,13 +382,13 @@ describe('getCurrent()', () => {
addedTime: 0, addedTime: 0,
duration: 100, duration: 100,
startedAt: 10, startedAt: 10,
finishedAt: null,
}, },
runtime: { runtime: {
plannedEnd: 100, plannedEnd: 100,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -407,13 +407,13 @@ describe('getCurrent()', () => {
addedTime: 7, addedTime: 7,
duration: 100, duration: 100,
startedAt: 10, startedAt: 10,
finishedAt: null,
}, },
runtime: { runtime: {
plannedEnd: 100, plannedEnd: 100,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -433,7 +433,6 @@ describe('getCurrent()', () => {
addedTime: 0, addedTime: 0,
duration: Infinity, // not relevant, duration: Infinity, // not relevant,
startedAt: 79200000, // 22:00:00 startedAt: 79200000, // 22:00:00
finishedAt: null,
}, },
runtime: { runtime: {
actualStart: 79200000, actualStart: 79200000,
@@ -441,6 +440,7 @@ describe('getCurrent()', () => {
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -461,7 +461,6 @@ describe('getCurrent()', () => {
addedTime: 0, addedTime: 0,
duration: Infinity, // not relevant, duration: Infinity, // not relevant,
startedAt: 79200000, // 22:00:00 startedAt: 79200000, // 22:00:00
finishedAt: null,
}, },
runtime: { runtime: {
actualStart: 82000000, // 22:46:40 <--- started now actualStart: 82000000, // 22:46:40 <--- started now
@@ -469,6 +468,7 @@ describe('getCurrent()', () => {
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -491,10 +491,10 @@ describe('getExpectedFinish() and getCurrentTime() combined', () => {
addedTime: 0, addedTime: 0,
duration, duration,
startedAt: 0, startedAt: 0,
finishedAt: null,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -519,10 +519,10 @@ describe('getExpectedFinish() and getCurrentTime() combined', () => {
addedTime: 3, addedTime: 3,
duration, duration,
startedAt: 0, startedAt: 0,
finishedAt: null,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
@@ -846,7 +846,6 @@ describe('getRuntimeOffset()', () => {
duration: 3600000, duration: 3600000,
elapsed: null, elapsed: null,
expectedFinish: null, expectedFinish: null,
finishedAt: null,
playback: 'armed', playback: 'armed',
secondaryTimer: null, secondaryTimer: null,
startedAt: null, startedAt: null,
@@ -887,7 +886,6 @@ describe('getRuntimeOffset()', () => {
duration: 3600000, duration: 3600000,
elapsed: null, elapsed: null,
expectedFinish: null, expectedFinish: null,
finishedAt: null,
playback: 'armed', playback: 'armed',
secondaryTimer: null, secondaryTimer: null,
startedAt: null, startedAt: null,
@@ -939,7 +937,6 @@ describe('getRuntimeOffset()', () => {
duration: 3600000, duration: 3600000,
elapsed: 2000000, elapsed: 2000000,
expectedFinish: 81600000, expectedFinish: 81600000,
finishedAt: null,
playback: Playback.Play, playback: Playback.Play,
secondaryTimer: null, secondaryTimer: null,
startedAt: 78000000, startedAt: 78000000,
@@ -991,7 +988,6 @@ describe('getRuntimeOffset()', () => {
duration: 3600000, duration: 3600000,
elapsed: 4000000, elapsed: 4000000,
expectedFinish: 81600000, expectedFinish: 81600000,
finishedAt: null,
playback: Playback.Play, playback: Playback.Play,
secondaryTimer: null, secondaryTimer: null,
startedAt: 78000000, startedAt: 78000000,
@@ -1032,7 +1028,6 @@ describe('getRuntimeOffset()', () => {
duration: 3600000, duration: 3600000,
elapsed: 0, elapsed: 0,
expectedFinish: 82000000 + 3600000, // <--- now + duration expectedFinish: 82000000 + 3600000, // <--- now + duration
finishedAt: null,
playback: Playback.Play, playback: Playback.Play,
secondaryTimer: null, secondaryTimer: null,
startedAt: 82000000, // <--- started now startedAt: 82000000, // <--- started now
@@ -1132,7 +1127,6 @@ describe('getTimerPhase()', () => {
duration: null, duration: null,
elapsed: null, elapsed: null,
expectedFinish: null, expectedFinish: null,
finishedAt: null,
playback: Playback.Stop, playback: Playback.Stop,
phase: TimerPhase.None, phase: TimerPhase.None,
secondaryTimer: null, secondaryTimer: null,
@@ -1236,7 +1230,6 @@ describe('getTimerPhase()', () => {
duration: null, duration: null,
elapsed: 0, elapsed: 0,
expectedFinish: null, expectedFinish: null,
finishedAt: null,
phase: 'none', phase: 'none',
playback: 'roll', playback: 'roll',
secondaryTimer: 168950, secondaryTimer: 168950,
@@ -1275,7 +1268,6 @@ describe('getTimerPhase()', () => {
duration: null, duration: null,
elapsed: 0, elapsed: 0,
expectedFinish: null, expectedFinish: null,
finishedAt: null,
phase: 'none', phase: 'none',
playback: 'roll', playback: 'roll',
secondaryTimer: 168950, secondaryTimer: 168950,
+1 -5
View File
@@ -13,7 +13,7 @@ export const normaliseEndTime = (start: number, end: number) => (end < start ? e
* @returns {number | null} new current time or null if nothing is running * @returns {number | null} new current time or null if nothing is running
*/ */
export function getExpectedFinish(state: RuntimeState): MaybeNumber { export function getExpectedFinish(state: RuntimeState): MaybeNumber {
const { startedAt, finishedAt, duration, addedTime } = state.timer; const { startedAt, duration, addedTime } = state.timer;
if (state.eventNow === null) { if (state.eventNow === null) {
return null; return null;
@@ -27,10 +27,6 @@ export function getExpectedFinish(state: RuntimeState): MaybeNumber {
return null; return null;
} }
if (finishedAt !== null) {
return finishedAt;
}
const pausedTime = pausedAt != null ? clock - pausedAt : 0; const pausedTime = pausedAt != null ? clock - pausedAt : 0;
if (countToEnd) { if (countToEnd) {
@@ -26,7 +26,6 @@ const baseState: RuntimeState = {
duration: null, duration: null,
elapsed: null, elapsed: null,
expectedFinish: null, expectedFinish: null,
finishedAt: null,
phase: TimerPhase.None, phase: TimerPhase.None,
playback: Playback.Stop, playback: Playback.Stop,
secondaryTimer: null, secondaryTimer: null,
@@ -36,6 +35,7 @@ const baseState: RuntimeState = {
forceFinish: null, forceFinish: null,
pausedAt: null, pausedAt: null,
secondaryTarget: null, secondaryTarget: null,
hasFinished: false,
}, },
_rundown: { _rundown: {
totalDelay: 0, totalDelay: 0,
@@ -43,13 +43,13 @@ const mockState = {
duration: null, duration: null,
elapsed: null, elapsed: null,
expectedFinish: null, expectedFinish: null,
finishedAt: null,
playback: Playback.Stop, playback: Playback.Stop,
secondaryTimer: null, secondaryTimer: null,
startedAt: null, startedAt: null,
}, },
_timer: { _timer: {
pausedAt: null, pausedAt: null,
hasFinished: false,
}, },
} as RuntimeState; } as RuntimeState;
+9 -7
View File
@@ -48,6 +48,7 @@ export type RuntimeState = {
forceFinish: MaybeNumber; // whether we should declare an event as finished, will contain the finish time forceFinish: MaybeNumber; // whether we should declare an event as finished, will contain the finish time
pausedAt: MaybeNumber; pausedAt: MaybeNumber;
secondaryTarget: MaybeNumber; secondaryTarget: MaybeNumber;
hasFinished: boolean;
}; };
_rundown: { _rundown: {
totalDelay: number; // this value comes from rundown service totalDelay: number; // this value comes from rundown service
@@ -70,6 +71,7 @@ const runtimeState: RuntimeState = {
forceFinish: null, forceFinish: null,
pausedAt: null, pausedAt: null,
secondaryTarget: null, secondaryTarget: null,
hasFinished: false,
}, },
_rundown: { _rundown: {
totalDelay: 0, totalDelay: 0,
@@ -115,6 +117,7 @@ export function clearEventData() {
runtimeState._timer.forceFinish = null; runtimeState._timer.forceFinish = null;
runtimeState._timer.pausedAt = null; runtimeState._timer.pausedAt = null;
runtimeState._timer.secondaryTarget = null; runtimeState._timer.secondaryTarget = null;
runtimeState._timer.hasFinished = false;
} }
// clear all necessary data when doing a full stop and the event is unloaded // clear all necessary data when doing a full stop and the event is unloaded
@@ -144,6 +147,7 @@ export function clearState() {
runtimeState._timer.forceFinish = null; runtimeState._timer.forceFinish = null;
runtimeState._timer.pausedAt = null; runtimeState._timer.pausedAt = null;
runtimeState._timer.secondaryTarget = null; runtimeState._timer.secondaryTarget = null;
runtimeState._timer.hasFinished = false;
} }
/** /**
@@ -332,7 +336,7 @@ export function updateLoaded(event?: PlayableEvent): string | undefined {
runtimeState.timer.current = runtimeState.timer.duration; runtimeState.timer.current = runtimeState.timer.duration;
runtimeState.timer.startedAt = null; runtimeState.timer.startedAt = null;
runtimeState.timer.finishedAt = null; runtimeState._timer.hasFinished = false;
runtimeState.timer.addedTime = 0; runtimeState.timer.addedTime = 0;
runtimeState._timer.pausedAt = null; runtimeState._timer.pausedAt = null;
@@ -450,15 +454,14 @@ export function addTime(amount: number) {
// handle edge cases // handle edge cases
// !!! we need to handle side effects before updating the state // !!! we need to handle side effects before updating the state
const willGoNegative = amount < 0 && Math.abs(amount) > runtimeState.timer.current; const willGoNegative = amount < 0 && Math.abs(amount) > runtimeState.timer.current;
const hasFinished = runtimeState.timer.finishedAt !== null;
if (willGoNegative && !hasFinished) { if (willGoNegative && !runtimeState._timer.hasFinished) {
// set finished time so side effects are triggered // set finished time so side effects are triggered
runtimeState._timer.forceFinish = timeNow(); runtimeState._timer.forceFinish = timeNow();
} else { } else {
const willGoPositive = runtimeState.timer.current < 0 && runtimeState.timer.current + amount > 0; const willGoPositive = runtimeState.timer.current < 0 && runtimeState.timer.current + amount > 0;
if (willGoPositive) { if (willGoPositive) {
runtimeState.timer.finishedAt = null; runtimeState._timer.hasFinished = false;
} }
} }
@@ -522,11 +525,10 @@ export function update(): UpdateResult {
const finishedNow = const finishedNow =
Boolean(runtimeState._timer.forceFinish) || Boolean(runtimeState._timer.forceFinish) ||
(runtimeState.timer.current <= timerConfig.triggerAhead && runtimeState.timer.finishedAt === null); (runtimeState.timer.current <= timerConfig.triggerAhead && !runtimeState._timer.hasFinished);
if (finishedNow) { if (finishedNow) {
// reset state runtimeState._timer.hasFinished = true;
runtimeState.timer.finishedAt = runtimeState._timer.forceFinish ?? runtimeState.clock;
} else { } else {
runtimeState.timer.expectedFinish = getExpectedFinish(runtimeState); runtimeState.timer.expectedFinish = getExpectedFinish(runtimeState);
} }
@@ -12,7 +12,6 @@ export const runtimeStorePlaceholder: Readonly<RuntimeStore> = {
duration: null, // only changes if event changes duration: null, // only changes if event changes
elapsed: null, // changes on every update elapsed: null, // changes on every update
expectedFinish: null, // change can only be initiated by user, can roll over midnight expectedFinish: null, // change can only be initiated by user, can roll over midnight
finishedAt: null, // can change on update or user action
phase: TimerPhase.None, // can change on update or user action phase: TimerPhase.None, // can change on update or user action
playback: Playback.Stop, // change initiated by user playback: Playback.Stop, // change initiated by user
secondaryTimer: null, // change on every update secondaryTimer: null, // change on every update
@@ -22,9 +22,9 @@ export type TimerState = {
elapsed: MaybeNumber; elapsed: MaybeNumber;
/** time we expect timer to finish */ /** time we expect timer to finish */
expectedFinish: MaybeNumber; expectedFinish: MaybeNumber;
/** only if timer has already finished */ /** phase of of the running event */
finishedAt: MaybeNumber;
phase: TimerPhase; phase: TimerPhase;
/** playback state of the event */
playback: Playback; playback: Playback;
/** used for roll mode */ /** used for roll mode */
secondaryTimer: MaybeNumber; secondaryTimer: MaybeNumber;