import { EntryId, OntimeEntry, OntimeEvent, OntimeGroup, OntimeView, PlayableEvent, isOntimeEvent, isOntimeGroup, isPlayableEvent, } from 'ontime-types'; import { useMemo, useState } from 'react'; import { IoAdd } from 'react-icons/io5'; import Button from '../../common/components/buttons/Button'; import Empty from '../../common/components/state/Empty'; import EmptyPage from '../../common/components/state/EmptyPage'; import ViewLogo from '../../common/components/view-logo/ViewLogo'; import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor'; import { useAutoTickingClock } from '../../common/hooks/useAutoTickingClock'; import { useWindowTitle } from '../../common/hooks/useWindowTitle'; import { ExtendedEntry } from '../../common/utils/rundownMetadata'; import { formatTime, getDefaultFormat } from '../../common/utils/time'; import { useTranslation } from '../../translation/TranslationProvider'; import Loader from '../common/loader/Loader'; import SuperscriptTime from '../common/superscript-time/SuperscriptTime'; import { getCountdownOptions, useCountdownOptions } from './countdown.options'; import { getOrderedSubscriptions, resolveSubscriptionTarget } from './countdown.utils'; import CountdownSelect from './CountdownSelect'; import CountdownSubscriptions from './CountdownSubscriptions'; import SingleEventCountdown from './SingleEventCountdown'; import { CountdownData, useCountdownData } from './useCountdownData'; import './Countdown.scss'; export default function CountdownLoader() { const { data, status } = useCountdownData(); useWindowTitle('Countdown'); if (status === 'pending') { return ; } if (status === 'error') { return ; } return ; } function Countdown({ customFields, rundownData, projectData, isMirrored, settings }: CountdownData) { const { getLocalizedString } = useTranslation(); const { subscriptions } = useCountdownOptions(); const [editMode, setEditMode] = useState(false); // gather subscribable items: playable events and groups that contain at least one playable child const candidates = rundownData.filter((entry): entry is ExtendedEntry => { if (isOntimeEvent(entry)) { return isPlayableEvent(entry); } if (isOntimeGroup(entry)) { return rundownData.some((item) => isOntimeEvent(item) && isPlayableEvent(item) && item.parent === entry.id); } return false; }); // gather presentation data const hasEvents = candidates.length > 0; // gather option data const defaultFormat = getDefaultFormat(settings?.timeFormat); const countdownOptions = useMemo( () => getCountdownOptions(defaultFormat, customFields, subscriptions), [defaultFormat, customFields, subscriptions], ); return (
{projectData?.logo && }
{projectData.title}
{!hasEvents && (
)} {hasEvents && editMode && ( setEditMode(false)} /> )} {hasEvents && !editMode && ( setEditMode(true)} /> )}
); } interface CountdownContentsProps { candidates: ExtendedEntry[]; rundownData: ExtendedEntry[]; subscriptions: EntryId[]; goToEditMode: () => void; } function CountdownContents({ candidates, rundownData, subscriptions, goToEditMode }: CountdownContentsProps) { const { getLocalizedString } = useTranslation(); const { hidePast } = useCountdownOptions(); if (subscriptions.length === 0) { return (
); } const subscribedEvents = getOrderedSubscriptions(subscriptions, candidates) .map((entry) => resolveSubscriptionTarget(entry, rundownData)) .filter((target): target is NonNullable => target !== null); const eventsToShow = !hidePast ? subscribedEvents : subscribedEvents.filter((event) => !event.isPast); if (subscribedEvents.length === 0) { return (
); } if (subscribedEvents.length === 1 && eventsToShow.length === 1) { const event = subscribedEvents.at(0); if (!event) return null; return ; } if (eventsToShow.length === 0) { return (
); } return ; } function CountdownClock() { const { timeformat } = useCountdownOptions(); const { getLocalizedString } = useTranslation(); const clock = useAutoTickingClock(); // gather timer data const formattedClock = formatTime(clock, { override: timeformat }); return (
{getLocalizedString('common.time_now')}
); }