diff --git a/apps/client/src/common/utils/offset.ts b/apps/client/src/common/utils/offset.ts index 532fae07e..ecaf6f774 100644 --- a/apps/client/src/common/utils/offset.ts +++ b/apps/client/src/common/utils/offset.ts @@ -21,5 +21,6 @@ export function getOffsetText(offset: MaybeNumber): string { export function getOffsetState(offset: MaybeNumber): 'over' | 'under' | 'muted' | null { if (offset === null) return 'muted'; if (offset === 0) return null; - return offset < 0 ? 'over' : 'under'; + // a positive value means that we are in over time aka behind schedule + return offset > 0 ? 'over' : 'under'; } diff --git a/apps/client/src/features/overview/composite/TimeElements.tsx b/apps/client/src/features/overview/composite/TimeElements.tsx index d8fee75c8..9caaae1f0 100644 --- a/apps/client/src/features/overview/composite/TimeElements.tsx +++ b/apps/client/src/features/overview/composite/TimeElements.tsx @@ -167,9 +167,8 @@ export function OffsetOverview() { const { offset, playback } = useRuntimePlaybackOverview(); const isPlaying = isPlaybackActive(playback); - const correctedOffset = offset * -1; const offsetState = getOffsetState(isPlaying ? offset : null); - const offsetText = getOffsetText(isPlaying ? correctedOffset : null); + const offsetText = getOffsetText(isPlaying ? offset : null); return ; } diff --git a/apps/client/src/features/rundown/entry-editor/GroupEditor.tsx b/apps/client/src/features/rundown/entry-editor/GroupEditor.tsx index 2ed797d76..bfd51d8ee 100644 --- a/apps/client/src/features/rundown/entry-editor/GroupEditor.tsx +++ b/apps/client/src/features/rundown/entry-editor/GroupEditor.tsx @@ -50,8 +50,8 @@ export default function GroupEditor({ group }: GroupEditorProps) { ); const isEditor = window.location.pathname.includes('editor'); - const planOffset = typeof group.targetDuration !== 'number' ? null : group.duration - group.targetDuration; - const planOffsetLabel = planOffset !== null ? getOffsetState(planOffset * -1) : null; + const planOffset = group.targetDuration === null ? null : group.duration - group.targetDuration; + const planOffsetLabel = planOffset !== null ? getOffsetState(planOffset) : null; return (
diff --git a/apps/client/src/features/rundown/rundown-group/RundownGroup.tsx b/apps/client/src/features/rundown/rundown-group/RundownGroup.tsx index 4db25e537..c558c31da 100644 --- a/apps/client/src/features/rundown/rundown-group/RundownGroup.tsx +++ b/apps/client/src/features/rundown/rundown-group/RundownGroup.tsx @@ -10,6 +10,7 @@ import { import { useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { EntryId, OntimeGroup } from 'ontime-types'; +import { MILLIS_PER_MINUTE } from 'ontime-utils'; import IconButton from '../../../common/components/buttons/IconButton'; import { useContextMenu } from '../../../common/hooks/useContextMenu'; @@ -102,8 +103,11 @@ export default function RundownGroup({ data, hasCursor, collapsed, onCollapse }: if (offset === 0) { return [null, 'under']; } - - return [offset < 0 ? `-${formatDuration(offset * -1)}` : `+${formatDuration(offset)}`, getOffsetState(offset * -1)]; + const absOffset = Math.abs(offset); + return [ + `${offset < 0 ? '-' : '+'}${formatDuration(absOffset, absOffset > 2 * MILLIS_PER_MINUTE)}`, + getOffsetState(offset), + ]; })(); const dragStyle = { diff --git a/apps/server/src/services/__tests__/timerUtils.test.ts b/apps/server/src/services/__tests__/timerUtils.test.ts index 8a6bed563..1496b3b5c 100644 --- a/apps/server/src/services/__tests__/timerUtils.test.ts +++ b/apps/server/src/services/__tests__/timerUtils.test.ts @@ -742,7 +742,7 @@ describe('getRuntimeOffset()', () => { } as RuntimeState; const { offsetAbs } = getRuntimeOffset(state); - expect(offsetAbs).toBe(-50); + expect(offsetAbs).toBe(50); }); it('added time subtracts time offset (positive offset)', () => { @@ -766,7 +766,7 @@ describe('getRuntimeOffset()', () => { } as RuntimeState; const { offsetAbs } = getRuntimeOffset(state); - expect(offsetAbs).toBe(-60); + expect(offsetAbs).toBe(60); }); it('considers running overtime (negative offset)', () => { @@ -791,7 +791,7 @@ describe('getRuntimeOffset()', () => { } as RuntimeState; const { offsetAbs } = getRuntimeOffset(state); - expect(offsetAbs).toBe(-10); + expect(offsetAbs).toBe(10); }); it('paused time is delayed time (negative offset)', () => { @@ -817,7 +817,7 @@ describe('getRuntimeOffset()', () => { } as RuntimeState; const { offsetAbs } = getRuntimeOffset(state); - expect(offsetAbs).toBe(-25); + expect(offsetAbs).toBe(25); }); it('offset doesnt exist if we havent started', () => { @@ -857,46 +857,6 @@ describe('getRuntimeOffset()', () => { expect(offsetAbs).toBe(0); }); - it('handles loaded event', () => { - const state = { - clock: 79521653, - eventNow: { - id: '835242', - timeStart: 81000000, - timeEnd: 84600000, - duration: 3600000, - timeStrategy: 'lock-duration', - linkStart: false, - endAction: 'none', - timerType: 'count-down', - delay: 0, - }, - runtime: { - selectedEventIndex: 1, - numEvents: 2, - offsetAbs: -81000000, - plannedStart: 77400000, - plannedEnd: 84600000, - actualStart: 79443403, - expectedEnd: null, - }, - timer: { - addedTime: 0, - current: 3600000, - duration: 3600000, - elapsed: null, - expectedFinish: null, - playback: 'armed', - secondaryTimer: null, - startedAt: null, - }, - _timer: { pausedAt: null }, - } as RuntimeState; - - const { offsetAbs } = getRuntimeOffset(state); - expect(offsetAbs).toBe(81000000 - 79521653); // clock - timestart - }); - it('with time-to-end, offsets dont exist if we are not in overtime', () => { const state = { clock: 80000000, // 22:13:20 @@ -996,7 +956,7 @@ describe('getRuntimeOffset()', () => { } as RuntimeState; const { offsetAbs } = getRuntimeOffset(state); - expect(offsetAbs).toBe(-400000); // <--- offset is always the overtime + expect(offsetAbs).toBe(400000); // <--- offset is always the overtime }); it('handles time-to-end started after the end time', () => { @@ -1038,8 +998,8 @@ describe('getRuntimeOffset()', () => { const updateCurrent = getCurrent(state); state.timer.current = updateCurrent; const { offsetAbs } = getRuntimeOffset(state); - expect(millisToString(offsetAbs)).toBe('-00:16:40'); - expect(offsetAbs).toBe(81000000 - 82000000); // <-- planned end - now + expect(millisToString(offsetAbs)).toBe('00:16:40'); + expect(offsetAbs).toBe(82000000 - 81000000); // <-- now - planned end }); }); @@ -1089,7 +1049,7 @@ describe('getoffsetRel()', () => { } as RuntimeState; const { offsetAbs, offsetRel } = getRuntimeOffset(state); - expect(offsetAbs).toBe(-50); + expect(offsetAbs).toBe(50); expect(offsetRel).toBe(0); }); it('relative offset is 0 when starting before the planed time', () => { @@ -1113,7 +1073,7 @@ describe('getoffsetRel()', () => { } as RuntimeState; const { offsetAbs, offsetRel } = getRuntimeOffset(state); - expect(offsetAbs).toBe(50); + expect(offsetAbs).toBe(-50); expect(offsetRel).toBe(0); }); }); diff --git a/apps/server/src/services/timerUtils.ts b/apps/server/src/services/timerUtils.ts index 6d3a41723..ff075f3fb 100644 --- a/apps/server/src/services/timerUtils.ts +++ b/apps/server/src/services/timerUtils.ts @@ -109,57 +109,44 @@ export function skippedOutOfEvent(state: RuntimeState, previousTime: number, ski /** * Calculates difference between the runtime and the schedule of an event - * Positive offset is time ahead - * Negative offset is time delayed + * Positive offset is over time / behind schedule + * Negative offset is under time / ahead of schedule */ export function getRuntimeOffset(state: RuntimeState): { offsetAbs: number; offsetRel: number } { + const { eventNow, clock } = state; + const { addedTime, current, startedAt } = state.timer; // nothing to calculate if there are no loaded events or if we havent started - if (state.eventNow === null || state.runtime.actualStart === null) { + if (eventNow === null || startedAt === null) { return { offsetAbs: 0, offsetRel: 0 }; } - const { clock } = state; - const { countToEnd, timeStart } = state.eventNow; - const { addedTime, current, startedAt } = state.timer; - const { actualStart, plannedStart } = state.runtime; + const { countToEnd, timeStart } = eventNow; + const { plannedStart, actualStart } = state.runtime; // eslint-disable-next-line no-unused-labels -- dev code path DEV: { // we know current exists as long as eventNow exists if (current === null) throw new Error('timerUtils.getRuntimeOffset: state.timer.current must be set'); if (plannedStart === null) throw new Error('timerUtils.getRuntimeOffset: state.runtime.plannedStart must be set'); + if (actualStart === null) throw new Error('timerUtils.getRuntimeOffset: state.runtime.actualStart must be set'); } - // if we havent started, but the timer is armed - // the offset is the difference to the schedule - if (startedAt === null) { - return { offsetAbs: timeStart - clock, offsetRel: 0 }; - } + // difference between planned event start and actual event start (will be positive if we stared behind ) + const eventStartOffset = startedAt - timeStart; - const overtime = Math.min(current, 0); - // in time-to-end, offset is overtime + // how long has the event been running over (is a negative number when in over timer so inverted before adding to offset) + const overtime = Math.abs(Math.min(current, 0)); - const startOffset = timeStart - startedAt; + // time the playback was paused, the different from now to when we paused is added to the offset TODO: brakes when crossing midnight const pausedTime = state._timer.pausedAt === null ? 0 : clock - state._timer.pausedAt; - // startOffset - difference between scheduled start and actual start - // addedTime - time added by user (negative offset) - // pausedTime - time the playback was paused (negative offset) - // overtime - how long the timer has been over-running (negative offset) - const offset = startOffset - addedTime - pausedTime + overtime; + const offsetAbs = eventStartOffset + overtime + pausedTime + addedTime; - // offset between planned rundown start and actual rundown start - const rundownStartOffset = actualStart - plannedStart; + // the relative offset i the same as the absolute offset but adjusted relative to the actual start time + const offsetRel = offsetAbs + plannedStart - actualStart; - // offset offset relative to the actual rundown start - const offsetRel = offset + rundownStartOffset; - - // in time-to-end, offset is overtime - if (countToEnd) { - return { offsetAbs: overtime, offsetRel }; - } - - return { offsetAbs: offset, offsetRel }; + // in case of count to end, the absolute offset is just the overtime + return countToEnd ? { offsetAbs: overtime, offsetRel } : { offsetAbs, offsetRel }; } /** diff --git a/apps/server/src/stores/__tests__/runtimeState.test.ts b/apps/server/src/stores/__tests__/runtimeState.test.ts index d7d11b9d6..dbeb61abf 100644 --- a/apps/server/src/stores/__tests__/runtimeState.test.ts +++ b/apps/server/src/stores/__tests__/runtimeState.test.ts @@ -67,17 +67,20 @@ beforeAll(() => { }); describe('mutation on runtimeState', () => { + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime('jan 1 00:01'); + }); afterEach(() => { vi.clearAllMocks(); + vi.useRealTimers(); }); describe('playback operations', async () => { it('refuses if nothing is loaded', async () => { // force update - vi.useFakeTimers(); await initRundown(makeRundown({}), {}); vi.runAllTimers(); - vi.useRealTimers(); let success = start(mockState); expect(success).toBe(false); @@ -93,10 +96,8 @@ describe('mutation on runtimeState', () => { order: [mockEvent.id, 'event2'], }); // force update - vi.useFakeTimers(); await initRundown(mockRundown, {}); vi.runAllTimers(); - vi.useRealTimers(); const { metadata, rundown } = rundownCache.get(); load(mockEvent, rundown, metadata); @@ -108,6 +109,7 @@ describe('mutation on runtimeState', () => { expect(newState.groupNow).toBeNull(); // 2. Start event + vi.setSystemTime('jan 1 00:02'); let success = start(); newState = getState(); expect(success).toBe(true); @@ -117,6 +119,7 @@ describe('mutation on runtimeState', () => { expect(newState.runtime.actualStart).toBe(newState.clock); // 3. Pause event + vi.setSystemTime('jan 1 00:03'); success = pause(); newState = getState(); expect(success).toBe(true); @@ -131,6 +134,7 @@ describe('mutation on runtimeState', () => { expect(success).toBe(false); // 4. Restart event + vi.setSystemTime('jan 1 00:04'); success = start(); newState = getState(); expect(success).toBe(true); @@ -150,6 +154,7 @@ describe('mutation on runtimeState', () => { expect(newState._timer.pausedAt).toBeNull(); // 5. Stop event + vi.setSystemTime('jan 1 00:05'); success = stop(); newState = getState(); expect(success).toBe(true); @@ -163,80 +168,81 @@ describe('mutation on runtimeState', () => { }); expect(newState.runtime.actualStart).toBeNull(); }); + }); - test('runtime offset', async () => { - const entries = { - event1: { ...mockEvent, id: 'event1', timeStart: 0, timeEnd: 1000, duration: 1000, parent: null }, - event2: { ...mockEvent, id: 'event2', timeStart: 1000, timeEnd: 1500, duration: 500, parent: null }, - }; - const mockRundown = makeRundown({ entries, order: ['event1', 'event2'] }); + test('runtime offset', async () => { + const entries = { + event1: { ...mockEvent, id: 'event1', timeStart: 0, timeEnd: 1000, duration: 1000, parent: null }, + event2: { ...mockEvent, id: 'event2', timeStart: 1000, timeEnd: 1500, duration: 500, parent: null }, + }; + const mockRundown = makeRundown({ entries, order: ['event1', 'event2'] }); - // force update - vi.useFakeTimers(); - await initRundown(mockRundown, {}); - vi.runAllTimers(); - vi.useRealTimers(); + // force update + await initRundown(mockRundown, {}); + vi.runAllTimers(); - const { metadata, rundown } = rundownCache.get(); + const { metadata, rundown } = rundownCache.get(); - // 1. Load event - load(entries.event1, rundown, metadata); - let newState = getState(); - expect(newState.runtime.actualStart).toBeNull(); - expect(newState.runtime.plannedStart).toBe(0); - expect(newState.runtime.plannedEnd).toBe(1500); - expect(newState.groupNow).toBeNull(); - expect(newState.runtime.offsetAbs).toBe(0); + // 1. Load event + vi.setSystemTime('jan 1 00:09'); + load(entries.event1, rundown, metadata); + let newState = getState(); + expect(newState.runtime.actualStart).toBeNull(); + expect(newState.runtime.plannedStart).toBe(0); + expect(newState.runtime.plannedEnd).toBe(1500); + expect(newState.groupNow).toBeNull(); + expect(newState.runtime.offsetAbs).toBe(0); - // 2. Start event - start(); - newState = getState(); - const firstStart = newState.clock; - if (newState.runtime.offsetAbs === null) { - throw new Error('Value cannot be null at this stage'); - } + // 2. Start event + vi.setSystemTime('jan 1 00:10'); + start(); + newState = getState(); + const firstStart = newState.clock; + if (newState.runtime.offsetAbs === null) { + throw new Error('Value cannot be null at this stage'); + } - expect(newState.runtime.actualStart).toBe(newState.clock); - expect(newState.runtime.offsetAbs).toBe(entries.event1.timeStart - newState.clock); - expect(newState.runtime.expectedEnd).toBe(entries.event2.timeEnd - newState.runtime.offsetAbs); + expect(newState.runtime.actualStart).toBe(newState.clock); + expect(newState.runtime.offsetAbs).toBe(newState.clock - entries.event1.timeStart); + expect(newState.runtime.expectedEnd).toBe(entries.event2.timeEnd + newState.runtime.offsetAbs); - // 3. Next event - load(entries.event2, rundown, metadata); - start(); + // 3. Next event + vi.setSystemTime('jan 1 00:12'); + load(entries.event2, rundown, metadata); + start(); - newState = getState(); - if (newState.runtime.actualStart === null || newState.runtime.offsetAbs === null) { - throw new Error('Value cannot be null at this stage'); - } + newState = getState(); + if (newState.runtime.actualStart === null || newState.runtime.offsetAbs === null) { + throw new Error('Value cannot be null at this stage'); + } - // there is a case where the calculation time overflows the millisecond which makes - // tests fail - const forgivingActualStart = Math.abs(newState.runtime.actualStart - firstStart); - expect(forgivingActualStart).toBeLessThanOrEqual(1); - // we are over-under, the difference between the schedule and the actual start - const delayBefore = entries.event2.timeStart - newState.clock; - expect(newState.runtime.offsetAbs).toBe(delayBefore); - // finish is the difference between the runtime and the schedule - expect(newState.runtime.expectedEnd).toBe(entries.event2.timeEnd - newState.runtime.offsetAbs); - expect(newState.groupNow).toBeNull(); + // there is a case where the calculation time overflows the millisecond which makes + // tests fail + const forgivingActualStart = Math.abs(newState.runtime.actualStart - firstStart); + expect(forgivingActualStart).toBeLessThanOrEqual(1); + // we are over-under, the difference between the schedule and the actual start + const delayBefore = newState.clock - entries.event2.timeStart; + expect(newState.runtime.offsetAbs).toBe(delayBefore); + // finish is the difference between the runtime and the schedule + expect(newState.runtime.expectedEnd).toBe(newState.runtime.offsetAbs + entries.event2.timeEnd); + expect(newState.groupNow).toBeNull(); - // 4. Add time - addTime(10); - newState = getState(); - if (newState.runtime.offsetAbs === null) { - throw new Error('Value cannot be null at this stage'); - } + // 4. Add time + addTime(10); + newState = getState(); + if (newState.runtime.offsetAbs === null) { + throw new Error('Value cannot be null at this stage'); + } - expect(newState.runtime.offsetAbs).toBe(delayBefore - 10); - expect(newState.runtime.expectedEnd).toBe(entries.event2.timeEnd - newState.runtime.offsetAbs); + expect(newState.runtime.offsetAbs).toBe(delayBefore + 10); + expect(newState.runtime.expectedEnd).toBe(entries.event2.timeEnd + newState.runtime.offsetAbs); - // 5. Stop event - stop(); - newState = getState(); - expect(newState.runtime.actualStart).toBeNull(); - expect(newState.runtime.offsetAbs).toBe(0); - expect(newState.runtime.expectedEnd).toBeNull(); - }); + // 5. Stop event + stop(); + newState = getState(); + expect(newState.runtime.actualStart).toBeNull(); + expect(newState.runtime.offsetAbs).toBe(0); + expect(newState.runtime.expectedEnd).toBeNull(); }); }); @@ -326,7 +332,7 @@ describe('roll mode', () => { start(); const result = roll(rundown, metadata); expect(result).toStrictEqual({ eventId: '1', didStart: false }); - expect(getState().runtime.offsetAbs).toBe(1000); + expect(getState().runtime.offsetAbs).toBe(-1000); }); }); @@ -353,18 +359,18 @@ describe('roll mode', () => { const currentOffset = getState().runtime.offsetAbs; let result = roll(rundown, metadata, getState().runtime.offsetAbs); expect(result).toStrictEqual({ eventId: '1', didStart: false }); - // the current offset should be maintain by roll mode whn taking over from play + // the current offset should be maintain by roll mode when taking over from play expect(getState().runtime.offsetAbs).toBe(currentOffset); vi.setSystemTime('jan 1 00:00:01'); result = roll(rundown, metadata, getState().runtime.offsetAbs); expect(result).toStrictEqual({ eventId: '2', didStart: true }); - expect(getState().runtime.offsetAbs).toBe(1000); + expect(getState().runtime.offsetAbs).toBe(-1000); vi.setSystemTime('jan 1 00:00:02'); result = roll(rundown, metadata, getState().runtime.offsetAbs); expect(result).toStrictEqual({ eventId: '3', didStart: true }); - expect(getState().runtime.offsetAbs).toBe(1000); + expect(getState().runtime.offsetAbs).toBe(-1000); vi.useRealTimers(); }); diff --git a/apps/server/src/stores/runtimeState.ts b/apps/server/src/stores/runtimeState.ts index 8b6d19cb4..3b7c67937 100644 --- a/apps/server/src/stores/runtimeState.ts +++ b/apps/server/src/stores/runtimeState.ts @@ -314,7 +314,7 @@ export function updateLoaded(event?: PlayableEvent): string | undefined { // handle edge cases with roll if (runtimeState.timer.playback === Playback.Roll) { - const offsetClock = runtimeState.clock + runtimeState.runtime.offsetAbs; + const offsetClock = runtimeState.clock - runtimeState.runtime.offsetAbs; // if waiting to roll, we update the targets and potentially start the timer if (runtimeState._timer.secondaryTarget !== null) { if (runtimeState.eventNow.timeStart < offsetClock && offsetClock < runtimeState.eventNow.timeEnd) { @@ -391,7 +391,6 @@ export function start(state: RuntimeState = runtimeState): boolean { state.timer.expectedFinish = getExpectedFinish(state); state.timer.elapsed = 0; - // update runtime delays: over - under if (state.runtime.actualStart === null) { state.runtime.actualStart = state.clock; } @@ -521,7 +520,6 @@ export function update(): UpdateResult { const { offsetAbs, offsetRel } = getRuntimeOffset(runtimeState); runtimeState.runtime.offsetAbs = offsetAbs; runtimeState.runtime.offsetRel = offsetRel; - // runtimeState.runtime.expectedEnd = getExpectedEnd(runtimeState); const finishedNow = Boolean(runtimeState._timer.forceFinish) || @@ -596,7 +594,7 @@ export function roll( runtimeState.timer.expectedFinish = normalisedEndTime; //account for offset - const offsetClock = runtimeState.clock + runtimeState.runtime.offsetAbs; + const offsetClock = runtimeState.clock - runtimeState.runtime.offsetAbs; // state catch up runtimeState.timer.duration = calculateDuration(runtimeState.eventNow.timeStart, normalisedEndTime); @@ -635,7 +633,7 @@ export function roll( //account for offset but we only keep it if passed to us runtimeState.runtime.offsetAbs = offset; - const offsetClock = runtimeState.clock + runtimeState.runtime.offsetAbs; + const offsetClock = runtimeState.clock - runtimeState.runtime.offsetAbs; const { index, isPending } = loadRoll(rundown, metadata, offsetClock); diff --git a/packages/types/src/definitions/runtime/Runtime.type.ts b/packages/types/src/definitions/runtime/Runtime.type.ts index 07d382574..4a0ea88ef 100644 --- a/packages/types/src/definitions/runtime/Runtime.type.ts +++ b/packages/types/src/definitions/runtime/Runtime.type.ts @@ -8,7 +8,7 @@ export enum OffsetMode { export type Runtime = { selectedEventIndex: MaybeNumber; numEvents: number; - offsetAbs: number; + offsetAbs: number; // a positive value means that we are in over time aka behind schedule offsetRel: number; plannedStart: MaybeNumber; actualStart: MaybeNumber; diff --git a/packages/utils/src/date-utils/getExpectedStart.test.ts b/packages/utils/src/date-utils/getExpectedStart.test.ts index b3906dcf6..30082f87c 100644 --- a/packages/utils/src/date-utils/getExpectedStart.test.ts +++ b/packages/utils/src/date-utils/getExpectedStart.test.ts @@ -33,7 +33,7 @@ describe('getExpectedStart()', () => { const testState = { currentDay: 0, totalGap: 0, - offset: -20, + offset: 20, offsetMode: OffsetMode.Absolute, actualStart: null, plannedStart: null, @@ -52,7 +52,7 @@ describe('getExpectedStart()', () => { const testState = { currentDay: 0, totalGap: 0, - offset: 10, + offset: -10, offsetMode: OffsetMode.Absolute, actualStart: null, plannedStart: null, @@ -71,7 +71,7 @@ describe('getExpectedStart()', () => { const testState = { currentDay: 0, totalGap: 20, - offset: -20, + offset: 20, offsetMode: OffsetMode.Absolute, actualStart: null, plannedStart: null, @@ -90,7 +90,7 @@ describe('getExpectedStart()', () => { const testState = { currentDay: 0, totalGap: 10, - offset: -20, + offset: 20, offsetMode: OffsetMode.Absolute, actualStart: 0, plannedStart: 0, @@ -219,12 +219,12 @@ describe('getExpectedStart()', () => { expect(getExpectedStart(testEvent, { ...testState, isLinkedToLoaded: true })).toBe(120); expect(getExpectedStart(testEvent, { ...testState, isLinkedToLoaded: false })).toBe(120); - testState.offset = 5; // remove 5 with addtime - we are ahead of time + testState.offset = -5; // remove 5 with addtime - we are ahead of time expect(getExpectedStart(testEvent, { ...testState, isLinkedToLoaded: true })).toBe(115); - expect(getExpectedStart(testEvent, { ...testState, isLinkedToLoaded: false })).toBe(120); // unlocked evets will stay on schedule + expect(getExpectedStart(testEvent, { ...testState, isLinkedToLoaded: false })).toBe(120); // unlinked events will stay on schedule - testState.offset = -5; // add 5 with addtime - we are behind + testState.offset = +5; // add 5 with addtime - we are behind expect(getExpectedStart(testEvent, { ...testState, isLinkedToLoaded: true })).toBe(125); expect(getExpectedStart(testEvent, { ...testState, isLinkedToLoaded: false })).toBe(125); diff --git a/packages/utils/src/date-utils/getExpectedStart.ts b/packages/utils/src/date-utils/getExpectedStart.ts index 5959443e8..7fb9d3395 100644 --- a/packages/utils/src/date-utils/getExpectedStart.ts +++ b/packages/utils/src/date-utils/getExpectedStart.ts @@ -43,14 +43,14 @@ export function getExpectedStart( const scheduledStartTime = normalisedTimeStart + relativeStartOffset; - const offsetStartTime = scheduledStartTime - offset; + const offsetStartTime = scheduledStartTime + offset; if (isLinkedToLoaded) { //if we are directly linked back to the loaded event we just follow the offset return offsetStartTime; } - const gapsCanCompensateForOffset = totalGap + offset >= 0; + const gapsCanCompensateForOffset = totalGap > offset; if (gapsCanCompensateForOffset) { // if we are ahead of schedule or the gap can compensate for the amount we are behind then expect to start at the scheduled time return scheduledStartTime;