mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-07 15:29:10 +00:00
Extract client timeuntil logic (#1521)
* extract * add to timeline * translate Due * fix timeline page * begin adding some tests * spelling Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com> * move file * refactor * create hook * refactor * update test * spelling Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com> * remove translation * explain isLinkedToLoaded * add delay to calculation * add delay value to test * spelling --------- Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
b60b1beeec
commit
99dd4b4d5c
@@ -170,6 +170,12 @@ export const useTimelineStatus = createSelector((state: RuntimeStore) => ({
|
|||||||
offset: state.runtime.offset,
|
offset: state.runtime.offset,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const useTimeUntilData = createSelector((state: RuntimeStore) => ({
|
||||||
|
clock: state.clock,
|
||||||
|
offset: state.runtime.offset,
|
||||||
|
currentDay: state.eventNow?.dayOffset ?? 0, //The day of the currently running event
|
||||||
|
}));
|
||||||
|
|
||||||
export const useRuntimeOffset = createSelector((state: RuntimeStore) => ({
|
export const useRuntimeOffset = createSelector((state: RuntimeStore) => ({
|
||||||
offset: state.runtime.offset,
|
offset: state.runtime.offset,
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { formatTime, nowInMillis } from '../time';
|
import { calculateTimeUntilStart, formatTime, nowInMillis } from '../time';
|
||||||
|
|
||||||
describe('nowInMillis()', () => {
|
describe('nowInMillis()', () => {
|
||||||
it('should return the current time in milliseconds', () => {
|
it('should return the current time in milliseconds', () => {
|
||||||
@@ -38,3 +38,84 @@ describe('formatTime()', () => {
|
|||||||
expect(time).toStrictEqual('-01:00');
|
expect(time).toStrictEqual('-01:00');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('calculateTimeUntilStart()', () => {
|
||||||
|
test('ontime', () => {
|
||||||
|
const test = {
|
||||||
|
timeStart: 100,
|
||||||
|
dayOffset: 0,
|
||||||
|
delay: 0,
|
||||||
|
currentDay: 0,
|
||||||
|
totalGap: 0,
|
||||||
|
clock: 90,
|
||||||
|
offset: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(calculateTimeUntilStart({ ...test, isLinkedToLoaded: false })).toBe(10);
|
||||||
|
expect(calculateTimeUntilStart({ ...test, isLinkedToLoaded: true })).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('running behind', () => {
|
||||||
|
const test = {
|
||||||
|
timeStart: 100,
|
||||||
|
dayOffset: 0,
|
||||||
|
delay: 0,
|
||||||
|
currentDay: 0,
|
||||||
|
totalGap: 0,
|
||||||
|
clock: 90,
|
||||||
|
offset: -20,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(calculateTimeUntilStart({ ...test, isLinkedToLoaded: false })).toBe(30);
|
||||||
|
expect(calculateTimeUntilStart({ ...test, isLinkedToLoaded: true })).toBe(30);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('running ahead', () => {
|
||||||
|
const test = {
|
||||||
|
timeStart: 100,
|
||||||
|
dayOffset: 0,
|
||||||
|
delay: 0,
|
||||||
|
currentDay: 0,
|
||||||
|
totalGap: 0,
|
||||||
|
clock: 80,
|
||||||
|
offset: 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(calculateTimeUntilStart({ ...test, isLinkedToLoaded: false })).toBe(20); // <-- when running ahead the unlinked timer stays put
|
||||||
|
expect(calculateTimeUntilStart({ ...test, isLinkedToLoaded: true })).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('running behind with enough gaps', () => {
|
||||||
|
const test = {
|
||||||
|
timeStart: 100,
|
||||||
|
dayOffset: 0,
|
||||||
|
delay: 0,
|
||||||
|
currentDay: 0,
|
||||||
|
totalGap: 20,
|
||||||
|
clock: 50,
|
||||||
|
offset: -20,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(calculateTimeUntilStart({ ...test, isLinkedToLoaded: false })).toBe(50);
|
||||||
|
expect(calculateTimeUntilStart({ ...test, isLinkedToLoaded: true })).toBe(70); // This should not be possible
|
||||||
|
});
|
||||||
|
|
||||||
|
test('running behind with to little gaps', () => {
|
||||||
|
const test = {
|
||||||
|
timeStart: 100,
|
||||||
|
dayOffset: 0,
|
||||||
|
delay: 0,
|
||||||
|
currentDay: 0,
|
||||||
|
totalGap: 10,
|
||||||
|
clock: 50,
|
||||||
|
offset: -20,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(calculateTimeUntilStart({ ...test, isLinkedToLoaded: false })).toBe(60);
|
||||||
|
expect(calculateTimeUntilStart({ ...test, isLinkedToLoaded: true })).toBe(70); // This should not be possible
|
||||||
|
});
|
||||||
|
|
||||||
|
//TODO: more indepth testing,
|
||||||
|
// including day offset handling
|
||||||
|
// and more?
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { MaybeNumber, Settings, TimeFormat } from 'ontime-types';
|
import { MaybeNumber, OntimeEvent, Settings, TimeFormat } from 'ontime-types';
|
||||||
import { formatFromMillis, MILLIS_PER_HOUR, MILLIS_PER_MINUTE, MILLIS_PER_SECOND } from 'ontime-utils';
|
import { dayInMs, formatFromMillis, MILLIS_PER_HOUR, MILLIS_PER_MINUTE, MILLIS_PER_SECOND } from 'ontime-utils';
|
||||||
|
|
||||||
import { FORMAT_12, FORMAT_24 } from '../../viewerConfig';
|
import { FORMAT_12, FORMAT_24 } from '../../viewerConfig';
|
||||||
import { APP_SETTINGS } from '../api/constants';
|
import { APP_SETTINGS } from '../api/constants';
|
||||||
|
import { useTimeUntilData } from '../hooks/useSocket';
|
||||||
import { ontimeQueryClient } from '../queryClient';
|
import { ontimeQueryClient } from '../queryClient';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -125,3 +126,71 @@ export function formatDuration(duration: number, hideSeconds = true): string {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param totalGap accumulated gap from the current event
|
||||||
|
* @param isLinkedToLoaded is this event part of a chain linking back to the current loaded event
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function useTimeUntilStart(
|
||||||
|
// typed like this to make it very clear what the data is
|
||||||
|
data: Pick<OntimeEvent, 'timeStart' | 'dayOffset' | 'delay'> & {
|
||||||
|
totalGap: number;
|
||||||
|
isLinkedToLoaded: boolean;
|
||||||
|
},
|
||||||
|
): number {
|
||||||
|
const { offset, clock, currentDay } = useTimeUntilData();
|
||||||
|
return calculateTimeUntilStart({ ...data, currentDay, clock, offset });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param currentDay the day offset of the urrently running event
|
||||||
|
* @param totalGap accumulated gap from the current event
|
||||||
|
* @param isLinkedToLoaded is this event part of a chain linking back to the current loaded event
|
||||||
|
* @param clock
|
||||||
|
* @param offset
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function calculateTimeUntilStart(
|
||||||
|
data: Pick<OntimeEvent, 'timeStart' | 'dayOffset' | 'delay'> & {
|
||||||
|
currentDay: number;
|
||||||
|
totalGap: number;
|
||||||
|
isLinkedToLoaded: boolean;
|
||||||
|
clock: number;
|
||||||
|
offset: number;
|
||||||
|
},
|
||||||
|
): number {
|
||||||
|
const { timeStart, dayOffset, currentDay, totalGap, isLinkedToLoaded, clock, offset, delay } = data;
|
||||||
|
|
||||||
|
//How many days from the currently running event to this one
|
||||||
|
const relativeDayOffset = dayOffset - currentDay;
|
||||||
|
|
||||||
|
const delayedStart = Math.max(0, timeStart + delay);
|
||||||
|
|
||||||
|
//The normalised start time of this event relative to the currently running event
|
||||||
|
const normalisedTimeStart = delayedStart + relativeDayOffset * dayInMs;
|
||||||
|
|
||||||
|
const offsetTimestart = normalisedTimeStart - offset;
|
||||||
|
const offsetTimeUntil = offsetTimestart - clock;
|
||||||
|
|
||||||
|
if (isLinkedToLoaded) {
|
||||||
|
//if we are directly linked back to the loaded event we just follow the offset
|
||||||
|
return offsetTimeUntil;
|
||||||
|
}
|
||||||
|
|
||||||
|
const scheduledTimeUntil = normalisedTimeStart - clock;
|
||||||
|
|
||||||
|
const isAheadOfSchedule = offset >= 0;
|
||||||
|
const gapsCanCompensadeForOffset = totalGap + offset >= 0;
|
||||||
|
|
||||||
|
if (isAheadOfSchedule || gapsCanCompensadeForOffset) {
|
||||||
|
// if we are ahead of schedule or the gap can compensate for the amount we are behind then expect to start at the scheduled time
|
||||||
|
return scheduledTimeUntil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise consume as much of the offset as possible with the gap
|
||||||
|
const offsetTimeUntilBufferedByGaps = offsetTimeUntil - totalGap;
|
||||||
|
return offsetTimeUntilBufferedByGaps;
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
isOntimeEvent,
|
isOntimeEvent,
|
||||||
isPlayableEvent,
|
isPlayableEvent,
|
||||||
MaybeString,
|
MaybeString,
|
||||||
OntimeEvent,
|
|
||||||
PlayableEvent,
|
PlayableEvent,
|
||||||
Playback,
|
Playback,
|
||||||
RundownCached,
|
RundownCached,
|
||||||
@@ -272,7 +271,7 @@ export default function Rundown({ data }: RundownProps) {
|
|||||||
let isNextDay = false;
|
let isNextDay = false;
|
||||||
let totalGap = 0;
|
let totalGap = 0;
|
||||||
const isEditMode = appMode === AppMode.Edit;
|
const isEditMode = appMode === AppMode.Edit;
|
||||||
let currentDay = 0;
|
let isLinkedToLoaded = true; //check if the event can link all the way back to the currently playing event
|
||||||
return (
|
return (
|
||||||
<div className={style.rundownContainer} ref={scrollRef} data-testid='rundown'>
|
<div className={style.rundownContainer} ref={scrollRef} data-testid='rundown'>
|
||||||
<DndContext onDragEnd={handleOnDragEnd} sensors={sensors} collisionDetection={closestCenter}>
|
<DndContext onDragEnd={handleOnDragEnd} sensors={sensors} collisionDetection={closestCenter}>
|
||||||
@@ -299,7 +298,10 @@ export default function Rundown({ data }: RundownProps) {
|
|||||||
|
|
||||||
if (isPlayableEvent(entry)) {
|
if (isPlayableEvent(entry)) {
|
||||||
isNextDay = checkIsNextDay(entry, lastEvent);
|
isNextDay = checkIsNextDay(entry, lastEvent);
|
||||||
totalGap += !isPast ? entry.gap : 0;
|
if (!isPast) {
|
||||||
|
totalGap += entry.gap;
|
||||||
|
isLinkedToLoaded = isLinkedToLoaded && entry.linkStart !== null;
|
||||||
|
}
|
||||||
if (isNewLatest(entry, lastEvent)) {
|
if (isNewLatest(entry, lastEvent)) {
|
||||||
// populate previous entry
|
// populate previous entry
|
||||||
thisEvent = entry;
|
thisEvent = entry;
|
||||||
@@ -313,7 +315,6 @@ export default function Rundown({ data }: RundownProps) {
|
|||||||
const hasCursor = entry.id === cursor;
|
const hasCursor = entry.id === cursor;
|
||||||
if (isLoaded) {
|
if (isLoaded) {
|
||||||
isPast = false;
|
isPast = false;
|
||||||
currentDay = (entry as OntimeEvent).dayOffset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -336,7 +337,7 @@ export default function Rundown({ data }: RundownProps) {
|
|||||||
isRolling={featureData.playback === Playback.Roll}
|
isRolling={featureData.playback === Playback.Roll}
|
||||||
isNextDay={isNextDay}
|
isNextDay={isNextDay}
|
||||||
totalGap={totalGap}
|
totalGap={totalGap}
|
||||||
currentDay={currentDay}
|
isLinkedToLoaded={isLinkedToLoaded}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ interface RundownEntryProps {
|
|||||||
playback?: Playback; // we only care about this if this event is playing
|
playback?: Playback; // we only care about this if this event is playing
|
||||||
isRolling: boolean; // we need to know even if not related to this event
|
isRolling: boolean; // we need to know even if not related to this event
|
||||||
totalGap: number;
|
totalGap: number;
|
||||||
currentDay: number;
|
isLinkedToLoaded: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function RundownEntry(props: RundownEntryProps) {
|
export default function RundownEntry(props: RundownEntryProps) {
|
||||||
@@ -56,7 +56,7 @@ export default function RundownEntry(props: RundownEntryProps) {
|
|||||||
eventIndex,
|
eventIndex,
|
||||||
isNextDay,
|
isNextDay,
|
||||||
totalGap,
|
totalGap,
|
||||||
currentDay,
|
isLinkedToLoaded,
|
||||||
} = props;
|
} = props;
|
||||||
const { emitError } = useEmitLog();
|
const { emitError } = useEmitLog();
|
||||||
const { addEvent, updateEvent, batchUpdateEvents, deleteEvent, swapEvents } = useEventAction();
|
const { addEvent, updateEvent, batchUpdateEvents, deleteEvent, swapEvents } = useEventAction();
|
||||||
@@ -178,8 +178,9 @@ export default function RundownEntry(props: RundownEntryProps) {
|
|||||||
isRolling={isRolling}
|
isRolling={isRolling}
|
||||||
gap={data.gap}
|
gap={data.gap}
|
||||||
isNextDay={isNextDay}
|
isNextDay={isNextDay}
|
||||||
dayOffset={data.dayOffset - currentDay}
|
dayOffset={data.dayOffset}
|
||||||
totalGap={totalGap}
|
totalGap={totalGap}
|
||||||
|
isLinkedToLoaded={isLinkedToLoaded}
|
||||||
actionHandler={actionHandler}
|
actionHandler={actionHandler}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ interface EventBlockProps {
|
|||||||
isNextDay: boolean;
|
isNextDay: boolean;
|
||||||
dayOffset: number;
|
dayOffset: number;
|
||||||
totalGap: number;
|
totalGap: number;
|
||||||
|
isLinkedToLoaded: boolean;
|
||||||
actionHandler: (
|
actionHandler: (
|
||||||
action: EventItemActions,
|
action: EventItemActions,
|
||||||
payload?:
|
payload?:
|
||||||
@@ -91,6 +92,7 @@ export default function EventBlock(props: EventBlockProps) {
|
|||||||
isNextDay,
|
isNextDay,
|
||||||
dayOffset,
|
dayOffset,
|
||||||
totalGap,
|
totalGap,
|
||||||
|
isLinkedToLoaded,
|
||||||
actionHandler,
|
actionHandler,
|
||||||
} = props;
|
} = props;
|
||||||
const { selectedEventId, setSelectedEventId, clearSelectedEventId } = useEventIdSwapping();
|
const { selectedEventId, setSelectedEventId, clearSelectedEventId } = useEventIdSwapping();
|
||||||
@@ -310,6 +312,7 @@ export default function EventBlock(props: EventBlockProps) {
|
|||||||
dayOffset={dayOffset}
|
dayOffset={dayOffset}
|
||||||
isPast={isPast}
|
isPast={isPast}
|
||||||
totalGap={totalGap}
|
totalGap={totalGap}
|
||||||
|
isLinkedToLoaded={isLinkedToLoaded}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { IoPlaySkipForward } from '@react-icons/all-files/io5/IoPlaySkipForward'
|
|||||||
import { IoStop } from '@react-icons/all-files/io5/IoStop';
|
import { IoStop } from '@react-icons/all-files/io5/IoStop';
|
||||||
import { IoTime } from '@react-icons/all-files/io5/IoTime';
|
import { IoTime } from '@react-icons/all-files/io5/IoTime';
|
||||||
import { EndAction, MaybeString, Playback, TimerType, TimeStrategy } from 'ontime-types';
|
import { EndAction, MaybeString, Playback, TimerType, TimeStrategy } from 'ontime-types';
|
||||||
import { dayInMs } from 'ontime-utils';
|
|
||||||
|
|
||||||
import { cx } from '../../../common/utils/styleUtils';
|
import { cx } from '../../../common/utils/styleUtils';
|
||||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||||
@@ -47,6 +46,7 @@ interface EventBlockInnerProps {
|
|||||||
dayOffset: number;
|
dayOffset: number;
|
||||||
isPast: boolean;
|
isPast: boolean;
|
||||||
totalGap: number;
|
totalGap: number;
|
||||||
|
isLinkedToLoaded: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EventBlockInner(props: EventBlockInnerProps) {
|
function EventBlockInner(props: EventBlockInnerProps) {
|
||||||
@@ -72,6 +72,7 @@ function EventBlockInner(props: EventBlockInnerProps) {
|
|||||||
dayOffset,
|
dayOffset,
|
||||||
isPast,
|
isPast,
|
||||||
totalGap,
|
totalGap,
|
||||||
|
isLinkedToLoaded,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [renderInner, setRenderInner] = useState(false);
|
const [renderInner, setRenderInner] = useState(false);
|
||||||
@@ -120,11 +121,13 @@ function EventBlockInner(props: EventBlockInnerProps) {
|
|||||||
<EventBlockChip
|
<EventBlockChip
|
||||||
className={style.chipSection}
|
className={style.chipSection}
|
||||||
id={eventId}
|
id={eventId}
|
||||||
trueTimeStart={timeStart + dayOffset * dayInMs}
|
timeStart={timeStart}
|
||||||
|
delay={delay}
|
||||||
|
dayOffset={dayOffset}
|
||||||
|
isLinkedToLoaded={isLinkedToLoaded}
|
||||||
isPast={isPast}
|
isPast={isPast}
|
||||||
isLoaded={loaded}
|
isLoaded={loaded}
|
||||||
totalGap={totalGap}
|
totalGap={totalGap}
|
||||||
isLinkedAndNext={isNext && linkStart !== null}
|
|
||||||
duration={duration}
|
duration={duration}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,27 +3,29 @@ import { Tooltip } from '@chakra-ui/react';
|
|||||||
import { IoCheckmarkCircle } from '@react-icons/all-files/io5/IoCheckmarkCircle';
|
import { IoCheckmarkCircle } from '@react-icons/all-files/io5/IoCheckmarkCircle';
|
||||||
import { isPlaybackActive, MILLIS_PER_MINUTE, MILLIS_PER_SECOND } from 'ontime-utils';
|
import { isPlaybackActive, MILLIS_PER_MINUTE, MILLIS_PER_SECOND } from 'ontime-utils';
|
||||||
|
|
||||||
import { usePlayback, useTimelineStatus } from '../../../../common/hooks/useSocket';
|
import { usePlayback } from '../../../../common/hooks/useSocket';
|
||||||
import useReport from '../../../../common/hooks-query/useReport';
|
import useReport from '../../../../common/hooks-query/useReport';
|
||||||
import { cx } from '../../../../common/utils/styleUtils';
|
import { cx } from '../../../../common/utils/styleUtils';
|
||||||
import { formatDuration, formatTime } from '../../../../common/utils/time';
|
import { formatDuration, formatTime, useTimeUntilStart } from '../../../../common/utils/time';
|
||||||
import { tooltipDelayFast } from '../../../../ontimeConfig';
|
import { tooltipDelayFast } from '../../../../ontimeConfig';
|
||||||
|
|
||||||
import style from './EventBlockChip.module.scss';
|
import style from './EventBlockChip.module.scss';
|
||||||
|
|
||||||
interface EventBlockChipProps {
|
interface EventBlockChipProps {
|
||||||
id: string;
|
id: string;
|
||||||
trueTimeStart: number;
|
timeStart: number;
|
||||||
|
delay: number;
|
||||||
|
dayOffset: number;
|
||||||
isPast: boolean;
|
isPast: boolean;
|
||||||
isLoaded: boolean;
|
isLoaded: boolean;
|
||||||
className: string;
|
className: string;
|
||||||
totalGap: number;
|
totalGap: number;
|
||||||
isLinkedAndNext: boolean;
|
|
||||||
duration: number;
|
duration: number;
|
||||||
|
isLinkedToLoaded: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EventBlockChip(props: EventBlockChipProps) {
|
export default function EventBlockChip(props: EventBlockChipProps) {
|
||||||
const { trueTimeStart, isPast, isLoaded, className, totalGap, isLinkedAndNext, id, duration } = props;
|
const { timeStart, delay, dayOffset, isPast, isLoaded, className, totalGap, id, duration, isLinkedToLoaded } = props;
|
||||||
const { playback } = usePlayback();
|
const { playback } = usePlayback();
|
||||||
|
|
||||||
if (isLoaded) {
|
if (isLoaded) {
|
||||||
@@ -41,7 +43,13 @@ export default function EventBlockChip(props: EventBlockChipProps) {
|
|||||||
return (
|
return (
|
||||||
<Tooltip label='Expected time until start' openDelay={tooltipDelayFast}>
|
<Tooltip label='Expected time until start' openDelay={tooltipDelayFast}>
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<EventUntil trueTimeStart={trueTimeStart} totalGap={totalGap} isLinkedAndNext={isLinkedAndNext} />
|
<EventUntil
|
||||||
|
timeStart={timeStart}
|
||||||
|
delay={delay}
|
||||||
|
dayOffset={dayOffset}
|
||||||
|
totalGap={totalGap}
|
||||||
|
isLinkedToLoaded={isLinkedToLoaded}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
@@ -51,18 +59,17 @@ export default function EventBlockChip(props: EventBlockChipProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface EventUntilProps {
|
interface EventUntilProps {
|
||||||
trueTimeStart: number;
|
timeStart: number;
|
||||||
|
delay: number;
|
||||||
|
dayOffset: number;
|
||||||
totalGap: number;
|
totalGap: number;
|
||||||
isLinkedAndNext: boolean;
|
isLinkedToLoaded: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EventUntil(props: EventUntilProps) {
|
function EventUntil(props: EventUntilProps) {
|
||||||
const { trueTimeStart, totalGap, isLinkedAndNext } = props;
|
const { timeStart, delay, dayOffset, totalGap, isLinkedToLoaded } = props;
|
||||||
const { clock, offset } = useTimelineStatus();
|
|
||||||
|
|
||||||
const consumedOffset = isLinkedAndNext ? offset : Math.min(offset + totalGap, 0);
|
const timeUntil = useTimeUntilStart({ timeStart, delay, dayOffset, totalGap, isLinkedToLoaded });
|
||||||
const offsetTimestart = trueTimeStart - consumedOffset;
|
|
||||||
const timeUntil = offsetTimestart - clock;
|
|
||||||
const isDue = timeUntil < MILLIS_PER_SECOND;
|
const isDue = timeUntil < MILLIS_PER_SECOND;
|
||||||
|
|
||||||
const timeUntilString = isDue ? 'DUE' : `${formatDuration(Math.abs(timeUntil), timeUntil > 2 * MILLIS_PER_MINUTE)}`;
|
const timeUntilString = isDue ? 'DUE' : `${formatDuration(Math.abs(timeUntil), timeUntil > 2 * MILLIS_PER_MINUTE)}`;
|
||||||
|
|||||||
Reference in New Issue
Block a user