wip: countdown view redesign

This commit is contained in:
Carlos Valente
2025-06-03 21:45:17 +02:00
parent 0c7c90cae4
commit 2f8f4ad129
16 changed files with 889 additions and 127 deletions
@@ -118,7 +118,8 @@
padding-inline: 0.25rem;
background-color: $gray-1250;
color: $ui-white;
white-space: pre;
// allow multi-line text but trim before
white-space: pre-line;
}
}
@@ -20,7 +20,7 @@ import { useTranslation } from '../../../translation/TranslationProvider';
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
import { getFormattedTimer, isStringBoolean } from '../common/viewUtils';
import { fetchTimerData, getTimerItems, TimerMessage } from './countdown.helpers';
import { getSubscriptionDisplayData, getTimerItems, TimerMessage } from '../../../views/countdown/countdown.utils';
import { getCountdownOptions } from './countdown.options';
import CountdownSelect from './CountdownSelect';
@@ -78,7 +78,7 @@ export default function Countdown(props: CountdownProps) {
}
}, [backstageEvents, searchParams]);
const { message: runningMessage, timer: runningTimer } = fetchTimerData(time, follow, selectedId, runtime.offset);
const { message: runningMessage, timer: runningTimer } = getSubscriptionDisplayData(time, follow, selectedId, runtime.offset);
const standby = time.playback !== Playback.Play && time.playback !== Playback.Roll && selectedId === follow?.id;
const finished = time.phase === TimerPhase.Overtime;
@@ -4,8 +4,7 @@ import { OntimeEntry, OntimeEvent, SupportedEntry } from 'ontime-types';
import Empty from '../../../common/components/state/Empty';
import { formatTime } from '../../../common/utils/time';
import { useTranslation } from '../../../translation/TranslationProvider';
import { sanitiseTitle } from './countdown.helpers';
import { sanitiseTitle } from '../../../views/countdown/countdown.utils';
import './Countdown.scss';
@@ -1,6 +1,6 @@
import { dayInMs } from 'ontime-utils';
import { fetchTimerData, sanitiseTitle, TimerMessage } from '../countdown.helpers';
import { getSubscriptionDisplayData, sanitiseTitle, TimerMessage } from '../../../../views/countdown/countdown.utils';
describe('sanitiseTitle() function', () => {
it('should return a title when valid', () => {
@@ -26,7 +26,7 @@ describe('fetchTimerData() function', () => {
const follow = { id: followId };
const time = { current: currentMockValue };
const { message, timer } = fetchTimerData(time, follow, followId);
const { message, timer } = getSubscriptionDisplayData(time, follow, followId);
expect(message).toBe(TimerMessage.running);
expect(timer).toBe(currentMockValue);
});
@@ -37,7 +37,7 @@ describe('fetchTimerData() function', () => {
const follow = { id: 'anotherevent', timeStart: startMockValue };
const time = { clock: timeNow };
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
const { message, timer } = getSubscriptionDisplayData(time, follow, 'notthesameevent');
expect(message).toBe(TimerMessage.toStart);
expect(timer).toBe(startMockValue - timeNow);
});
@@ -50,7 +50,7 @@ describe('fetchTimerData() function', () => {
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
const time = { clock: timeNow, current: endMockValue - startMockValue };
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
const { message, timer } = getSubscriptionDisplayData(time, follow, 'notthesameevent');
expect(message).toBe(TimerMessage.waiting);
expect(timer).toBe(endMockValue - startMockValue);
});
@@ -63,7 +63,7 @@ describe('fetchTimerData() function', () => {
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
const time = { clock: timeNow, current: endMockValue - startMockValue };
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
const { message, timer } = getSubscriptionDisplayData(time, follow, 'notthesameevent');
expect(message).toBe(TimerMessage.ended);
expect(timer).toBe(endMockValue);
});
@@ -76,7 +76,7 @@ describe('fetchTimerData() function', () => {
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
const time = { clock: timeNow, current: dayInMs + endMockValue - startMockValue };
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
const { message, timer } = getSubscriptionDisplayData(time, follow, 'notthesameevent');
expect(message).toBe(TimerMessage.waiting);
expect(timer).toBe(dayInMs + endMockValue - startMockValue);
});
@@ -89,7 +89,7 @@ describe('fetchTimerData() function', () => {
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
const time = { clock: timeNow, current: dayInMs + endMockValue - startMockValue };
const { message, timer } = fetchTimerData(time, follow, followId);
const { message, timer } = getSubscriptionDisplayData(time, follow, followId);
expect(message).toBe(TimerMessage.running);
expect(timer).toBe(dayInMs + endMockValue - startMockValue);
});
@@ -102,7 +102,7 @@ describe('fetchTimerData() function', () => {
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
const time = { clock: timeNow, current: dayInMs + endMockValue - startMockValue };
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
const { message, timer } = getSubscriptionDisplayData(time, follow, 'notthesameevent');
expect(message).toBe(TimerMessage.toStart);
expect(timer).toBe(startMockValue - timeNow);
});
@@ -1,106 +0,0 @@
import { OntimeEvent, Playback } from 'ontime-types';
import { ViewExtendedTimer } from '../../../common/models/TimeManager.type';
import { formatTime } from '../../../common/utils/time';
import { isStringBoolean } from '../common/viewUtils';
export enum TimerMessage {
toStart = 'to_start',
waiting = 'waiting',
running = 'running',
ended = 'ended',
unhandled = '',
}
/**
* Parses string as a title
*/
export const sanitiseTitle = (title: string | null) => (title ? title : '{no title}');
/**
* Returns a parsed timer and relevant status message
*/
export const fetchTimerData = (
time: ViewExtendedTimer,
follow: OntimeEvent | null,
selectedId: string | null,
offset: number,
): { message: TimerMessage; timer: number } => {
if (follow === null) {
return { message: TimerMessage.unhandled, timer: 0 };
}
if (selectedId === follow.id) {
// if it is selected, it may not be running
return {
message: time.playback === Playback.Pause ? TimerMessage.waiting : TimerMessage.running,
timer: time.current ?? 0,
};
}
const showProjected = getShouldShowProjected();
const addedTime = showProjected ? offset : 0;
if (time.clock < follow.timeStart) {
// if it hasnt started, we count to start
return { message: TimerMessage.toStart, timer: follow.timeStart - time.clock - addedTime };
}
if (follow.timeStart <= time.clock && time.clock <= follow.timeEnd) {
// if it has started, we show running timer
return { message: TimerMessage.waiting, timer: time.current ?? 0 };
}
// running timer timer is not the one we are following
// ends day after
if (follow.timeStart > follow.timeEnd) {
if (follow.timeStart > time.clock) {
// if it hasnt started, we count to start
return { message: TimerMessage.toStart, timer: follow.timeStart - time.clock - addedTime };
}
if (follow.timeStart <= time.clock) {
// if it has started, we show running timer
return { message: TimerMessage.waiting, timer: time.current ?? 0 };
}
// if it has ended, we show how long ago
return { message: TimerMessage.ended, timer: follow.timeEnd };
}
// if it has ended, we show how long ago
return { message: TimerMessage.ended, timer: follow.timeEnd };
};
/**
* Gets values for the timer items
*/
export function getTimerItems(start: number | undefined, end: number | undefined, delay: number, offset: number) {
if (start == null || end == null) {
return {
scheduledStart: '',
scheduledEnd: '',
projectedStart: '',
projectedEnd: '',
};
}
const showProjected = getShouldShowProjected();
const scheduledStart = formatTime(start + delay);
const scheduledEnd = formatTime(end + delay);
const projectedStart = showProjected ? formatTime(start + delay - offset) : '';
const projectedEnd = showProjected ? formatTime(end + delay - offset) : '';
return {
scheduledStart,
scheduledEnd,
projectedStart,
projectedEnd,
};
}
/**
* Gets from the URL whether the showProjected option is active
*/
function getShouldShowProjected() {
const params = new URL(document.location.href).searchParams;
return isStringBoolean(params.get('showProjected'));
}