Refactor/time formatting (#696)

* chore: upgrade related dependencies

* fix: skip sentry on dev

* refactor: unify time formatting

* refactor: display default format for page
This commit is contained in:
Carlos Valente
2024-01-09 15:30:11 +01:00
committed by GitHub
parent 9031051465
commit b4e73ff6b5
54 changed files with 1177 additions and 687 deletions
@@ -1,9 +1,8 @@
import { useEffect, useState } from 'react';
import QRCode from 'react-qr-code';
import { useSearchParams } from 'react-router-dom';
import { AnimatePresence, motion } from 'framer-motion';
import { Message, OntimeEvent, ProjectData, Settings, SupportedEvent, ViewSettings } from 'ontime-types';
import { formatDisplay } from 'ontime-utils';
import { millisToString, removeLeadingZero } from 'ontime-utils';
import { overrideStylesURL } from '../../../common/api/apiConstants';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
@@ -16,8 +15,7 @@ import { getBackstageOptions } from '../../../common/components/view-params-edit
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { formatTime } from '../../../common/utils/time';
import { isStringBoolean } from '../../../common/utils/viewUtils';
import { formatTime, getDefaultFormat } from '../../../common/utils/time';
import { useTranslation } from '../../../translation/TranslationProvider';
import { titleVariants } from '../common/animation';
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
@@ -41,8 +39,6 @@ export default function Backstage(props: BackstageProps) {
const { isMirrored, publ, eventNow, eventNext, time, backstageEvents, selectedId, general, viewSettings, settings } =
props;
const [searchParams] = useSearchParams();
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
const { getLocalizedString } = useTranslation();
const [blinkClass, setBlinkClass] = useState(false);
@@ -68,36 +64,22 @@ export default function Backstage(props: BackstageProps) {
return null;
}
const hideSeconds = isStringBoolean(searchParams.get('hideSeconds'));
const formatOptions = {
showSeconds: !hideSeconds,
format: 'hh:mm:ss a',
};
const clock = formatTime(time.clock, formatOptions);
const startedAt = formatTime(time.startedAt, formatOptions);
const clock = formatTime(time.clock);
const startedAt = formatTime(time.startedAt);
const isNegative = (time.current ?? 0) < 0;
const expectedFinish = isNegative
? getLocalizedString('countdown.overtime')
: formatTime(time.expectedFinish, formatOptions);
const expectedFinish = isNegative ? getLocalizedString('countdown.overtime') : formatTime(time.expectedFinish);
const qrSize = Math.max(window.innerWidth / 15, 128);
const filteredEvents = backstageEvents.filter((event) => event.type === SupportedEvent.Event);
const showPublicMessage = publ.text && publ.visible;
const showProgress = time.playback !== 'stop';
let stageTimer;
if (time.current === null) {
stageTimer = '- - : - -';
} else {
stageTimer = formatDisplay(Math.abs(time.current), true);
if (isNegative) {
stageTimer = `-${stageTimer}`;
}
}
let stageTimer = millisToString(time.current, { fallback: '- - : - -' });
stageTimer = removeLeadingZero(stageTimer);
const totalTime = (time.duration ?? 0) + (time.addedTime ?? 0);
const backstageOptions = getBackstageOptions(settings?.timeFormat ?? '24');
const defaultFormat = getDefaultFormat(settings?.timeFormat);
const backstageOptions = getBackstageOptions(defaultFormat);
return (
<div className={`backstage ${isMirrored ? 'mirror' : ''}`} data-testid='backstage-view'>
@@ -9,7 +9,7 @@ import ViewParamsEditor from '../../../common/components/view-params-editor/View
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { OverridableOptions } from '../../../common/models/View.types';
import { formatTime } from '../../../common/utils/time';
import { formatTime, getDefaultFormat } from '../../../common/utils/time';
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
import './Clock.scss';
@@ -21,11 +21,6 @@ interface ClockProps {
settings: Settings | undefined;
}
const formatOptions = {
showSeconds: true,
format: 'hh:mm:ss a',
};
export default function Clock(props: ClockProps) {
const { isMirrored, time, viewSettings, settings } = props;
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
@@ -122,10 +117,11 @@ export default function Clock(props: ClockProps) {
}
}
const clock = formatTime(time.clock, formatOptions);
const clock = formatTime(time.clock);
const clean = clock.replace('/:/g', '');
const clockOptions = getClockOptions(settings?.timeFormat ?? '24');
const defaultFormat = getDefaultFormat(settings?.timeFormat);
const clockOptions = getClockOptions(defaultFormat);
return (
<div
@@ -1,21 +0,0 @@
import { removePrependedZero } from '../viewerUtils.js';
describe('removePrependedZero', () => {
it('should remove "00:" from the start of the string', () => {
const timer = '00:10:10';
const result = removePrependedZero(timer);
expect(result).toBe('10:10');
});
it('should remove "00:0:" from the start of the string', () => {
const timer = '00:01:10';
const result = removePrependedZero(timer);
expect(result).toBe('1:10');
});
it('should not modify the string if it does not start with "00:" or "00:0:"', () => {
const timer = '10:10:10';
const result = removePrependedZero(timer);
expect(result).toBe(timer);
});
});
@@ -0,0 +1,25 @@
/**
* encapsulate logic related to showing a clock timer
*/
import { MaybeNumber } from 'ontime-types';
import { formatTime } from '../../../../common/utils/time';
import { FORMAT_12, FORMAT_24 } from '../../../../viewerConfig';
import SuperscriptTime from '../superscript-time/SuperscriptTime';
interface ClockTimeProps {
value: MaybeNumber;
preferredFormat12?: string;
preferredFormat24?: string;
className?: string;
}
export default function ClockTime(props: ClockTimeProps) {
const { value, preferredFormat12 = FORMAT_12, preferredFormat24 = FORMAT_24, className } = props;
// TODO: should we get the params from URL here to see if the user is overriding the default?
const formattedTime = formatTime(value, { format12: preferredFormat12, format24: preferredFormat24 });
return <SuperscriptTime className={className} time={formattedTime} />;
}
@@ -0,0 +1,28 @@
/**
* encapsulate logic related to showing a running timer
*/
import { MaybeNumber } from 'ontime-types';
import { millisToString, removeLeadingZero, removeSeconds } from 'ontime-utils';
interface RunningTimeProps {
value: MaybeNumber;
hideSeconds?: boolean;
hideLeadingZero?: boolean;
className?: string;
}
export default function RunningTime(props: RunningTimeProps) {
const { value, hideSeconds, hideLeadingZero, className } = props;
let formattedTime = millisToString(value);
if (hideLeadingZero) {
formattedTime = removeLeadingZero(formattedTime);
}
if (hideSeconds) {
formattedTime = removeSeconds(formattedTime);
}
return <div className={className}>{formattedTime}</div>;
}
@@ -23,17 +23,3 @@ export function getTimerByType(timerObject?: TimerTypeParams): number | null {
}
}
}
/**
* Receives a string such as 00:10:10 and removes the hours field if it is 00
* @param timer
*/
export const removePrependedZero = (timer: string): string => {
if (timer.startsWith('00:0')) {
return timer.slice(4);
}
if (timer.startsWith('00:')) {
return timer.slice(3);
}
return timer;
};
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { OntimeEvent, OntimeRundownEntry, Playback, Settings, SupportedEvent, ViewSettings } from 'ontime-types';
import { formatDisplay } from 'ontime-utils';
import { millisToString, removeLeadingZero } from 'ontime-utils';
import { overrideStylesURL } from '../../../common/api/apiConstants';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
@@ -9,8 +9,7 @@ import { getCountdownOptions } from '../../../common/components/view-params-edit
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { formatTime } from '../../../common/utils/time';
import { isStringBoolean } from '../../../common/utils/viewUtils';
import { formatTime, getDefaultFormat } from '../../../common/utils/time';
import { useTranslation } from '../../../translation/TranslationProvider';
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
@@ -19,11 +18,6 @@ import CountdownSelect from './CountdownSelect';
import './Countdown.scss';
const formatOptionsFinished = {
showSeconds: false,
format: 'hh:mm a',
};
interface CountdownProps {
isMirrored: boolean;
backstageEvents: OntimeEvent[];
@@ -95,24 +89,24 @@ export default function Countdown(props: CountdownProps) {
const isSelected = runningMessage === TimerMessage.running;
const delayedTimerStyles = delay > 0 ? 'aux-timers__value--delayed' : '';
const hideSeconds = isStringBoolean(searchParams.get('hideSeconds'));
const formatOptions = {
showSeconds: !hideSeconds,
format: 'hh:mm:ss a',
const clock = formatTime(time.clock);
const startTime = follow === null ? '...' : formatTime(follow.timeStart + delay);
const endTime = follow === null ? '...' : formatTime(follow.timeEnd + delay);
const formatTimer = (): string => {
if (runningMessage === TimerMessage.ended) {
return formatTime(runningTimer, { format12: 'hh:mm a', format24: 'HH:mm' });
}
let formattedTime = millisToString(isSelected ? runningTimer : runningTimer + delay);
if (isSelected || runningMessage === TimerMessage.waiting) {
formattedTime = removeLeadingZero(formattedTime);
}
return formattedTime;
};
const formattedTimer = formatTimer();
const clock = formatTime(time.clock, formatOptions);
const startTime = follow === null ? '...' : formatTime(follow.timeStart + delay, formatOptions);
const endTime = follow === null ? '...' : formatTime(follow.timeEnd + delay, formatOptions);
const formattedTimer =
runningMessage === TimerMessage.ended
? formatTime(runningTimer, formatOptionsFinished)
: formatDisplay(
isSelected ? runningTimer : runningTimer + delay,
isSelected || runningMessage === TimerMessage.waiting,
);
const timeOption = getCountdownOptions(settings?.timeFormat ?? '24');
const defaultFormat = getDefaultFormat(settings?.timeFormat);
const timeOption = getCountdownOptions(defaultFormat);
return (
<div className={`countdown ${isMirrored ? 'mirror' : ''}`} data-testid='countdown-view'>
@@ -135,7 +129,7 @@ export default function Countdown(props: CountdownProps) {
time={formattedTimer}
className={`timer ${standby ? 'timer--paused' : ''} ${isRunningFinished ? 'timer--finished' : ''}`}
/>
<div className='title'>{follow?.title || 'Untitled Event'}</div>
{follow?.title && <div className='title'>{follow.title}</div>}
<div className='timer-group'>
<div className='aux-timers'>
@@ -9,14 +9,12 @@ import { sanitiseTitle } from './countdown.helpers';
import './Countdown.scss';
const formatOptions = {
format: 'hh:mm a',
};
interface CountdownSelectProps {
events: OntimeRundownEntry[];
}
const scheduleFormat = { format12: 'hh:mm a', format24: 'HH:mm' };
export default function CountdownSelect(props: CountdownSelectProps) {
const { events } = props;
const { getLocalizedString } = useTranslation();
@@ -35,8 +33,8 @@ export default function CountdownSelect(props: CountdownSelectProps) {
filteredEvents.map((event: OntimeEvent, counter: number) => {
const index = counter + 1;
const title = sanitiseTitle(event.title);
const start = formatTime(event.timeStart, formatOptions);
const end = formatTime(event.timeEnd, formatOptions);
const start = formatTime(event.timeStart, scheduleFormat);
const end = formatTime(event.timeEnd, scheduleFormat);
return (
<li key={event.id}>
@@ -1,6 +1,7 @@
import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types';
import { millisToString, removeLeadingZero, removeSeconds } from 'ontime-utils';
import { overrideStylesURL } from '../../../common/api/apiConstants';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
@@ -9,10 +10,9 @@ import ViewParamsEditor from '../../../common/components/view-params-editor/View
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { OverridableOptions } from '../../../common/models/View.types';
import { formatTime } from '../../../common/utils/time';
import { isStringBoolean } from '../../../common/utils/viewUtils';
import { useTranslation } from '../../../translation/TranslationProvider';
import { getTimerByType, removePrependedZero } from '../common/viewerUtils';
import { getTimerByType } from '../common/viewerUtils';
import './MinimalTimer.scss';
@@ -153,13 +153,17 @@ export default function MinimalTimer(props: MinimalTimerProps) {
: viewSettings.normalColor;
const stageTimer = getTimerByType(time);
let display = '-- : -- : --';
let display = millisToString(stageTimer, { fallback: '-- : -- : --' });
if (stageTimer !== null) {
display = formatTime(stageTimer, {
showSeconds: !userOptions.hideTimerSeconds,
format: 'hh:mm:ss a',
});
display = removePrependedZero(display);
if (hideTimerSeconds) {
display = removeSeconds(display);
}
display = removeLeadingZero(display);
// last unit rounds up in negative timers
const isNegative = stageTimer ?? 0 < 0;
if (isNegative && display === '0') {
display = '-1';
}
if (display.length < 3) {
display = `${display} ${getLocalizedString('common.minutes')}`;
}
@@ -1,6 +1,5 @@
import { useEffect } from 'react';
import QRCode from 'react-qr-code';
import { useSearchParams } from 'react-router-dom';
import { AnimatePresence, motion } from 'framer-motion';
import { Message, OntimeEvent, ProjectData, Settings, ViewSettings } from 'ontime-types';
@@ -14,8 +13,7 @@ import { getPublicOptions } from '../../../common/components/view-params-editor/
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { formatTime } from '../../../common/utils/time';
import { isStringBoolean } from '../../../common/utils/viewUtils';
import { formatTime, getDefaultFormat } from '../../../common/utils/time';
import { useTranslation } from '../../../translation/TranslationProvider';
import { titleVariants } from '../common/animation';
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
@@ -49,8 +47,6 @@ export default function Public(props: BackstageProps) {
settings,
} = props;
const [searchParams] = useSearchParams();
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
const { getLocalizedString } = useTranslation();
@@ -64,17 +60,11 @@ export default function Public(props: BackstageProps) {
}
const showPublicMessage = publ.text && publ.visible;
const hideSeconds = isStringBoolean(searchParams.get('hideSeconds'));
const formatOptions = {
showSeconds: !hideSeconds,
format: 'hh:mm:ss a',
};
const clock = formatTime(time.clock, formatOptions);
const clock = formatTime(time.clock);
const qrSize = Math.max(window.innerWidth / 15, 128);
const publicOptions = getPublicOptions(settings?.timeFormat ?? '24');
const defaultFormat = getDefaultFormat(settings?.timeFormat);
const publicOptions = getPublicOptions(defaultFormat);
return (
<div className={`public-screen ${isMirrored ? 'mirror' : ''}`} data-testid='public-view'>
@@ -2,7 +2,7 @@ import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import type { OntimeEvent, OntimeRundown, Settings, ViewSettings } from 'ontime-types';
import { isOntimeEvent, Playback } from 'ontime-types';
import { formatDisplay } from 'ontime-utils';
import { millisToString, removeSeconds } from 'ontime-utils';
import { overrideStylesURL } from '../../../common/api/apiConstants';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
@@ -11,13 +11,11 @@ import ViewParamsEditor from '../../../common/components/view-params-editor/View
import useFitText from '../../../common/hooks/useFitText';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { secondsInMillis } from '../../../common/utils/dateConfig';
import { formatTime, resolveTimeFormat } from '../../../common/utils/time';
import { mth } from '../../../common/utils/timeConstants';
import { formatTime, getDefaultFormat } from '../../../common/utils/time';
import { isStringBoolean } from '../../../common/utils/viewUtils';
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
import { trimRundown } from './studioClock.utils';
import { secondsInMillis, trimRundown } from './studioClock.utils';
import './StudioClock.scss';
@@ -36,6 +34,7 @@ interface StudioClockProps {
export default function StudioClock(props: StudioClockProps) {
const { isMirrored, eventNext, time, backstageEvents, selectedId, nextId, onAir, viewSettings, settings } = props;
// TODO: can we prevent the Flash of Unstyled Content on the 7segment fonts?
// deferring rendering seems to affect styling (font and useFitText)
useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
const { fontSize: titleFontSize, ref: titleRef } = useFitText({ maxFontSize: 500 });
@@ -52,45 +51,51 @@ export default function StudioClock(props: StudioClockProps) {
document.title = 'ontime - Studio Clock';
}, []);
const hideSeconds = isStringBoolean(searchParams.get('hideSeconds'));
const formatOptions = {
showSeconds: !hideSeconds,
format: 'hh:mm:ss',
};
let clock = formatTime(time.clock);
let hasAmPm = '';
if (clock.includes('AM')) {
clock = clock.replace('PM', '');
hasAmPm = 'AM';
} else if (clock.includes('PM')) {
clock = clock.replace('PM', '');
hasAmPm = 'PM';
}
const clock = formatTime(time.clock, formatOptions);
const secondsNow = secondsInMillis(time.clock);
const isNegative = (time.current ?? 0) < 0;
const isPaused = time.playback === Playback.Pause;
const studioClockOptions = getStudioClockOptions(settings?.timeFormat ?? '24');
const defaultFormat = getDefaultFormat(settings?.timeFormat);
const studioClockOptions = getStudioClockOptions(defaultFormat);
const delayed = backstageEvents.filter((event) => isOntimeEvent(event)) as OntimeEvent[];
const trimmedRundown = trimRundown(delayed, selectedId, MAX_TITLES);
const isAm = time.clock / (mth * 12) > 12;
const timeFormat = resolveTimeFormat();
let timer = millisToString(time.current, { fallback: '---' });
const hideSeconds = isStringBoolean(searchParams.get('hideTimerSeconds'));
if (time.current != null && hideSeconds) {
timer = removeSeconds(timer);
}
return (
<div className={`studio-clock ${isMirrored ? 'mirror' : ''}`} data-testid='studio-view'>
<NavigationMenu />
<ViewParamsEditor paramFields={studioClockOptions} />
<div className='clock-container'>
{timeFormat == '12' && <div className='clock__ampm'>{isAm ? 'am' : 'pm'}</div>}
<div className={`studio-timer ${formatOptions.showSeconds ? 'studio-timer--with-seconds' : ''}`}>{clock}</div>
{hasAmPm && <div className='clock__ampm'>{hasAmPm}</div>}
<div className={`studio-timer ${!hideSeconds ? 'studio-timer--with-seconds' : ''}`}>{clock}</div>
<div
ref={titleRef}
className='next-title'
style={{ fontSize: titleFontSize, height: '12.5vh', width: '100%', maxWidth: '80%' }}
>
{eventNext?.title ?? '---'}
{eventNext?.title}
</div>
<div
className={`
next-countdown ${isNegative ? ' next-countdown--overtime' : ''} ${isPaused ? ' next-countdown--paused' : ''}
`}
>
{isNegative ? '-' : ''}
{formatDisplay(time.current)}
{timer}
</div>
<div className='clock-indicators'>
{activeIndicators.map((i) => (
@@ -122,7 +127,7 @@ export default function StudioClock(props: StudioClockProps) {
</div>
<ul className='schedule'>
{trimmedRundown.map((event) => {
const start = formatTime(event.timeStart + (event?.delay ?? 0));
const start = formatTime(event.timeStart + (event?.delay ?? 0), { format12: 'h:mm a', format24: 'HH:mm' });
const isSelected = event.id === selectedId;
const isNext = event.id === nextId;
const classes = `schedule__item schedule__item${isSelected ? '--now' : isNext ? '--next' : '--future'}`;
@@ -1,6 +1,6 @@
import { OntimeEvent } from 'ontime-types';
import { trimRundown } from '../studioClock.utils';
import { secondsInMillis, trimRundown } from '../studioClock.utils';
describe('test trimEventlist function', () => {
const limit = 8;
@@ -118,3 +118,14 @@ describe('test trimEventlist function', () => {
expect(l).toStrictEqual(expected);
});
});
describe('secondsInMillis()', () => {
it('return 0 if value is null', () => {
expect(secondsInMillis(null)).toBe(0);
});
it('returns the seconds value of a millis date', () => {
const date = 1686255053619; // Thu Jun 08 2023 20:10:53
const seconds = secondsInMillis(date);
expect(seconds).toBe(53);
});
});
@@ -1,4 +1,5 @@
import { OntimeEvent } from 'ontime-types';
import { MaybeNumber, OntimeEvent } from 'ontime-types';
import { MILLIS_PER_MINUTE, MILLIS_PER_SECOND } from 'ontime-utils';
/**
* @description Returns trimmed event list array
@@ -19,3 +20,15 @@ export function trimRundown(rundown: OntimeEvent[], selectedId: string | null, l
const trimmedRundown = rundown.slice(startIndex, endIndex);
return trimmedRundown;
}
/**
* @description Returns amount of seconds in a date given in milliseconds. For studio clock second indicator
* @param {MaybeNumber} millis time to format
* @returns amount of elapsed seconds
*/
export function secondsInMillis(millis: MaybeNumber): number {
if (!millis) {
return 0;
}
return Math.floor((millis % MILLIS_PER_MINUTE) / MILLIS_PER_SECOND);
}
@@ -2,6 +2,7 @@ import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import { AnimatePresence, motion } from 'framer-motion';
import { Message, OntimeEvent, Playback, Settings, TimerMessage, TimerType, ViewSettings } from 'ontime-types';
import { millisToString, removeLeadingZero, removeSeconds } from 'ontime-utils';
import { overrideStylesURL } from '../../../common/api/apiConstants';
import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar';
@@ -11,11 +12,11 @@ import { getTimerOptions } from '../../../common/components/view-params-editor/c
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { formatTime } from '../../../common/utils/time';
import { formatTime, getDefaultFormat } from '../../../common/utils/time';
import { isStringBoolean } from '../../../common/utils/viewUtils';
import { useTranslation } from '../../../translation/TranslationProvider';
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
import { getTimerByType, removePrependedZero } from '../common/viewerUtils';
import { getTimerByType } from '../common/viewerUtils';
import './Timer.scss';
@@ -85,10 +86,7 @@ export default function Timer(props: TimerProps) {
const hideClockSeconds = searchParams.get('hideClockSeconds');
userOptions.hideClockSeconds = isStringBoolean(hideClockSeconds);
const clock = formatTime(time.clock, {
showSeconds: !userOptions.hideClockSeconds,
format: 'hh:mm:ss a',
});
const clock = formatTime(time.clock);
const hideTimerSeconds = searchParams.get('hideTimerSeconds');
userOptions.hideTimerSeconds = isStringBoolean(hideTimerSeconds);
@@ -117,13 +115,17 @@ export default function Timer(props: TimerProps) {
: viewSettings.normalColor;
const stageTimer = getTimerByType(time);
let display = '-- : -- : --';
let display = millisToString(stageTimer, { fallback: '-- : -- : --' });
if (stageTimer !== null) {
display = formatTime(stageTimer, {
showSeconds: !userOptions.hideTimerSeconds,
format: 'hh:mm:ss a',
});
display = removePrependedZero(display);
if (hideTimerSeconds) {
display = removeSeconds(display);
}
display = removeLeadingZero(display);
// last unit rounds up in negative timers
const isNegative = stageTimer ?? 0 < 0;
if (isNegative && display === '0') {
display = '-1';
}
if (display.length < 3) {
display = `${display} ${getLocalizedString('common.minutes')}`;
}
@@ -140,7 +142,8 @@ export default function Timer(props: TimerProps) {
const timerContainerClasses = `timer-container ${showBlinking ? (showOverlay ? '' : 'blink') : ''}`;
const timerClasses = `timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''}`;
const timerOptions = getTimerOptions(settings?.timeFormat ?? '24');
const defaultFormat = getDefaultFormat(settings?.timeFormat);
const timerOptions = getTimerOptions(defaultFormat);
return (
<div className={showFinished ? `${baseClasses} stage-timer--finished` : baseClasses} data-testid='timer-view'>