diff --git a/apps/server/src/stores/runtimeState.ts b/apps/server/src/stores/runtimeState.ts index 1df9255ef..36edb0bd5 100644 --- a/apps/server/src/stores/runtimeState.ts +++ b/apps/server/src/stores/runtimeState.ts @@ -47,6 +47,7 @@ export type RuntimeState = { timer: TimerState; // private properties of the timer calculations _timer: { + forceFinish: MaybeNumber; totalDelay: number; // this value comes from rundown service pausedAt: MaybeNumber; secondaryTarget: MaybeNumber; @@ -62,6 +63,7 @@ const runtimeState: RuntimeState = { runtime: { ...initialRuntime }, timer: { ...initialTimer }, _timer: { + forceFinish: null, totalDelay: 0, pausedAt: null, secondaryTarget: null, @@ -331,15 +333,14 @@ export function addTime(amount: number) { return false; } - runtimeState.timer.addedTime += amount; - runtimeState.timer.expectedFinish += amount; - runtimeState.timer.current += amount; - // handle edge cases + // !!! we need to handle side effects before updating the state const willGoNegative = amount < 0 && Math.abs(amount) > runtimeState.timer.current; const hasFinished = runtimeState.timer.finishedAt !== null; + if (willGoNegative && !hasFinished) { - runtimeState.timer.finishedAt = clock.timeNow(); + // set finished time so side effects are triggered + runtimeState._timer.forceFinish = clock.timeNow(); } else { const willGoPositive = runtimeState.timer.current < 0 && runtimeState.timer.current + amount > 0; if (willGoPositive) { @@ -347,6 +348,11 @@ export function addTime(amount: number) { } } + // we can update the state after handling the side effects + runtimeState.timer.addedTime += amount; + runtimeState.timer.expectedFinish += amount; + runtimeState.timer.current += amount; + // update runtime delays: over - under runtimeState.runtime.offset = getRuntimeOffset(runtimeState); runtimeState.runtime.expectedEnd = getExpectedEnd(runtimeState); @@ -405,16 +411,21 @@ export function update(): UpdateResult { function onPlayUpdate() { let isFinished = false; runtimeState.timer.current = getCurrent(runtimeState); + const shouldForceFinish = runtimeState._timer.forceFinish !== null; const finishedNow = - runtimeState.timer.current <= timerConfig.triggerAhead && runtimeState.timer.finishedAt === null; + shouldForceFinish || + (runtimeState.timer.current <= timerConfig.triggerAhead && runtimeState.timer.finishedAt === null); if (runtimeState.timer.playback === Playback.Play && finishedNow) { - runtimeState.timer.finishedAt = runtimeState.clock; + runtimeState.timer.finishedAt = runtimeState._timer.forceFinish ?? runtimeState.clock; isFinished = true; } else { runtimeState.timer.expectedFinish = getExpectedFinish(runtimeState); } + if (shouldForceFinish) { + runtimeState._timer.forceFinish = null; + } runtimeState.timer.elapsed = runtimeState.timer.duration - runtimeState.timer.current; return { isFinished };