mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-03 06:28:01 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb493eb20f |
@@ -0,0 +1,26 @@
|
|||||||
|
@use '@/theme/viewerDefs' as *;
|
||||||
|
|
||||||
|
.schedule__ {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25em;
|
||||||
|
font-size: $timer-label-size;
|
||||||
|
color: var(--label-color-override, $timer-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule__delayed {
|
||||||
|
color: $ontime-delay-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule__strike {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: var(--label-color-override, $viewer-label-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule__over {
|
||||||
|
color: $playback-over;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schedule__under {
|
||||||
|
color: $playback-under;
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import { OntimeEvent } from 'ontime-types';
|
||||||
|
import { dayInMs, MILLIS_PER_MINUTE } from 'ontime-utils';
|
||||||
|
|
||||||
|
import ClockTime from '../../../views/common/clock-time/ClockTime';
|
||||||
|
import { getOffsetState } from '../../utils/offset';
|
||||||
|
import { ExtendedEntry } from '../../utils/rundownMetadata';
|
||||||
|
|
||||||
|
import './ScheduleTime.scss';
|
||||||
|
import { cx } from '../../utils/styleUtils';
|
||||||
|
|
||||||
|
type ScheduleTimeProps = {
|
||||||
|
event: ExtendedEntry<OntimeEvent>;
|
||||||
|
showExpected: boolean;
|
||||||
|
className?: string;
|
||||||
|
preferredFormat12?: string;
|
||||||
|
preferredFormat24?: string;
|
||||||
|
expectedStart: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
//TODO: consider relative mode
|
||||||
|
export default function ScheduleTime(props: ScheduleTimeProps) {
|
||||||
|
const {
|
||||||
|
event,
|
||||||
|
showExpected,
|
||||||
|
className,
|
||||||
|
preferredFormat12 = 'h:mm a',
|
||||||
|
preferredFormat24 = 'HH:mm',
|
||||||
|
expectedStart,
|
||||||
|
} = props;
|
||||||
|
const { timeStart, duration, delay, countToEnd } = event;
|
||||||
|
|
||||||
|
const plannedStart = timeStart + delay + event.dayOffset * dayInMs;
|
||||||
|
|
||||||
|
// only show new expected value if outside range of the planned value
|
||||||
|
const isExpectedValueShow = showExpected && isOutsideRange(plannedStart, expectedStart);
|
||||||
|
|
||||||
|
const plannedStateClass = isExpectedValueShow ? 'schedule__strike' : delay !== 0 ? 'schedule__delayed' : '';
|
||||||
|
|
||||||
|
const expectedOffsetState = getOffsetState(expectedStart - plannedStart);
|
||||||
|
const expectedStateClass = expectedOffsetState ? `schedule__${expectedOffsetState}` : '';
|
||||||
|
const plannedEnd = plannedStart + duration;
|
||||||
|
const expectedEnd = countToEnd ? Math.max(expectedStart + duration, plannedEnd) : expectedStart + duration;
|
||||||
|
const expectedEndOffsetState = getOffsetState(expectedEnd - plannedEnd);
|
||||||
|
const expectedEndClass = expectedEndOffsetState ? `schedule__${expectedEndOffsetState}` : '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cx(['schedule__', className])}>
|
||||||
|
<ClockTime
|
||||||
|
value={plannedStart}
|
||||||
|
preferredFormat12={preferredFormat12}
|
||||||
|
preferredFormat24={preferredFormat24}
|
||||||
|
className={plannedStateClass}
|
||||||
|
/>
|
||||||
|
{!isExpectedValueShow && (
|
||||||
|
<>
|
||||||
|
→
|
||||||
|
<ClockTime
|
||||||
|
value={plannedEnd}
|
||||||
|
preferredFormat12={preferredFormat12}
|
||||||
|
preferredFormat24={preferredFormat24}
|
||||||
|
className={plannedStateClass}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{isExpectedValueShow && (
|
||||||
|
<>
|
||||||
|
<ClockTime
|
||||||
|
value={expectedStart}
|
||||||
|
className={expectedStateClass}
|
||||||
|
preferredFormat12={preferredFormat12}
|
||||||
|
preferredFormat24={preferredFormat24}
|
||||||
|
/>
|
||||||
|
→
|
||||||
|
<ClockTime
|
||||||
|
value={expectedEnd}
|
||||||
|
className={expectedEndClass}
|
||||||
|
preferredFormat12={preferredFormat12}
|
||||||
|
preferredFormat24={preferredFormat24}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOutsideRange(a: number, b: number): boolean {
|
||||||
|
return Math.abs(a - b) > MILLIS_PER_MINUTE;
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
@use '@/theme/viewerDefs' as *;
|
@use '@/theme/viewerDefs' as *;
|
||||||
|
@use 'sass:color';
|
||||||
|
|
||||||
$item-height: 3.5rem;
|
$item-height: 3.5rem;
|
||||||
|
|
||||||
@@ -118,7 +119,8 @@ $item-height: 3.5rem;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sub--live {
|
.sub--live {
|
||||||
background-color: $active-green;
|
// we need to dim this if the schedule times are ahead of schedule they will be green and diaper in a full green background
|
||||||
|
background-color: color.adjust($active-green, $lightness: -10%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub--armed {
|
.sub--armed {
|
||||||
@@ -132,6 +134,10 @@ $item-height: 3.5rem;
|
|||||||
|
|
||||||
.sub__schedule {
|
.sub__schedule {
|
||||||
grid-area: schedule;
|
grid-area: schedule;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub__schedule-inline {
|
||||||
|
grid-area: schedule;
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -140,22 +146,6 @@ $item-height: 3.5rem;
|
|||||||
color: var(--label-color-override, $viewer-label-color);
|
color: var(--label-color-override, $viewer-label-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sub__schedule--delayed {
|
|
||||||
color: $ontime-delay-text;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub__schedule--strike {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub__schedule--over {
|
|
||||||
color: $playback-over;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub__schedule--under {
|
|
||||||
color: $playback-under;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub__title {
|
.sub__title {
|
||||||
grid-area: title;
|
grid-area: title;
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export default function CountdownSelect({ events, subscriptions, disableEdit }:
|
|||||||
className={cx(['sub', isSelected && 'sub--selected'])}
|
className={cx(['sub', isSelected && 'sub--selected'])}
|
||||||
>
|
>
|
||||||
<div className='sub__binder' style={{ '--user-color': event?.colour ?? '' }} />
|
<div className='sub__binder' style={{ '--user-color': event?.colour ?? '' }} />
|
||||||
<div className='sub__schedule'>
|
<div className='sub__schedule-inline'>
|
||||||
<ClockTime value={event.timeStart} preferredFormat12='h:mm a' preferredFormat24='HH:mm' />
|
<ClockTime value={event.timeStart} preferredFormat12='h:mm a' preferredFormat24='HH:mm' />
|
||||||
→
|
→
|
||||||
<ClockTime value={event.timeEnd} preferredFormat12='h:mm a' preferredFormat24='HH:mm' />
|
<ClockTime value={event.timeEnd} preferredFormat12='h:mm a' preferredFormat24='HH:mm' />
|
||||||
|
|||||||
@@ -1,33 +1,24 @@
|
|||||||
import { MaybeNumber, OntimeEvent } from 'ontime-types';
|
import { MaybeNumber, OntimeEvent } from 'ontime-types';
|
||||||
import { dayInMs } from 'ontime-utils';
|
import { getExpectedStart } from 'ontime-utils';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { IoPencil } from 'react-icons/io5';
|
import { IoPencil } from 'react-icons/io5';
|
||||||
|
|
||||||
import Button from '../../common/components/buttons/Button';
|
import Button from '../../common/components/buttons/Button';
|
||||||
|
import ScheduleTime from '../../common/components/schedule-time/ScheduleTime';
|
||||||
import useReport from '../../common/hooks-query/useReport';
|
import useReport from '../../common/hooks-query/useReport';
|
||||||
import { useFadeOutOnInactivity } from '../../common/hooks/useFadeOutOnInactivity';
|
import { useFadeOutOnInactivity } from '../../common/hooks/useFadeOutOnInactivity';
|
||||||
import useFollowComponent from '../../common/hooks/useFollowComponent';
|
import useFollowComponent from '../../common/hooks/useFollowComponent';
|
||||||
import { useExpectedStartData, usePlayback, useSelectedEventId } from '../../common/hooks/useSocket';
|
import { useExpectedStartData, usePlayback, useSelectedEventId } from '../../common/hooks/useSocket';
|
||||||
import { getOffsetState } from '../../common/utils/offset';
|
|
||||||
import { ExtendedEntry } from '../../common/utils/rundownMetadata';
|
import { ExtendedEntry } from '../../common/utils/rundownMetadata';
|
||||||
import { cx } from '../../common/utils/styleUtils';
|
import { cx } from '../../common/utils/styleUtils';
|
||||||
import { throttle } from '../../common/utils/throttle';
|
import { throttle } from '../../common/utils/throttle';
|
||||||
import FollowButton from '../../features/operator/follow-button/FollowButton';
|
import FollowButton from '../../features/operator/follow-button/FollowButton';
|
||||||
import ClockTime from '../common/clock-time/ClockTime';
|
|
||||||
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
|
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
|
||||||
import { getPropertyValue } from '../common/viewUtils';
|
import { getPropertyValue } from '../common/viewUtils';
|
||||||
import { useCountdownOptions } from './countdown.options';
|
import { useCountdownOptions } from './countdown.options';
|
||||||
import {
|
|
||||||
CountdownEvent,
|
|
||||||
extendEventData,
|
|
||||||
getIsLive,
|
|
||||||
isOutsideRange,
|
|
||||||
preferredFormat12,
|
|
||||||
preferredFormat24,
|
|
||||||
useSubscriptionDisplayData,
|
|
||||||
} from './countdown.utils';
|
|
||||||
|
|
||||||
import './Countdown.scss';
|
import './Countdown.scss';
|
||||||
|
import { getIsLive, useSubscriptionDisplayData } from './countdown.utils';
|
||||||
|
|
||||||
interface CountdownSubscriptionsProps {
|
interface CountdownSubscriptionsProps {
|
||||||
subscribedEvents: ExtendedEntry<OntimeEvent>[];
|
subscribedEvents: ExtendedEntry<OntimeEvent>[];
|
||||||
@@ -35,6 +26,7 @@ interface CountdownSubscriptionsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function CountdownSubscriptions({ subscribedEvents, goToEditMode }: CountdownSubscriptionsProps) {
|
export default function CountdownSubscriptions({ subscribedEvents, goToEditMode }: CountdownSubscriptionsProps) {
|
||||||
|
'use memo';
|
||||||
const { mainSource, secondarySource, showExpected } = useCountdownOptions();
|
const { mainSource, secondarySource, showExpected } = useCountdownOptions();
|
||||||
const playback = usePlayback();
|
const playback = usePlayback();
|
||||||
const selectedEventId = useSelectedEventId();
|
const selectedEventId = useSelectedEventId();
|
||||||
@@ -101,7 +93,17 @@ export default function CountdownSubscriptions({ subscribedEvents, goToEditMode
|
|||||||
const secondaryData = getPropertyValue(event, secondarySource);
|
const secondaryData = getPropertyValue(event, secondarySource);
|
||||||
const isLive = getIsLive(event.id, selectedEventId, playback);
|
const isLive = getIsLive(event.id, selectedEventId, playback);
|
||||||
const isArmed = !isLive && event.id === selectedEventId;
|
const isArmed = !isLive && event.id === selectedEventId;
|
||||||
const countdownEvent = extendEventData(event, currentDay, actualStart, plannedStart, offset, mode, reportData);
|
const { totalGap, isLinkedToLoaded } = event;
|
||||||
|
const expectedStart = getExpectedStart(event, {
|
||||||
|
currentDay,
|
||||||
|
totalGap,
|
||||||
|
actualStart,
|
||||||
|
plannedStart,
|
||||||
|
isLinkedToLoaded,
|
||||||
|
offset,
|
||||||
|
mode,
|
||||||
|
});
|
||||||
|
const { endedAt } = reportData[event.id] ?? { endedAt: null };
|
||||||
const displayTitle = getPropertyValue(event, mainSource ?? 'title');
|
const displayTitle = getPropertyValue(event, mainSource ?? 'title');
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -111,8 +113,13 @@ export default function CountdownSubscriptions({ subscribedEvents, goToEditMode
|
|||||||
data-testid={event.cue}
|
data-testid={event.cue}
|
||||||
>
|
>
|
||||||
<div className='sub__binder' style={{ '--user-color': event.colour }} />
|
<div className='sub__binder' style={{ '--user-color': event.colour }} />
|
||||||
<ScheduleTime event={countdownEvent} showExpected={showExpected} />
|
<ScheduleTime
|
||||||
<SubscriptionStatus event={countdownEvent} />
|
event={event}
|
||||||
|
expectedStart={expectedStart}
|
||||||
|
showExpected={showExpected}
|
||||||
|
className='sub__schedule'
|
||||||
|
/>
|
||||||
|
<SubscriptionStatus event={event} expectedStart={expectedStart} endedAt={endedAt} />
|
||||||
<div className={cx(['sub__title', !displayTitle && 'subdued'])}>{displayTitle}</div>
|
<div className={cx(['sub__title', !displayTitle && 'subdued'])}>{displayTitle}</div>
|
||||||
{secondaryData && <div className='sub__secondary'>{secondaryData}</div>}
|
{secondaryData && <div className='sub__secondary'>{secondaryData}</div>}
|
||||||
</div>
|
</div>
|
||||||
@@ -128,73 +135,15 @@ export default function CountdownSubscriptions({ subscribedEvents, goToEditMode
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type ScheduleTimeProps = {
|
|
||||||
event: CountdownEvent;
|
|
||||||
showExpected: boolean;
|
|
||||||
};
|
|
||||||
//TODO: consider relative mode
|
|
||||||
export function ScheduleTime(props: ScheduleTimeProps) {
|
|
||||||
const { event, showExpected } = props;
|
|
||||||
const { timeStart, duration, delay, expectedStart, countToEnd } = event;
|
|
||||||
|
|
||||||
const plannedStart = timeStart + delay + event.dayOffset * dayInMs;
|
|
||||||
|
|
||||||
// only show new exacted value if outside range of the planned value
|
|
||||||
const isExpectedValueShow = showExpected && isOutsideRange(plannedStart, expectedStart);
|
|
||||||
|
|
||||||
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;
|
|
||||||
const expectedEndClass = `sub__schedule--${getOffsetState(expectedEnd - plannedEnd)}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='sub__schedule'>
|
|
||||||
<ClockTime
|
|
||||||
value={plannedStart}
|
|
||||||
preferredFormat12={preferredFormat12}
|
|
||||||
preferredFormat24={preferredFormat24}
|
|
||||||
className={plannedStateClass}
|
|
||||||
/>
|
|
||||||
{!isExpectedValueShow && (
|
|
||||||
<>
|
|
||||||
→
|
|
||||||
<ClockTime
|
|
||||||
value={plannedEnd}
|
|
||||||
preferredFormat12={preferredFormat12}
|
|
||||||
preferredFormat24={preferredFormat24}
|
|
||||||
className={plannedStateClass}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{isExpectedValueShow && (
|
|
||||||
<>
|
|
||||||
<ClockTime
|
|
||||||
value={expectedStart}
|
|
||||||
className={expectedStateClass}
|
|
||||||
preferredFormat12={preferredFormat12}
|
|
||||||
preferredFormat24={preferredFormat24}
|
|
||||||
/>
|
|
||||||
→
|
|
||||||
<ClockTime
|
|
||||||
value={expectedEnd}
|
|
||||||
className={expectedEndClass}
|
|
||||||
preferredFormat12={preferredFormat12}
|
|
||||||
preferredFormat24={preferredFormat24}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SubscriptionStatusProps {
|
interface SubscriptionStatusProps {
|
||||||
event: ExtendedEntry<OntimeEvent> & { endedAt: MaybeNumber; expectedStart: number };
|
event: ExtendedEntry<OntimeEvent>;
|
||||||
|
endedAt: MaybeNumber;
|
||||||
|
expectedStart: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubscriptionStatus({ event }: SubscriptionStatusProps) {
|
function SubscriptionStatus({ event, endedAt, expectedStart }: SubscriptionStatusProps) {
|
||||||
const { status, statusDisplay, timeDisplay } = useSubscriptionDisplayData(event);
|
'use memo';
|
||||||
|
const { status, statusDisplay, timeDisplay } = useSubscriptionDisplayData(event, endedAt, expectedStart);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -7,27 +7,6 @@
|
|||||||
gap: 5rem;
|
gap: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.event__title {
|
|
||||||
background-color: var(--card-background-color-override, $viewer-card-bg-color);
|
|
||||||
padding: $view-card-padding;
|
|
||||||
border-radius: $element-border-radius;
|
|
||||||
font-size: clamp(40px, 4.5vw, 80px);
|
|
||||||
line-height: 1.1;
|
|
||||||
text-align: center;
|
|
||||||
border-left: 0.25em solid;
|
|
||||||
border-color: var(--card-background-color-override, $viewer-card-bg-color);
|
|
||||||
|
|
||||||
.secondary {
|
|
||||||
text-align: left;
|
|
||||||
font-size: $title-font-size;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sub__schedule {
|
|
||||||
color: inherit;
|
|
||||||
font-size: $base-font-size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.event__status {
|
.event__status {
|
||||||
color: var(--secondary-color-override, $viewer-secondary-color);
|
color: var(--secondary-color-override, $viewer-secondary-color);
|
||||||
font-size: $header-font-size;
|
font-size: $header-font-size;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { getExpectedStart } from 'ontime-utils';
|
|||||||
import { IoPencil } from 'react-icons/io5';
|
import { IoPencil } from 'react-icons/io5';
|
||||||
|
|
||||||
import Button from '../../common/components/buttons/Button';
|
import Button from '../../common/components/buttons/Button';
|
||||||
|
import TitleCard from '../../common/components/title-card/TitleCard';
|
||||||
import useReport from '../../common/hooks-query/useReport';
|
import useReport from '../../common/hooks-query/useReport';
|
||||||
import { useFadeOutOnInactivity } from '../../common/hooks/useFadeOutOnInactivity';
|
import { useFadeOutOnInactivity } from '../../common/hooks/useFadeOutOnInactivity';
|
||||||
import { useExpectedStartData } from '../../common/hooks/useSocket';
|
import { useExpectedStartData } from '../../common/hooks/useSocket';
|
||||||
@@ -11,10 +12,9 @@ import { cx } from '../../common/utils/styleUtils';
|
|||||||
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
|
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
|
||||||
import { getPropertyValue } from '../common/viewUtils';
|
import { getPropertyValue } from '../common/viewUtils';
|
||||||
import { useCountdownOptions } from './countdown.options';
|
import { useCountdownOptions } from './countdown.options';
|
||||||
import { useSubscriptionDisplayData } from './countdown.utils';
|
|
||||||
import { ScheduleTime } from './CountdownSubscriptions';
|
|
||||||
|
|
||||||
import './SingleEventCountdown.scss';
|
import './SingleEventCountdown.scss';
|
||||||
|
import { useSubscriptionDisplayData } from './countdown.utils';
|
||||||
|
|
||||||
interface SingleEventCountdownProps {
|
interface SingleEventCountdownProps {
|
||||||
subscribedEvent: ExtendedEntry<OntimeEvent>;
|
subscribedEvent: ExtendedEntry<OntimeEvent>;
|
||||||
@@ -39,19 +39,22 @@ export default function SingleEventCountdown({ subscribedEvent, goToEditMode }:
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { endedAt } = reportData[subscribedEvent.id] ?? { endedAt: null };
|
const { endedAt } = reportData[subscribedEvent.id] ?? { endedAt: null };
|
||||||
const countdownEvent = { ...subscribedEvent, expectedStart, endedAt };
|
const title = getPropertyValue(subscribedEvent, mainSource ?? 'title');
|
||||||
const titleTmp = getPropertyValue(subscribedEvent, mainSource ?? 'title');
|
|
||||||
const title = titleTmp?.length ? titleTmp : ' '; // insert utf-8 empty space to avoid the line collapsing;
|
|
||||||
const secondaryData = getPropertyValue(subscribedEvent, secondarySource);
|
const secondaryData = getPropertyValue(subscribedEvent, secondarySource);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='single-container' data-testid='countdown-event'>
|
<div className='single-container' data-testid='countdown-event'>
|
||||||
<SubscriptionStatus event={countdownEvent} />
|
<SubscriptionStatus event={subscribedEvent} expectedStart={expectedStart} endedAt={endedAt} />
|
||||||
<div className='event__title' style={{ borderColor: countdownEvent.colour }}>
|
<TitleCard
|
||||||
<ScheduleTime event={countdownEvent} showExpected={showExpected} />
|
title={title}
|
||||||
{title}
|
secondary={secondaryData}
|
||||||
{secondaryData && <div className='secondary'>{secondaryData}</div>}
|
colour={subscribedEvent.colour}
|
||||||
</div>
|
textAlign='center'
|
||||||
|
size='lg'
|
||||||
|
event={subscribedEvent}
|
||||||
|
showExpected={showExpected}
|
||||||
|
expectedStart={expectedStart}
|
||||||
|
/>
|
||||||
<div className={cx(['fab-container', !showFab && 'fab-container--hidden'])}>
|
<div className={cx(['fab-container', !showFab && 'fab-container--hidden'])}>
|
||||||
<Button variant='primary' size='xlarge' onClick={goToEditMode}>
|
<Button variant='primary' size='xlarge' onClick={goToEditMode}>
|
||||||
<IoPencil /> Edit
|
<IoPencil /> Edit
|
||||||
@@ -62,11 +65,13 @@ export default function SingleEventCountdown({ subscribedEvent, goToEditMode }:
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SubscriptionStatusProps {
|
interface SubscriptionStatusProps {
|
||||||
event: ExtendedEntry<OntimeEvent> & { endedAt: MaybeNumber; expectedStart: number };
|
event: ExtendedEntry<OntimeEvent>;
|
||||||
|
endedAt: MaybeNumber;
|
||||||
|
expectedStart: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubscriptionStatus({ event }: SubscriptionStatusProps) {
|
function SubscriptionStatus({ event, endedAt, expectedStart }: SubscriptionStatusProps) {
|
||||||
const { status, statusDisplay, timeDisplay } = useSubscriptionDisplayData(event);
|
const { status, statusDisplay, timeDisplay } = useSubscriptionDisplayData(event, endedAt, expectedStart);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { EntryId, MaybeNumber, OffsetMode, OntimeEntry, OntimeEvent, OntimeReport, Playback } from 'ontime-types';
|
import { EntryId, MaybeNumber, OntimeEntry, OntimeEvent, Playback } from 'ontime-types';
|
||||||
import { MILLIS_PER_MINUTE, getExpectedStart, millisToString, removeLeadingZero } from 'ontime-utils';
|
import { MILLIS_PER_MINUTE, millisToString, removeLeadingZero } from 'ontime-utils';
|
||||||
|
|
||||||
import { useCountdownSocket } from '../../common/hooks/useSocket';
|
import { useCountdownSocket } from '../../common/hooks/useSocket';
|
||||||
import { ExtendedEntry } from '../../common/utils/rundownMetadata';
|
import { ExtendedEntry } from '../../common/utils/rundownMetadata';
|
||||||
@@ -13,9 +13,6 @@ export function sanitiseTitle(title: string | null) {
|
|||||||
return title ?? '{no title}';
|
return title ?? '{no title}';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const preferredFormat12 = 'h:mm a';
|
|
||||||
export const preferredFormat24 = 'HH:mm';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the current event is live
|
* Whether the current event is live
|
||||||
*/
|
*/
|
||||||
@@ -40,7 +37,9 @@ export const timerProgress: TimerMessage = {
|
|||||||
* Handles events in different days but disregards whether an event has actually played
|
* Handles events in different days but disregards whether an event has actually played
|
||||||
*/
|
*/
|
||||||
export function useSubscriptionDisplayData(
|
export function useSubscriptionDisplayData(
|
||||||
subscribedEvent: ExtendedEntry<OntimeEvent> & { endedAt: MaybeNumber; expectedStart: number },
|
subscribedEvent: ExtendedEntry<OntimeEvent>,
|
||||||
|
endedAt: MaybeNumber,
|
||||||
|
expectedStart: number,
|
||||||
): { status: ProgressStatus; statusDisplay: string; timeDisplay: string } {
|
): { status: ProgressStatus; statusDisplay: string; timeDisplay: string } {
|
||||||
const { playback, current, clock } = useCountdownSocket();
|
const { playback, current, clock } = useCountdownSocket();
|
||||||
const { getLocalizedString } = useTranslation();
|
const { getLocalizedString } = useTranslation();
|
||||||
@@ -84,11 +83,11 @@ export function useSubscriptionDisplayData(
|
|||||||
return {
|
return {
|
||||||
status: 'done',
|
status: 'done',
|
||||||
statusDisplay: getLocalizedString(timerProgress['done']),
|
statusDisplay: getLocalizedString(timerProgress['done']),
|
||||||
timeDisplay: formatTime(subscribedEvent.endedAt, { format12: preferredFormat12, format24: preferredFormat24 }),
|
timeDisplay: formatTime(endedAt, { format12: 'h:mm a', format24: 'HH:mm' }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subscribedEvent.expectedStart - clock <= 0) {
|
if (expectedStart - clock <= 0) {
|
||||||
return {
|
return {
|
||||||
status: 'due',
|
status: 'due',
|
||||||
statusDisplay: getLocalizedString(timerProgress['future']), // We use future here on purpose for the look of it
|
statusDisplay: getLocalizedString(timerProgress['future']), // We use future here on purpose for the look of it
|
||||||
@@ -99,7 +98,7 @@ export function useSubscriptionDisplayData(
|
|||||||
return {
|
return {
|
||||||
status: 'future',
|
status: 'future',
|
||||||
statusDisplay: getLocalizedString(timerProgress['future']),
|
statusDisplay: getLocalizedString(timerProgress['future']),
|
||||||
timeDisplay: bigDuration(subscribedEvent.expectedStart - clock),
|
timeDisplay: bigDuration(expectedStart - clock),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,32 +162,3 @@ export function isLinkedToLoadedEvent(events: OntimeEvent[], loadedId: EntryId |
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isOutsideRange(a: number, b: number): boolean {
|
|
||||||
return Math.abs(a - b) > MILLIS_PER_MINUTE;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CountdownEvent = ExtendedEntry<OntimeEvent> & { expectedStart: number; endedAt: MaybeNumber };
|
|
||||||
|
|
||||||
export function extendEventData(
|
|
||||||
event: ExtendedEntry<OntimeEvent>,
|
|
||||||
currentDay: number,
|
|
||||||
actualStart: MaybeNumber,
|
|
||||||
plannedStart: MaybeNumber,
|
|
||||||
offset: number,
|
|
||||||
mode: OffsetMode,
|
|
||||||
reportData: OntimeReport,
|
|
||||||
): CountdownEvent {
|
|
||||||
const { totalGap, isLinkedToLoaded } = event;
|
|
||||||
const expectedStart = getExpectedStart(event, {
|
|
||||||
currentDay,
|
|
||||||
totalGap,
|
|
||||||
actualStart,
|
|
||||||
plannedStart,
|
|
||||||
isLinkedToLoaded,
|
|
||||||
offset,
|
|
||||||
mode,
|
|
||||||
});
|
|
||||||
const { endedAt } = reportData[event.id] ?? { endedAt: null };
|
|
||||||
return { ...event, expectedStart, endedAt };
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user