import { useMemo } from 'react'; import { TbCalendarClock, TbCalendarPin, TbCalendarStar, TbFlagPin, TbFlagStar, TbFolderPin, TbFolderStar, } from 'react-icons/tb'; import { OffsetMode, OntimeEvent, OntimeGroup, TimerPhase, TimerType } from 'ontime-types'; import { dayInMs, isPlaybackActive, millisToString } from 'ontime-utils'; import Tooltip from '../../../common/components/tooltip/Tooltip'; import { useClock, useCurrentGroupId, useFlagTimerOverView, useGroupTimerOverView, useNextFlag, useOffsetOverview, useProgressOverview, useRundownOverview, useTimer, } from '../../../common/hooks/useSocket'; import { useEntry } from '../../../common/hooks-query/useRundown'; import { getOffsetState, getOffsetText } from '../../../common/utils/offset'; import { cx, enDash, timerPlaceholder } from '../../../common/utils/styleUtils'; import { formatTime } from '../../../common/utils/time'; import { calculateEndAndDaySpan, formatDueTime, formattedTime } from '../overview.utils'; import { OverUnder, TimeColumn } from './TimeLayout'; import style from './TimeElements.module.scss'; export function StartTimes() { const { plannedEnd, plannedStart, actualStart, expectedEnd } = useRundownOverview(); const plannedStartText = plannedStart === null ? timerPlaceholder : formatTime(plannedStart); const [maybePlannedEnd, maybePlannedDaySpan] = useMemo(() => calculateEndAndDaySpan(plannedEnd), [plannedEnd]); const [maybeExpectedEnd, maybeExpectedDaySpan] = useMemo(() => calculateEndAndDaySpan(expectedEnd), [expectedEnd]); const plannedEndText = maybePlannedEnd === null ? timerPlaceholder : formatTime(maybePlannedEnd); return (
Start
} /> {plannedStartText}
} /> {formattedTime(actualStart)}
End
} /> {maybePlannedDaySpan > 0 ? ( } > {plannedEndText} ) : ( {plannedEndText} )}
} /> {maybeExpectedEnd !== null && maybeExpectedDaySpan > 0 ? ( } > {formattedTime(maybeExpectedEnd)} ) : ( {formattedTime(maybeExpectedEnd)} )}
); } export function MetadataTimes() { return (
); } function GroupTimes() { const { clock, groupExpectedEnd, actualGroupStart, mode, playback, currentDay } = useGroupTimerOverView(); const { currentGroupId } = useCurrentGroupId(); const group = useEntry(currentGroupId) as OntimeGroup | null; const active = isPlaybackActive(playback); // the group end time dose not encode any day offsets so it is calculated with group start time and duration const plannedGroupEnd = (() => { if (!active) return null; if (!group || group.timeStart === null) return null; const normalizedClock = clock + currentDay * dayInMs; return mode === OffsetMode.Absolute ? group.timeStart + group.duration - normalizedClock : actualGroupStart + group.duration - normalizedClock; })(); const plannedTimeUntilGroupEnd = formatDueTime(plannedGroupEnd, 3, TimerType.CountDown); const expectedGroupEnd = groupExpectedEnd !== null ? groupExpectedEnd - clock : null; const expectedTimeUntilGroupEnd = formatDueTime(expectedGroupEnd, 3, TimerType.CountDown); const groupTitle = group?.title ?? null; return (
{`${groupTitle || 'Group'} `}
} /> {plannedTimeUntilGroupEnd}
} /> {expectedTimeUntilGroupEnd}
); } function FlagTimes() { const { clock, mode, actualStart, plannedStart, playback, currentDay } = useFlagTimerOverView(); const { id, expectedStart } = useNextFlag(); const entry = useEntry(id) as OntimeEvent | null; const active = isPlaybackActive(playback); const plannedFlagStart = (() => { if (!active) return null; if (!entry) return null; const normalizedTimeStart = entry.timeStart + entry.dayOffset * dayInMs; const normalizedClock = clock + currentDay * dayInMs; return mode === OffsetMode.Absolute ? normalizedTimeStart - normalizedClock : normalizedTimeStart + actualStart - plannedStart - normalizedClock; })(); const plannedTimeUntilDisplay = formatDueTime(plannedFlagStart, 3, TimerType.CountDown); const expectedTimeUntil = expectedStart !== null ? expectedStart - clock : null; const expectedTimeUntilDisplay = formatDueTime(expectedTimeUntil, 3, TimerType.CountDown); const title = entry?.title ?? null; return (
{`${title || 'Flag'} `}
} /> {plannedTimeUntilDisplay}
} /> {expectedTimeUntilDisplay}
); } export function ProgressOverview() { const { numEvents, selectedEventIndex } = useProgressOverview(); const current = selectedEventIndex !== null ? selectedEventIndex + 1 : enDash; const progressText = numEvents ? `${current} of ${numEvents || enDash}` : enDash; return ; } export function OffsetOverview() { const { offset, playback } = useOffsetOverview(); const isPlaying = isPlaybackActive(playback); const offsetState = getOffsetState(isPlaying ? offset : null); const offsetText = getOffsetText(isPlaying ? offset : null); return ; } export function ClockOverview({ className }: { className?: string }) { const { clock } = useClock(); const formattedClock = formatTime(clock); return ; } export function TimerOverview({ className }: { className?: string }) { const timer = useTimer(); 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 ; }