refactor(time control): improve handling of overtime and count to end

This commit is contained in:
Carlos Valente
2026-07-14 14:30:59 +02:00
committed by Carlos Valente
parent 0c84a3ff9e
commit f598e8dab1
3 changed files with 38 additions and 13 deletions
+11
View File
@@ -139,6 +139,17 @@ export const setEventPlayback = {
pause: () => sendSocket('pause', undefined), pause: () => sendSocket('pause', undefined),
}; };
export const useTimerProgress = createSelector((state: RuntimeStore) => ({
playback: state.timer.playback,
phase: state.timer.phase,
addedTime: state.timer.addedTime,
secondaryTimer: state.timer.secondaryTimer,
current: state.timer.current,
expectedFinish: state.timer.expectedFinish,
startedAt: state.timer.startedAt,
isCountToEnd: state.eventNow?.countToEnd ?? false,
}));
export const useTimer = createSelector((state: RuntimeStore) => ({ export const useTimer = createSelector((state: RuntimeStore) => ({
...state.timer, ...state.timer,
})); }));
@@ -73,6 +73,10 @@
margin-right: 0.25rem; margin-right: 0.25rem;
} }
.tagOvertime {
color: $playback-over;
}
.time { .time {
color: $section-white; color: $section-white;
font-size: $text-body-size; font-size: $text-body-size;
@@ -1,12 +1,13 @@
import { MaybeNumber, Playback, TimerPhase } from 'ontime-types'; import { MaybeNumber, Playback, TimerPhase } from 'ontime-types';
import { dayInMs, millisToString } from 'ontime-utils'; import { millisToString } from 'ontime-utils';
import { PropsWithChildren } from 'react'; import { PropsWithChildren } from 'react';
import AppLink from '../../../../common/components/link/app-link/AppLink'; import AppLink from '../../../../common/components/link/app-link/AppLink';
import Tooltip from '../../../../common/components/tooltip/Tooltip'; import Tooltip from '../../../../common/components/tooltip/Tooltip';
import useReport from '../../../../common/hooks-query/useReport'; import useReport from '../../../../common/hooks-query/useReport';
import { useTimer } from '../../../../common/hooks/useSocket'; import { useTimerProgress } from '../../../../common/hooks/useSocket';
import { formatDuration } from '../../../../common/utils/time'; import { cx } from '../../../../common/utils/styleUtils';
import { formatDuration, normaliseWallClock } from '../../../../common/utils/time';
import TimerDisplay from '../timer-display/TimerDisplay'; import TimerDisplay from '../timer-display/TimerDisplay';
import style from './PlaybackTimer.module.scss'; import style from './PlaybackTimer.module.scss';
@@ -24,15 +25,14 @@ function resolveAddedTimeLabel(addedTime: number) {
} }
export default function PlaybackTimer({ children }: PropsWithChildren) { export default function PlaybackTimer({ children }: PropsWithChildren) {
const timer = useTimer(); 'use memo';
const timer = useTimerProgress();
const isRolling = timer.playback === Playback.Roll; const isRolling = timer.playback === Playback.Roll;
const isWaiting = timer.phase === TimerPhase.Pending; const isWaiting = timer.phase === TimerPhase.Pending;
const isOvertime = timer.phase === TimerPhase.Overtime; const isOvertime = timer.phase === TimerPhase.Overtime;
const hasAddedTime = Boolean(timer.addedTime); const hasAddedTime = Boolean(timer.addedTime);
const rollLabel = isRolling ? 'Roll mode active' : ''; const rollLabel = isRolling ? 'Roll mode active' : '';
const addedTimeLabel = resolveAddedTimeLabel(timer.addedTime); const addedTimeLabel = resolveAddedTimeLabel(timer.addedTime);
return ( return (
@@ -51,7 +51,13 @@ export default function PlaybackTimer({ children }: PropsWithChildren) {
{isWaiting ? ( {isWaiting ? (
<span className={style.rolltag}>Roll: Countdown to start</span> <span className={style.rolltag}>Roll: Countdown to start</span>
) : ( ) : (
<RunningStatus startedAt={timer.startedAt} expectedFinish={timer.expectedFinish} playback={timer.playback} /> <RunningStatus
startedAt={timer.startedAt}
expectedFinish={timer.expectedFinish}
isStopped={timer.playback === Playback.Stop}
isCountToEnd={timer.isCountToEnd}
isOvertime={isOvertime}
/>
)} )}
</div> </div>
{children} {children}
@@ -62,16 +68,18 @@ export default function PlaybackTimer({ children }: PropsWithChildren) {
interface RunningStatusProps { interface RunningStatusProps {
startedAt: MaybeNumber; startedAt: MaybeNumber;
expectedFinish: MaybeNumber; expectedFinish: MaybeNumber;
playback: Playback; isStopped: boolean;
isCountToEnd: boolean;
isOvertime: boolean;
} }
function RunningStatus({ startedAt, expectedFinish, playback }: RunningStatusProps) {
if (playback === Playback.Stop) { function RunningStatus({ startedAt, expectedFinish, isStopped, isCountToEnd, isOvertime }: RunningStatusProps) {
if (isStopped) {
return <StoppedStatus />; return <StoppedStatus />;
} }
const started = millisToString(startedAt); const started = millisToString(startedAt);
const finishedMs = expectedFinish !== null ? expectedFinish % dayInMs : null; const finish = millisToString(expectedFinish === null ? null : normaliseWallClock(expectedFinish));
const finish = millisToString(finishedMs);
return ( return (
<> <>
@@ -80,7 +88,9 @@ function RunningStatus({ startedAt, expectedFinish, playback }: RunningStatusPro
<span className={style.time}>{started}</span> <span className={style.time}>{started}</span>
</span> </span>
<span className={style.finish}> <span className={style.finish}>
<span className={style.tag}>Expect end</span> <span className={cx([style.tag, isOvertime && style.tagOvertime])}>
{isCountToEnd ? 'Scheduled end' : 'Expected end'}
</span>
<span className={style.time}>{finish}</span> <span className={style.time}>{finish}</span>
</span> </span>
</> </>