refactor: allow overriding timer type in view

This commit is contained in:
Carlos Valente
2025-01-24 11:29:28 +01:00
committed by Carlos Valente
parent 78c2bd79b5
commit d58335d1a1
4 changed files with 43 additions and 11 deletions
@@ -7,11 +7,17 @@ import { formatTime } from '../../../common/utils/time';
type TimerTypeParams = Pick<ViewExtendedTimer, 'countToEnd' | 'timerType' | 'current' | 'elapsed' | 'clock'>; type TimerTypeParams = Pick<ViewExtendedTimer, 'countToEnd' | 'timerType' | 'current' | 'elapsed' | 'clock'>;
export function getTimerByType(freezeEnd: boolean, timerObject?: TimerTypeParams): number | null { export function getTimerByType(
freezeEnd: boolean,
timerObject?: TimerTypeParams,
timerTypeOverride?: TimerType,
): number | null {
if (!timerObject) { if (!timerObject) {
return null; return null;
} }
const viewTimerType = timerTypeOverride ?? timerObject.timerType;
if (timerObject.countToEnd) { if (timerObject.countToEnd) {
if (timerObject.current === null) { if (timerObject.current === null) {
return null; return null;
@@ -19,7 +25,7 @@ export function getTimerByType(freezeEnd: boolean, timerObject?: TimerTypeParams
return freezeEnd ? Math.max(timerObject.current, 0) : timerObject.current; return freezeEnd ? Math.max(timerObject.current, 0) : timerObject.current;
} }
switch (timerObject.timerType) { switch (viewTimerType) {
case TimerType.CountDown: case TimerType.CountDown:
if (timerObject.current === null) { if (timerObject.current === null) {
return null; return null;
@@ -31,10 +37,8 @@ export function getTimerByType(freezeEnd: boolean, timerObject?: TimerTypeParams
return timerObject.clock; return timerObject.clock;
case TimerType.None: case TimerType.None:
return null; return null;
default: { default:
const exhaustiveCheck: never = timerObject.timerType; return null;
return exhaustiveCheck;
}
} }
} }
+6 -4
View File
@@ -65,6 +65,7 @@ export default function Timer(props: TimerProps) {
removeLeadingZeros, removeLeadingZeros,
mainSource, mainSource,
secondarySource, secondarySource,
timerType,
} = useTimerOptions(); } = useTimerOptions();
const { getLocalizedString } = useTranslation(); const { getLocalizedString } = useTranslation();
@@ -73,6 +74,7 @@ export default function Timer(props: TimerProps) {
useWindowTitle('Timer'); useWindowTitle('Timer');
// gather modifiers // gather modifiers
const viewTimerType = timerType ?? time.timerType;
const showOverlay = getShowMessage(message.timer); const showOverlay = getShowMessage(message.timer);
const { showEndMessage, showFinished, showWarning, showDanger } = getShowModifiers( const { showEndMessage, showFinished, showWarning, showDanger } = getShowModifiers(
time.timerType, time.timerType,
@@ -81,8 +83,8 @@ export default function Timer(props: TimerProps) {
viewSettings, viewSettings,
); );
const isPlaying = getIsPlaying(time.playback); const isPlaying = getIsPlaying(time.playback);
const showClock = !hideClock && getShowClock(time.timerType); const showClock = !hideClock && getShowClock(viewTimerType);
const showProgressBar = !hideProgress && getShowProgressBar(time.timerType); const showProgressBar = !hideProgress && getShowProgressBar(viewTimerType);
// gather card data // gather card data
const { showNow, nowMain, nowSecondary, showNext, nextMain, nextSecondary } = getCardData( const { showNow, nowMain, nowSecondary, showNext, nextMain, nextSecondary } = getCardData(
@@ -97,8 +99,8 @@ export default function Timer(props: TimerProps) {
// gather timer data // gather timer data
const totalTime = getTotalTime(time.duration, time.addedTime); const totalTime = getTotalTime(time.duration, time.addedTime);
const clock = formatTime(time.clock); const clock = formatTime(time.clock);
const stageTimer = getTimerByType(viewSettings.freezeEnd, time); const stageTimer = getTimerByType(viewSettings.freezeEnd, time, timerType);
const display = getFormattedTimer(stageTimer, time.timerType, localisedMinutes, { const display = getFormattedTimer(stageTimer, viewTimerType, localisedMinutes, {
removeSeconds: hideTimerSeconds, removeSeconds: hideTimerSeconds,
removeLeadingZero: removeLeadingZeros, removeLeadingZero: removeLeadingZeros,
}); });
+23 -1
View File
@@ -1,6 +1,7 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { CustomFields, OntimeEvent } from 'ontime-types'; import { CustomFields, OntimeEvent, TimerType } from 'ontime-types';
import { validateTimerType } from 'ontime-utils';
import { import {
getTimeOption, getTimeOption,
@@ -11,6 +12,14 @@ import {
import { ViewOption } from '../../common/components/view-params-editor/types'; import { ViewOption } from '../../common/components/view-params-editor/types';
import { isStringBoolean } from '../../features/viewers/common/viewUtils'; import { isStringBoolean } from '../../features/viewers/common/viewUtils';
// manually match the properties of TimerType excluding the None
const timerDisplayOptions = {
'no-overrides': 'No Overrides',
'count-up': 'Count up',
'count-down': 'Count down',
clock: 'Clock',
};
export const getTimerOptions = (timeFormat: string, customFields: CustomFields): ViewOption[] => { export const getTimerOptions = (timeFormat: string, customFields: CustomFields): ViewOption[] => {
const mainOptions = makeOptionsFromCustomFields(customFields, { title: 'Title', note: 'Note' }); const mainOptions = makeOptionsFromCustomFields(customFields, { title: 'Title', note: 'Note' });
const secondaryOptions = makeOptionsFromCustomFields(customFields, { title: 'Title', note: 'Note' }); const secondaryOptions = makeOptionsFromCustomFields(customFields, { title: 'Title', note: 'Note' });
@@ -21,6 +30,14 @@ export const getTimerOptions = (timeFormat: string, customFields: CustomFields):
{ section: 'Timer Options' }, { section: 'Timer Options' },
hideTimerSeconds, hideTimerSeconds,
showLeadingZeros, showLeadingZeros,
{
id: 'timerType',
title: 'Timer type',
description: 'Override the timer type',
type: 'option',
values: timerDisplayOptions,
defaultValue: 'no-overrides',
},
{ section: 'Data sources' }, { section: 'Data sources' },
{ {
id: 'main', id: 'main',
@@ -87,6 +104,7 @@ type TimerOptions = {
removeLeadingZeros: boolean; removeLeadingZeros: boolean;
mainSource: keyof OntimeEvent | null; mainSource: keyof OntimeEvent | null;
secondarySource: keyof OntimeEvent | null; secondarySource: keyof OntimeEvent | null;
timerType?: TimerType;
}; };
/** /**
@@ -94,6 +112,7 @@ type TimerOptions = {
* the names and fallbacks are manually matched with timerOptions * the names and fallbacks are manually matched with timerOptions
*/ */
function getOptionsFromParams(searchParams: URLSearchParams): TimerOptions { function getOptionsFromParams(searchParams: URLSearchParams): TimerOptions {
const timerType = validateTimerType(searchParams.get('timerType'), TimerType.None);
// we manually make an object that matches the key above // we manually make an object that matches the key above
return { return {
hideClock: isStringBoolean(searchParams.get('hideClock')), hideClock: isStringBoolean(searchParams.get('hideClock')),
@@ -106,6 +125,9 @@ function getOptionsFromParams(searchParams: URLSearchParams): TimerOptions {
mainSource: searchParams.get('main') as keyof OntimeEvent | null, mainSource: searchParams.get('main') as keyof OntimeEvent | null,
secondarySource: searchParams.get('secondary-src') as keyof OntimeEvent | null, secondarySource: searchParams.get('secondary-src') as keyof OntimeEvent | null,
// none doesnt make sense as a configuration of the view
timerType: timerType === TimerType.None ? undefined : timerType,
}; };
} }
@@ -27,4 +27,8 @@ describe('validateTimerType()', () => {
expect(emptyType).toBe(TimerType.Clock); expect(emptyType).toBe(TimerType.Clock);
expect(invalidType).toBe(TimerType.CountDown); expect(invalidType).toBe(TimerType.CountDown);
}); });
it('handles a null value from params', () => {
const nullType = validateTimerType(null, TimerType.Clock);
expect(nullType).toBe(TimerType.Clock);
});
}); });