feat: timer overview shows waiting timer

This commit is contained in:
Carlos Valente
2025-08-06 06:28:34 +02:00
committed by Carlos Valente
parent dddeefd544
commit f1a8db9649
9 changed files with 37 additions and 79 deletions
@@ -1,5 +1,3 @@
import { Playback } from 'ontime-types';
import { usePlaybackControl } from '../../../common/hooks/useSocket'; import { usePlaybackControl } from '../../../common/hooks/useSocket';
import AddTime from './add-time/AddTime'; import AddTime from './add-time/AddTime';
@@ -14,7 +12,7 @@ export default function PlaybackControl() {
return ( return (
<div className={style.mainContainer}> <div className={style.mainContainer}>
<PlaybackTimer playback={data.playback as Playback}> <PlaybackTimer>
<AddTime playback={data.playback} /> <AddTime playback={data.playback} />
</PlaybackTimer> </PlaybackTimer>
<PlaybackButtons <PlaybackButtons
@@ -11,10 +11,6 @@ import TimerDisplay from '../timer-display/TimerDisplay';
import style from './PlaybackTimer.module.scss'; import style from './PlaybackTimer.module.scss';
interface PlaybackTimerProps {
playback: Playback;
}
function resolveAddedTimeLabel(addedTime: number) { function resolveAddedTimeLabel(addedTime: number) {
if (addedTime > 0) { if (addedTime > 0) {
return `Added ${formatDuration(addedTime, false)}`; return `Added ${formatDuration(addedTime, false)}`;
@@ -27,11 +23,10 @@ function resolveAddedTimeLabel(addedTime: number) {
return ''; return '';
} }
export default function PlaybackTimer(props: PropsWithChildren<PlaybackTimerProps>) { export default function PlaybackTimer({ children }: PropsWithChildren) {
const { playback, children } = props;
const timer = useTimer(); const timer = useTimer();
const isRolling = 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);
@@ -52,7 +47,7 @@ export default function PlaybackTimer(props: PropsWithChildren<PlaybackTimerProp
{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={playback} /> <RunningStatus startedAt={timer.startedAt} expectedFinish={timer.expectedFinish} playback={timer.playback} />
)} )}
</div> </div>
{children} {children}
@@ -65,9 +60,7 @@ interface RunningStatusProps {
expectedFinish: MaybeNumber; expectedFinish: MaybeNumber;
playback: Playback; playback: Playback;
} }
function RunningStatus(props: RunningStatusProps) { function RunningStatus({ startedAt, expectedFinish, playback }: RunningStatusProps) {
const { startedAt, expectedFinish, playback } = props;
if (playback === Playback.Stop) { if (playback === Playback.Stop) {
return <StoppedStatus />; return <StoppedStatus />;
} }
@@ -3,15 +3,12 @@
top: 0; top: 0;
width: 100%; width: 100%;
z-index: $zindex-floating; z-index: $zindex-floating;
background-color: $gray-1350; background-color: $gray-1350;
border-bottom: 1px solid $white-10;
box-shadow: $large-top-drawer-shadow;
} }
.timers { .timers {
padding-block: 0.75rem 0.5rem; padding-block: 0.75rem 0.5rem;
padding-inline: 1rem; padding-inline: 1rem 0;
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr;
@@ -20,35 +17,14 @@
.runningTimer { .runningTimer {
grid-area: timers; grid-area: timers;
display: flex;
flex-direction: column;
justify-self: center; justify-self: center;
color: $muted-gray;
} }
.timeNow { .timeNow {
grid-area: clock; grid-area: clock;
display: flex;
flex-direction: column;
justify-self: right; justify-self: right;
} }
.label {
font-size: calc(1rem - 2px);
color: $gray-700;
line-height: 0.9em;
}
.timer {
font-size: 1.5rem;
letter-spacing: 0.5px;
line-height: 1.5;
}
.active {
color: $ui-white;
}
.progressOverride { .progressOverride {
grid-area: bar; grid-area: bar;
height: 1rem; height: 1rem;
@@ -1,33 +1,12 @@
import { isPlaybackActive } from 'ontime-utils'; import { ClockOverview, TimerOverview } from '../../overview/composite/TimeElements';
import { useClock, useTimer } from '../../../common/hooks/useSocket'; import style from './StatusBar.module.scss';
import { cx } from '../../../common/utils/styleUtils';
import ClockTime from '../../viewers/common/clock-time/ClockTime';
import RunningTime from '../../viewers/common/running-time/RunningTime';
import styles from './StatusBar.module.scss';
export default function StatusBarTimers() { export default function StatusBarTimers() {
const timer = useTimer();
const { clock } = useClock();
const playbackActive = isPlaybackActive(timer.playback);
return ( return (
<div className={styles.timers}> <div className={style.timers}>
<div className={styles.runningTimer}> <TimerOverview className={style.runningTimer} />
<span className={styles.label}>Running timer</span> <ClockOverview className={style.timeNow} />
<RunningTime
className={cx([styles.timer, playbackActive && styles.active])}
value={timer.current}
hideLeadingZero
/>
</div>
<div className={styles.timeNow}>
<span className={styles.label}>Time now</span>
<ClockTime className={styles.timer} value={clock} />
</div>
</div> </div>
); );
} }
@@ -1,6 +1,6 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { TbCalendar, TbCalendarClock, TbCalendarDown, TbCalendarStar, TbFlagDown, TbFlagStar } from 'react-icons/tb'; import { TbCalendar, TbCalendarClock, TbCalendarDown, TbCalendarStar, TbFlagDown, TbFlagStar } from 'react-icons/tb';
import { isOntimeBlock, OntimeBlock, OntimeEvent, TimerType } from 'ontime-types'; import { isOntimeBlock, OntimeBlock, OntimeEvent, TimerPhase, TimerType } from 'ontime-types';
import { isPlaybackActive, millisToString } from 'ontime-utils'; import { isPlaybackActive, millisToString } from 'ontime-utils';
import Tooltip from '../../../common/components/tooltip/Tooltip'; import Tooltip from '../../../common/components/tooltip/Tooltip';
@@ -194,7 +194,7 @@ export function ProgressOverview() {
const current = selectedEventIndex !== null ? selectedEventIndex + 1 : enDash; const current = selectedEventIndex !== null ? selectedEventIndex + 1 : enDash;
const progressText = numEvents ? `${current} of ${numEvents || enDash}` : enDash; const progressText = numEvents ? `${current} of ${numEvents || enDash}` : enDash;
return <TimeColumn label='Progress' value={progressText} muted={selectedEventIndex === null} />; return <TimeColumn label='Progress' value={progressText} state={selectedEventIndex === null ? 'muted' : 'active'} />;
} }
export function OffsetOverview() { export function OffsetOverview() {
@@ -208,16 +208,23 @@ export function OffsetOverview() {
return <OverUnder state={offsetState} value={offsetText} testId='offset' />; return <OverUnder state={offsetState} value={offsetText} testId='offset' />;
} }
export function ClockOverview() { export function ClockOverview({ className }: { className?: string }) {
const { clock } = useClock(); const { clock } = useClock();
return <TimeColumn label='Time now' value={formattedTime(clock)} />; return <TimeColumn label='Time now' value={formattedTime(clock)} className={className} />;
} }
export function TimerOverview() { export function TimerOverview({ className }: { className?: string }) {
const { current } = useTimer(); const timer = useTimer();
const display = millisToString(current, { fallback: timerPlaceholder }); const isWaiting = timer.phase === TimerPhase.Pending;
const title = isWaiting ? 'Count to start' : 'Running timer';
const display = millisToString(isWaiting ? timer.secondaryTimer : timer.current, { fallback: timerPlaceholder });
const timerState = (() => {
if (isWaiting) return 'waiting';
if (timer.current === null) return 'muted';
return 'active';
})();
return <TimeColumn label='Running timer' value={display} muted={current === null} />; return <TimeColumn label={title} value={display} state={timerState} className={className} />;
} }
@@ -29,6 +29,11 @@
gap: 0.25rem; gap: 0.25rem;
} }
&[data-state='waiting'] {
.label {
color: $ontime-roll;
}
}
&[data-state='over'] { &[data-state='over'] {
.label .over { .label .over {
color: $playback-over; color: $playback-over;
@@ -5,17 +5,17 @@ import style from './TimeLayout.module.scss';
interface TimeLayoutProps { interface TimeLayoutProps {
label: string; label: string;
value: string; value: string;
muted?: boolean; state?: 'muted' | 'waiting' | 'active';
daySpan?: number; daySpan?: number;
className?: string; className?: string;
testId?: string; testId?: string;
} }
export function TimeColumn({ label, value, muted, className, testId }: TimeLayoutProps) { export function TimeColumn({ label, value, state = 'active', className, testId }: TimeLayoutProps) {
return ( return (
<div className={style.column} data-state={muted ? 'muted' : 'active'}> <div className={cx([style.column, className])} data-state={state}>
<span className={style.label}>{label}</span> <span className={style.label}>{label}</span>
<span className={cx([style.clock, className])} data-testid={testId}> <span className={style.clock} data-testid={testId}>
{value} {value}
</span> </span>
</div> </div>
+1 -1
View File
@@ -28,7 +28,7 @@ $active-green: $green-700;
// playback colours // playback colours
$playback-start: $green-600; $playback-start: $green-600;
$ontime-roll: #0274B6; $ontime-roll: #22a0e9;
$ontime-delay: #F57C13; $ontime-delay: #F57C13;
$ontime-delay-text: #E69056; $ontime-delay-text: #E69056;
$ontime-paused: #c05621; $ontime-paused: #c05621;