fix: count-to-end events break link chain

This commit is contained in:
Carlos Valente
2026-06-20 10:16:25 +02:00
parent daa032e92c
commit 0ff226f0ec
12 changed files with 479 additions and 89 deletions
+16 -10
View File
@@ -4,6 +4,7 @@ import {
MILLIS_PER_MINUTE,
MILLIS_PER_SECOND,
formatFromMillis,
getExpectedEnd,
getExpectedStart,
} from 'ontime-utils';
@@ -172,23 +173,28 @@ export function getExpectedTimesFromExtendedEvent(
) {
if (event === null) return { expectedStart: 0, timeToStart: 0, expectedEnd: 0, plannedEnd: 0 };
const expectedStartState = {
totalGap: event.totalGap,
isLinkedToLoaded: event.isLinkedToLoaded,
...state,
};
const expectedStart = getExpectedStart(
{ timeStart: event.timeStart, delay: event.delay, dayOffset: event.dayOffset },
{
totalGap: event.totalGap,
isLinkedToLoaded: event.isLinkedToLoaded,
...state,
},
expectedStartState,
);
const plannedEnd = event.timeStart + event.duration + event.delay;
// count to end events are fixed to the scheduled end and ignore delays
const delayToAdd = event.countToEnd ? 0 : event.delay;
const plannedEnd = event.timeStart + event.duration + delayToAdd;
// we let timeToStart go negative to allow the UI to show due timers
const timeToStart = expectedStart - state.clock;
return {
expectedStart,
timeToStart: expectedStart - state.clock,
expectedEnd: event.countToEnd
? Math.max(expectedStart + event.duration, plannedEnd)
: expectedStart + event.duration,
timeToStart,
expectedEnd: getExpectedEnd(event, expectedStartState),
plannedEnd,
};
}
@@ -151,9 +151,8 @@ type ScheduleTimeProps = {
showExpected: boolean;
};
//TODO: consider relative mode
export function ScheduleTime(props: ScheduleTimeProps) {
const { event, showExpected } = props;
const { timeStart, duration, delay, expectedStart, countToEnd } = event;
export function ScheduleTime({ event, showExpected }: ScheduleTimeProps) {
const { timeStart, duration, delay, expectedStart, expectedEnd, countToEnd } = event;
const plannedStart = timeStart + delay + event.dayOffset * dayInMs;
@@ -163,8 +162,14 @@ export function ScheduleTime(props: ScheduleTimeProps) {
const plannedStateClass = isExpectedValueShow ? 'sub__schedule--strike' : delay !== 0 ? 'sub__schedule--delayed' : '';
const expectedStateClass = `sub__schedule--${getOffsetState(expectedStart - plannedStart)}`;
const plannedEnd = plannedStart + duration + delay;
const expectedEnd = countToEnd ? Math.max(expectedStart + duration, plannedEnd) : expectedStart + duration;
// count to end events are fixed to the scheduled end and ignore delays
const plannedEnd = (() => {
if (countToEnd) {
return timeStart + event.dayOffset * dayInMs + duration;
}
return plannedStart + duration;
})();
const expectedEndClass = `sub__schedule--${getOffsetState(expectedEnd - plannedEnd)}`;
return (
@@ -1,5 +1,4 @@
import { MaybeNumber, OntimeEvent } from 'ontime-types';
import { getExpectedStart } from 'ontime-utils';
import { IoPencil } from 'react-icons/io5';
import Button from '../../common/components/buttons/Button';
@@ -11,7 +10,7 @@ import { cx } from '../../common/utils/styleUtils';
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
import { getPropertyValue } from '../common/viewUtils';
import { useCountdownOptions } from './countdown.options';
import { CountdownTarget, useSubscriptionDisplayData } from './countdown.utils';
import { CountdownTarget, extendEventData, useSubscriptionDisplayData } from './countdown.utils';
import { ScheduleTime } from './CountdownSubscriptions';
import './SingleEventCountdown.scss';
@@ -27,19 +26,15 @@ export default function SingleEventCountdown({ subscribedEvent, goToEditMode }:
const { data: reportData } = useReport();
const { offset, currentDay, actualStart, plannedStart, mode } = useExpectedStartData();
const { totalGap, isLinkedToLoaded } = subscribedEvent;
const expectedStart = getExpectedStart(subscribedEvent, {
const countdownEvent = extendEventData(
subscribedEvent,
currentDay,
totalGap,
actualStart,
plannedStart,
isLinkedToLoaded,
offset,
mode,
});
const { endedAt } = reportData[subscribedEvent.reportId ?? subscribedEvent.id] ?? { endedAt: null };
const countdownEvent = { ...subscribedEvent, expectedStart, endedAt };
reportData,
);
const titleTmp = getPropertyValue(subscribedEvent, mainSource ?? 'title');
const title = titleTmp?.length ? titleTmp : ' '; // insert utf-8 empty space to avoid the line collapsing;
// while a group is live, surface the running event's title as the secondary line
@@ -12,7 +12,7 @@ import {
isOntimeGroup,
isPlayableEvent,
} from 'ontime-types';
import { MILLIS_PER_MINUTE, getExpectedStart, millisToString, removeLeadingZero } from 'ontime-utils';
import { MILLIS_PER_MINUTE, getExpectedEnd, getExpectedStart, millisToString, removeLeadingZero } from 'ontime-utils';
import { useCountdownSocket } from '../../common/hooks/useSocket';
import { ExtendedEntry } from '../../common/utils/rundownMetadata';
@@ -197,7 +197,11 @@ export type CountdownTarget = ExtendedEntry<OntimeEvent> & {
liveEntry?: ExtendedEntry<OntimeEvent> | null; // the running child while a group is live
};
export type CountdownEvent = CountdownTarget & { expectedStart: number; endedAt: MaybeNumber };
export type CountdownEvent = CountdownTarget & {
expectedStart: number;
expectedEnd: number;
endedAt: MaybeNumber;
};
/**
* Resolves a subscription (event or group) into an event-shaped countdown target.
@@ -262,7 +266,7 @@ export function extendEventData(
reportData: OntimeReport,
): CountdownEvent {
const { totalGap, isLinkedToLoaded } = event;
const expectedStart = getExpectedStart(event, {
const expectedStartState = {
currentDay,
totalGap,
actualStart,
@@ -270,7 +274,9 @@ export function extendEventData(
isLinkedToLoaded,
offset,
mode,
});
};
const expectedStart = getExpectedStart(event, expectedStartState);
const expectedEnd = getExpectedEnd(event, expectedStartState);
const { endedAt } = reportData[event.reportId ?? event.id] ?? { endedAt: null };
return { ...event, expectedStart, endedAt };
return { ...event, expectedStart, expectedEnd, endedAt };
}