refactor: improve detection of day after

fix: prevent mutation of initial object

refactor: download project file

Update apps/client/src/features/rundown/event-block/EventBlock.utils.ts

Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk>

Update apps/client/src/features/rundown/event-block/__tests__/EventBlock.utils.test.ts

Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk>
This commit is contained in:
Carlos Valente
2024-05-10 21:35:12 +02:00
committed by Carlos Valente
parent 07074260a4
commit 3c7e061e51
4 changed files with 64 additions and 40 deletions
@@ -242,13 +242,7 @@ export default function EventBlock(props: EventBlockProps) {
onContextMenu={onContextMenu} onContextMenu={onContextMenu}
id='event-block' id='event-block'
> >
<RundownIndicators <RundownIndicators timeStart={timeStart} previousStart={previousStart} previousEnd={previousEnd} delay={delay} />
timeStart={timeStart}
timeEnd={timeEnd}
previousStart={previousStart}
previousEnd={previousEnd}
delay={delay}
/>
<div className={style.binder} style={{ ...binderColours }} tabIndex={-1}> <div className={style.binder} style={{ ...binderColours }} tabIndex={-1}>
<span className={style.drag} ref={handleRef} {...dragAttributes} {...dragListeners}> <span className={style.drag} ref={handleRef} {...dragAttributes} {...dragListeners}>
@@ -10,24 +10,41 @@ export function formatDelay(timeStart: number, delay: number): string | undefine
return `New start ${timeTag}`; return `New start ${timeTag}`;
} }
/**
* Utility function checks whether a given event is the day after from its predecessor
* We consider an event to be the day after, if it begins before the start of the previous
* @example day after
* // 09:00 - 10:00
* // 08:00 - 10:30 <--- day after
* @example same day
* // 09:00 - 10:00
* // 09:30 - 10:30 <--- same day
*/
function checkIsNextDay(previousStart: number, timeStart: number): boolean {
return timeStart < previousStart;
}
export function formatOverlap( export function formatOverlap(
previousStart: MaybeNumber, previousStart: MaybeNumber,
previousEnd: MaybeNumber, previousEnd: MaybeNumber,
timeStart: number, timeStart: number,
timeEnd: number,
): string | undefined { ): string | undefined {
if (previousEnd === null) return; const noPreviousElement = previousEnd === null || previousStart === null;
if (noPreviousElement) return;
const overlap = previousEnd - timeStart; const overlap = previousEnd - timeStart;
if (overlap === 0) return; if (overlap === 0) return;
if (previousStart && timeStart < previousEnd) { const previousCrossMidnight = previousStart > previousEnd;
const overlap = timeEnd - previousStart; const isNextDay = previousCrossMidnight
if (overlap > 0) { ? checkIsNextDay(previousEnd, timeStart) || previousEnd == 0 // exception for when previousEnd is precisely midnight
const overlapString = removeLeadingZero(millisToString(Math.abs(overlap))); : checkIsNextDay(previousStart, timeStart);
return `Overlap ${overlapString}`;
} const correctedPreviousEnd = previousCrossMidnight ? previousEnd + dayInMs : previousEnd;
const gap = timeStart + dayInMs - previousEnd;
if (isNextDay) {
const gap = dayInMs - correctedPreviousEnd + timeStart;
if (gap === 0) return;
const gapString = removeLeadingZero(millisToString(Math.abs(gap))); const gapString = removeLeadingZero(millisToString(Math.abs(gap)));
return `Gap ${gapString} (next day)`; return `Gap ${gapString} (next day)`;
} }
@@ -6,16 +6,15 @@ import style from './RundownIndicators.module.scss';
interface RundownIndicatorProps { interface RundownIndicatorProps {
timeStart: number; timeStart: number;
timeEnd: number;
previousStart: MaybeNumber; previousStart: MaybeNumber;
previousEnd: MaybeNumber; previousEnd: MaybeNumber;
delay: number; delay: number;
} }
export default function RundownIndicators(props: RundownIndicatorProps) { export default function RundownIndicators(props: RundownIndicatorProps) {
const { timeStart, timeEnd, previousStart, previousEnd, delay } = props; const { timeStart, previousStart, previousEnd, delay } = props;
const hasOverlap = formatOverlap(previousStart, previousEnd, timeStart, timeEnd); const hasOverlap = formatOverlap(previousStart, previousEnd, timeStart);
const hasDelay = formatDelay(timeStart, delay); const hasDelay = formatDelay(timeStart, delay);
return ( return (
@@ -1,3 +1,5 @@
import { MILLIS_PER_HOUR } from 'ontime-utils';
import { formatDelay, formatOverlap } from '../EventBlock.utils'; import { formatDelay, formatOverlap } from '../EventBlock.utils';
describe('formatDelay()', () => { describe('formatDelay()', () => {
@@ -14,35 +16,47 @@ describe('formatOverlap()', () => {
const previousStart = 0; const previousStart = 0;
const previousEnd = 60000; // 1 min const previousEnd = 60000; // 1 min
const timeStart = 30000; // 30 sec const timeStart = 30000; // 30 sec
const timeEnd = 90000; // 1:30 min const result = formatOverlap(previousStart, previousEnd, timeStart);
const result = formatOverlap(previousStart, previousEnd, timeStart, timeEnd);
expect(result).toEqual('Overlap 0:30'); expect(result).toEqual('Overlap 0:30');
}); });
it('bug #949 recognises an overlap between two times', () => {
const previousStart = 46800000; // 13:00:00
const previousEnd = 48600000; // 13:30:00
const timeStart = 48300000; // 13:25:00
const result = formatOverlap(previousStart, previousEnd, timeStart);
expect(result).toEqual('Overlap 5:00');
});
it('handles events the day after, without overlap', () => { it('handles events the day after, without overlap', () => {
const previousStart = new Date(0).setUTCHours(11); const previousStart = 11 * MILLIS_PER_HOUR;
const previousEnd = new Date(0).setUTCHours(12); const previousEnd = 12 * MILLIS_PER_HOUR;
const timeStart = new Date(0).setUTCHours(6); const timeStart = 6 * MILLIS_PER_HOUR;
const timeEnd = new Date(0).setUTCHours(10); const result = formatOverlap(previousStart, previousEnd, timeStart);
const result = formatOverlap(previousStart, previousEnd, timeStart, timeEnd);
expect(result).toBe('Gap 18:00:00 (next day)'); expect(result).toBe('Gap 18:00:00 (next day)');
}); });
it('handles events the day after, with overlap', () => {
const previousStart = new Date(0).setUTCHours(9);
const previousEnd = new Date(0).setUTCHours(10);
const timeStart = new Date(0).setUTCHours(6);
const timeEnd = new Date(0).setUTCHours(11);
const result = formatOverlap(previousStart, previousEnd, timeStart, timeEnd);
expect(result).toBe('Overlap 02:00:00');
});
it('handles events the day after, with gap', () => { it('handles events the day after, with gap', () => {
const previousStart = new Date(0).setUTCHours(17); const previousStart = 17 * MILLIS_PER_HOUR;
const previousEnd = new Date(0).setUTCHours(23); const previousEnd = 23 * MILLIS_PER_HOUR;
const timeStart = new Date(0).setUTCHours(9); const timeStart = 9 * MILLIS_PER_HOUR;
const timeEnd = new Date(0).setUTCHours(11); const result = formatOverlap(previousStart, previousEnd, timeStart);
const result = formatOverlap(previousStart, previousEnd, timeStart, timeEnd);
expect(result).toBe('Gap 10:00:00 (next day)'); expect(result).toBe('Gap 10:00:00 (next day)');
}); });
it('handles events the day after, with previous ending at midnight', () => {
const previousStart = 23 * MILLIS_PER_HOUR; // 23:00:00
const previousEnd = 0; // 00:00:00
const timeStart = 1 * MILLIS_PER_HOUR; // 01:00:00
const result = formatOverlap(previousStart, previousEnd, timeStart);
expect(result).toBe('Gap 01:00:00 (next day)');
});
it('handles events the day after, with previous ending over midnight', () => {
const previousStart = 23 * MILLIS_PER_HOUR;
const previousEnd = 1 * MILLIS_PER_HOUR;
const timeStart = 2 * MILLIS_PER_HOUR;
const result = formatOverlap(previousStart, previousEnd, timeStart);
expect(result).toBe('Gap 01:00:00');
});
}); });