mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-08 08:53:51 +00:00
refactor: finish of count-to-end is plan or start time
This commit is contained in:
@@ -42,7 +42,7 @@
|
||||
"scripts": {
|
||||
"addversion": "node -p \"'export const ONTIME_VERSION = ' + JSON.stringify(require('../../package.json').version) + ';'\" > src/ONTIME_VERSION.js",
|
||||
"postinstall": "pnpm addversion",
|
||||
"dev": "cross-env NODE_ENV=development tsx watch --tsconfig tsconfig.app.json ./src/index.ts",
|
||||
"dev": "cross-env PORT=4001 NODE_ENV=development tsx watch --tsconfig tsconfig.app.json ./src/index.ts",
|
||||
"dev:electron": "pnpm dev",
|
||||
"dev:inspect": "cross-env NODE_ENV=development tsx watch --tsconfig tsconfig.app.json --inspect ./src/index.ts",
|
||||
"lint": "oxlint --quiet --type-aware",
|
||||
|
||||
@@ -268,29 +268,31 @@ describe('getExpectedFinish()', () => {
|
||||
const calculatedFinish = getExpectedFinish(state);
|
||||
expect(calculatedFinish).toBe(30);
|
||||
});
|
||||
it('handles events that finish the day after', () => {
|
||||
|
||||
it('returns the start time for count to end times which started late', () => {
|
||||
const state = {
|
||||
eventNow: {
|
||||
timeEnd: 600000, // 00:10:00
|
||||
timeStart: 20 * MILLIS_PER_HOUR, // 20:00:00
|
||||
timeEnd: 21 * MILLIS_PER_MINUTE, // 21:00:00
|
||||
countToEnd: true,
|
||||
},
|
||||
timer: {
|
||||
addedTime: 0,
|
||||
startedAt: 79200000, // 22:00:00
|
||||
startedAt: 22 * MILLIS_PER_HOUR, // 22:00:00 <--------------
|
||||
},
|
||||
_timer: {
|
||||
pausedAt: null,
|
||||
hasFinished: false,
|
||||
},
|
||||
rundown: {
|
||||
actualStart: 79200000,
|
||||
plannedEnd: 600000,
|
||||
actualStart: 20 * MILLIS_PER_HOUR, // 20:00:00
|
||||
plannedEnd: 21 * MILLIS_PER_HOUR, // 21:00:00
|
||||
},
|
||||
} as RuntimeState;
|
||||
|
||||
const calculatedFinish = getExpectedFinish(state);
|
||||
// expected finish is not a duration but a point in time
|
||||
expect(calculatedFinish).toBe(600000);
|
||||
// timeEnd is numerically before startedAt for overnight events, so expectedFinish is clamped to startedAt
|
||||
expect(calculatedFinish).toBe(22 * MILLIS_PER_HOUR);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Expose errors where we reach invalid states
|
||||
* used mostly in shouldCrashDev patterns
|
||||
*/
|
||||
export class InvalidStateError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { Day, MaybeNumber, TimeOfDay, TimerPhase } from 'ontime-types';
|
||||
import { MILLIS_PER_HOUR, checkIsNow, dayInMs, isPlaybackActive } from 'ontime-utils';
|
||||
|
||||
import type { RuntimeState } from '../stores/runtimeState.js';
|
||||
import { InvalidStateError } from './generic.errors.js';
|
||||
|
||||
/**
|
||||
* handle events that span over midnight
|
||||
@@ -24,37 +25,35 @@ export function hasCrossedMidnight(previous: TimeOfDay, current: TimeOfDay): boo
|
||||
* @returns {number | null} new current time or null if nothing is running
|
||||
*/
|
||||
export function getExpectedFinish(state: RuntimeState): MaybeNumber {
|
||||
const { startedAt, duration, addedTime } = state.timer;
|
||||
|
||||
if (state.eventNow === null) {
|
||||
// if there is a loaded event it must have started
|
||||
// either way, we have no expected finish if nothing is playing
|
||||
if (state.eventNow === null || state.timer.startedAt === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { countToEnd, timeEnd } = state.eventNow;
|
||||
const { pausedAt } = state._timer;
|
||||
const { clock } = state;
|
||||
|
||||
if (startedAt === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const pausedTime = pausedAt != null ? clock - pausedAt : 0;
|
||||
|
||||
if (countToEnd) {
|
||||
if (state.eventNow.countToEnd) {
|
||||
// count to end events are anchored to their fixed end: added time and pauses
|
||||
// do not move the end, they surface as offset instead (see getRuntimeOffset)
|
||||
return timeEnd;
|
||||
return Math.max(state.eventNow.timeEnd, state.timer.startedAt);
|
||||
}
|
||||
|
||||
const pausedTime = state._timer.pausedAt != null ? state.clock - state._timer.pausedAt : 0;
|
||||
|
||||
DEV: {
|
||||
if (state.timer.duration === null) {
|
||||
throw new InvalidStateError('a running timer cannot have null duration');
|
||||
}
|
||||
}
|
||||
|
||||
// handle events that finish the day after
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- duration exists if ther eis a timer
|
||||
const expectedFinish = startedAt + duration! + addedTime + pausedTime;
|
||||
const expectedFinish = state.timer.startedAt + state.timer.duration + state.timer.addedTime + pausedTime;
|
||||
if (expectedFinish > dayInMs) {
|
||||
return expectedFinish - dayInMs;
|
||||
}
|
||||
|
||||
// an event cannot finish before it started (user added too much negative time)
|
||||
return Math.max(expectedFinish, startedAt);
|
||||
return Math.max(expectedFinish, state.timer.startedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user