mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-01 04:19:12 +00:00
refactor: extract and simplify logic
This commit is contained in:
committed by
Carlos Valente
parent
49cae647d6
commit
3f35b5e297
@@ -1,57 +1,42 @@
|
|||||||
import { useSearchParams } from 'react-router-dom';
|
import { AnimatePresence } from 'framer-motion';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
|
||||||
import {
|
import {
|
||||||
CustomFields,
|
CustomFields,
|
||||||
MessageState,
|
MessageState,
|
||||||
OntimeEvent,
|
OntimeEvent,
|
||||||
Playback,
|
|
||||||
ProjectData,
|
ProjectData,
|
||||||
Settings,
|
Settings,
|
||||||
SimpleTimerState,
|
SimpleTimerState,
|
||||||
TimerPhase,
|
|
||||||
TimerType,
|
|
||||||
ViewSettings,
|
ViewSettings,
|
||||||
} from 'ontime-types';
|
} from 'ontime-types';
|
||||||
|
|
||||||
import { FitText } from '../../common/components/fit-text/FitText';
|
import { FitText } from '../../common/components/fit-text/FitText';
|
||||||
import MultiPartProgressBar from '../../common/components/multi-part-progress-bar/MultiPartProgressBar';
|
import MultiPartProgressBar from '../../common/components/multi-part-progress-bar/MultiPartProgressBar';
|
||||||
import TitleCard from '../../common/components/title-card/TitleCard';
|
|
||||||
import ViewLogo from '../../common/components/view-logo/ViewLogo';
|
import ViewLogo from '../../common/components/view-logo/ViewLogo';
|
||||||
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
|
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
|
||||||
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
||||||
import { ViewExtendedTimer } from '../../common/models/TimeManager.type';
|
import { ViewExtendedTimer } from '../../common/models/TimeManager.type';
|
||||||
|
import { cx } from '../../common/utils/styleUtils';
|
||||||
import { formatTime, getDefaultFormat } from '../../common/utils/time';
|
import { formatTime, getDefaultFormat } from '../../common/utils/time';
|
||||||
import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime';
|
import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime';
|
||||||
import {
|
import { getFormattedTimer, getPropertyValue, getTimerByType } from '../../features/viewers/common/viewUtils';
|
||||||
getFormattedTimer,
|
|
||||||
getPropertyValue,
|
|
||||||
getTimerByType,
|
|
||||||
isStringBoolean,
|
|
||||||
} from '../../features/viewers/common/viewUtils';
|
|
||||||
import { useTranslation } from '../../translation/TranslationProvider';
|
import { useTranslation } from '../../translation/TranslationProvider';
|
||||||
|
|
||||||
import { getTimerOptions } from './timer.options';
|
import { MotionTitleCard, titleVariants } from './timer.animations';
|
||||||
|
import { getTimerOptions, useTimerOptions } from './timer.options';
|
||||||
|
import {
|
||||||
|
getEstimatedFontSize,
|
||||||
|
getIsPlaying,
|
||||||
|
getSecondaryDisplay,
|
||||||
|
getShowClock,
|
||||||
|
getShowMessage,
|
||||||
|
getShowModifiers,
|
||||||
|
getShowProgressBar,
|
||||||
|
getTimerColour,
|
||||||
|
getTotalTime,
|
||||||
|
} from './timer.utils';
|
||||||
|
|
||||||
import './Timer.scss';
|
import './Timer.scss';
|
||||||
|
|
||||||
// motion
|
|
||||||
const titleVariants = {
|
|
||||||
hidden: {
|
|
||||||
x: -2500,
|
|
||||||
},
|
|
||||||
visible: {
|
|
||||||
x: 0,
|
|
||||||
transition: {
|
|
||||||
duration: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
exit: {
|
|
||||||
x: -2500,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const MotionTitleCard = motion(TitleCard);
|
|
||||||
|
|
||||||
interface TimerProps {
|
interface TimerProps {
|
||||||
auxTimer: SimpleTimerState;
|
auxTimer: SimpleTimerState;
|
||||||
customFields: CustomFields;
|
customFields: CustomFields;
|
||||||
@@ -69,170 +54,126 @@ export default function Timer(props: TimerProps) {
|
|||||||
const { auxTimer, customFields, eventNow, eventNext, general, isMirrored, message, settings, time, viewSettings } =
|
const { auxTimer, customFields, eventNow, eventNext, general, isMirrored, message, settings, time, viewSettings } =
|
||||||
props;
|
props;
|
||||||
|
|
||||||
|
const {
|
||||||
|
hideClock,
|
||||||
|
hideCards,
|
||||||
|
hideProgress,
|
||||||
|
hideMessage,
|
||||||
|
hideTimerSeconds,
|
||||||
|
removeLeadingZeros,
|
||||||
|
mainSource,
|
||||||
|
secondarySource,
|
||||||
|
} = useTimerOptions();
|
||||||
|
|
||||||
const { getLocalizedString } = useTranslation();
|
const { getLocalizedString } = useTranslation();
|
||||||
const [searchParams] = useSearchParams();
|
|
||||||
|
|
||||||
useWindowTitle('Timer');
|
useWindowTitle('Timer');
|
||||||
|
|
||||||
// USER OPTIONS
|
// gather modifiers
|
||||||
const userOptions = {
|
const showOverlay = getShowMessage(message.timer);
|
||||||
hideClock: false,
|
const { showEndMessage, showFinished, showWarning, showDanger } = getShowModifiers(
|
||||||
hideCards: false,
|
time.timerType,
|
||||||
hideProgress: false,
|
time.countToEnd,
|
||||||
hideMessage: false,
|
time.phase,
|
||||||
hideTimerSeconds: false,
|
viewSettings,
|
||||||
hideClockSeconds: false,
|
);
|
||||||
removeLeadingZeros: true,
|
const isPlaying = getIsPlaying(time.playback);
|
||||||
};
|
const showClock = !hideClock && getShowClock(time.timerType);
|
||||||
|
const showProgressBar = !hideProgress && getShowProgressBar(time.timerType);
|
||||||
|
|
||||||
const hideClock = searchParams.get('hideClock');
|
// gather card data
|
||||||
userOptions.hideClock = isStringBoolean(hideClock);
|
const mainFieldNow = getPropertyValue(eventNow, mainSource) ?? eventNow?.title ?? '';
|
||||||
|
const mainFieldNext = getPropertyValue(eventNext, mainSource) ?? eventNext?.title ?? '';
|
||||||
const hideCards = searchParams.get('hideCards');
|
|
||||||
userOptions.hideCards = isStringBoolean(hideCards);
|
|
||||||
|
|
||||||
const hideProgress = searchParams.get('hideProgress');
|
|
||||||
userOptions.hideProgress = isStringBoolean(hideProgress);
|
|
||||||
|
|
||||||
const hideMessage = searchParams.get('hideMessage');
|
|
||||||
userOptions.hideMessage = isStringBoolean(hideMessage);
|
|
||||||
|
|
||||||
const hideClockSeconds = searchParams.get('hideClockSeconds');
|
|
||||||
userOptions.hideClockSeconds = isStringBoolean(hideClockSeconds);
|
|
||||||
const clock = formatTime(time.clock);
|
|
||||||
|
|
||||||
const hideTimerSeconds = searchParams.get('hideTimerSeconds');
|
|
||||||
userOptions.hideTimerSeconds = isStringBoolean(hideTimerSeconds);
|
|
||||||
|
|
||||||
const showLeadingZeros = searchParams.get('showLeadingZeros');
|
|
||||||
userOptions.removeLeadingZeros = !isStringBoolean(showLeadingZeros);
|
|
||||||
|
|
||||||
const secondarySource = searchParams.get('secondary-src');
|
|
||||||
const secondaryTextNow = getPropertyValue(eventNow, secondarySource);
|
const secondaryTextNow = getPropertyValue(eventNow, secondarySource);
|
||||||
const secondaryTextNext = getPropertyValue(eventNext, secondarySource);
|
const secondaryTextNext = getPropertyValue(eventNext, secondarySource);
|
||||||
|
|
||||||
const main = searchParams.get('main');
|
// gather timer data
|
||||||
const mainFieldNow = (main ? getPropertyValue(eventNow, main) : eventNow?.title) ?? '';
|
const totalTime = getTotalTime(time.duration, time.addedTime);
|
||||||
const mainFieldNext = (main ? getPropertyValue(eventNext, main) : eventNext?.title) ?? '';
|
const clock = formatTime(time.clock);
|
||||||
|
|
||||||
const showOverlay = message.timer.text !== '' && message.timer.visible;
|
|
||||||
const isPlaying = time.playback !== Playback.Pause;
|
|
||||||
|
|
||||||
const timerIsTimeOfDay = time.timerType === TimerType.Clock;
|
|
||||||
|
|
||||||
const finished = time.phase === TimerPhase.Overtime;
|
|
||||||
const totalTime = (time.duration ?? 0) + (time.addedTime ?? 0);
|
|
||||||
|
|
||||||
const shouldShowModifiers = time.timerType === TimerType.CountDown || time.countToEnd;
|
|
||||||
const showEndMessage = shouldShowModifiers && finished && viewSettings.endMessage;
|
|
||||||
const showProgress =
|
|
||||||
eventNow !== null &&
|
|
||||||
time.timerType !== TimerType.None &&
|
|
||||||
time.timerType !== TimerType.Clock &&
|
|
||||||
time.playback !== Playback.Stop;
|
|
||||||
const showFinished = shouldShowModifiers && finished && (shouldShowModifiers || showEndMessage);
|
|
||||||
const showWarning = shouldShowModifiers && time.phase === TimerPhase.Warning;
|
|
||||||
const showDanger = shouldShowModifiers && time.phase === TimerPhase.Danger;
|
|
||||||
const showClock = time.timerType !== TimerType.Clock;
|
|
||||||
|
|
||||||
const secondaryContent = ((): string | undefined => {
|
|
||||||
if (message.timer.secondarySource === 'aux') {
|
|
||||||
return getFormattedTimer(auxTimer.current, TimerType.CountDown, getLocalizedString('common.minutes'), {
|
|
||||||
removeSeconds: userOptions.hideTimerSeconds,
|
|
||||||
removeLeadingZero: userOptions.removeLeadingZeros,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (message.timer.secondarySource === 'external' && message.external) {
|
|
||||||
return message.external;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
})();
|
|
||||||
|
|
||||||
let timerColor = viewSettings.normalColor;
|
|
||||||
if (!timerIsTimeOfDay && showProgress && showWarning) timerColor = viewSettings.warningColor;
|
|
||||||
if (!timerIsTimeOfDay && showProgress && showDanger) timerColor = viewSettings.dangerColor;
|
|
||||||
|
|
||||||
const stageTimer = getTimerByType(viewSettings.freezeEnd, time);
|
const stageTimer = getTimerByType(viewSettings.freezeEnd, time);
|
||||||
const display = getFormattedTimer(stageTimer, time.timerType, getLocalizedString('common.minutes'), {
|
const display = getFormattedTimer(stageTimer, time.timerType, getLocalizedString('common.minutes'), {
|
||||||
removeSeconds: userOptions.hideTimerSeconds,
|
removeSeconds: hideTimerSeconds,
|
||||||
removeLeadingZero: userOptions.removeLeadingZeros,
|
removeLeadingZero: removeLeadingZeros,
|
||||||
});
|
});
|
||||||
|
|
||||||
const stageTimerCharacters = display.replace('/:/g', '').length;
|
const secondaryContent = getSecondaryDisplay(
|
||||||
|
message,
|
||||||
|
auxTimer.current,
|
||||||
|
getLocalizedString('common.minutes'),
|
||||||
|
hideTimerSeconds,
|
||||||
|
removeLeadingZeros,
|
||||||
|
);
|
||||||
|
|
||||||
const baseClasses = `stage-timer ${isMirrored ? 'mirror' : ''}`;
|
// gather presentation styles
|
||||||
|
const timerColour = getTimerColour(viewSettings, showWarning, showDanger);
|
||||||
let timerFontSize = 89 / (stageTimerCharacters - 1);
|
const { timerFontSize, externalFontSize } = getEstimatedFontSize(display, Boolean(secondaryContent));
|
||||||
// we need to shrink the timer if the external is going to be there
|
|
||||||
if (secondaryContent) {
|
|
||||||
timerFontSize *= 0.8;
|
|
||||||
}
|
|
||||||
const externalFontSize = timerFontSize * 0.4;
|
|
||||||
const timerContainerClasses = `timer-container ${message.timer.blink ? (showOverlay ? '' : 'blink') : ''}`;
|
|
||||||
const timerClasses = `timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''}`;
|
|
||||||
|
|
||||||
|
// gather option data
|
||||||
const defaultFormat = getDefaultFormat(settings?.timeFormat);
|
const defaultFormat = getDefaultFormat(settings?.timeFormat);
|
||||||
const timerOptions = getTimerOptions(defaultFormat, customFields);
|
const timerOptions = getTimerOptions(defaultFormat, customFields);
|
||||||
const disableProgress = timerIsTimeOfDay || time.timerType === TimerType.None;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={showFinished ? `${baseClasses} stage-timer--finished` : baseClasses} data-testid='timer-view'>
|
<div
|
||||||
|
className={cx(['stage-timer', isMirrored && 'mirror', showFinished && 'stage-timer--finished'])}
|
||||||
|
data-testid='timer-view'
|
||||||
|
>
|
||||||
{general?.projectLogo && <ViewLogo name={general.projectLogo} className='logo' />}
|
{general?.projectLogo && <ViewLogo name={general.projectLogo} className='logo' />}
|
||||||
|
|
||||||
<ViewParamsEditor viewOptions={timerOptions} />
|
<ViewParamsEditor viewOptions={timerOptions} />
|
||||||
<div className={message.timer.blackout ? 'blackout blackout--active' : 'blackout'} />
|
|
||||||
{!userOptions.hideMessage && (
|
<div className={cx(['blackout', message.timer.blackout && 'blackout--active'])} />
|
||||||
<div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}>
|
|
||||||
<FitText mode='multi' min={32} max={256} className={`message ${message.timer.blink ? 'blink' : ''}`}>
|
{!hideMessage && (
|
||||||
|
<div className={cx(['message-overlay', showOverlay && ' message-overlay--active'])}>
|
||||||
|
<FitText mode='multi' min={32} max={256} className={cx(['message', message.timer.blink && 'blink'])}>
|
||||||
{message.timer.text}
|
{message.timer.text}
|
||||||
</FitText>
|
</FitText>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!userOptions.hideClock && (
|
{showClock && (
|
||||||
<div className={`clock-container ${showClock ? '' : 'clock-container--hidden'}`}>
|
<div className='clock-container'>
|
||||||
<div className='label'>{getLocalizedString('common.time_now')}</div>
|
<div className='label'>{getLocalizedString('common.time_now')}</div>
|
||||||
<SuperscriptTime time={clock} className='clock' />
|
<SuperscriptTime time={clock} className='clock' />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={timerContainerClasses}>
|
<div className={cx(['timer-container', message.timer.blink && !showOverlay && 'blink'])}>
|
||||||
{showEndMessage ? (
|
{showEndMessage ? (
|
||||||
<div className='end-message'>{viewSettings.endMessage}</div>
|
<div className='end-message'>{viewSettings.endMessage}</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className={timerClasses}
|
className={cx(['timer', !isPlaying && 'timer--paused', showFinished && 'timer--finished'])}
|
||||||
style={{
|
style={{
|
||||||
fontSize: `${timerFontSize}vw`,
|
fontSize: `${timerFontSize}vw`,
|
||||||
'--phase-color': timerColor,
|
'--phase-color': timerColour,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{display}
|
{display}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className={`secondary${secondaryContent ? '' : ' secondary--hidden'}`}
|
className={cx(['secondary', !secondaryContent && 'secondary--hidden'])}
|
||||||
style={{ fontSize: `${externalFontSize}vw` }}
|
style={{ fontSize: `${externalFontSize}vw` }}
|
||||||
>
|
>
|
||||||
{secondaryContent}
|
{secondaryContent}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!userOptions.hideProgress && (
|
{showProgressBar && (
|
||||||
<MultiPartProgressBar
|
<MultiPartProgressBar
|
||||||
className={isPlaying ? 'progress-container' : 'progress-container progress-container--paused'}
|
className={cx(['progress-container', !isPlaying && 'progress-container--paused'])}
|
||||||
now={disableProgress ? null : time.current}
|
now={time.current}
|
||||||
complete={totalTime}
|
complete={totalTime}
|
||||||
normalColor={viewSettings.normalColor}
|
normalColor={viewSettings.normalColor}
|
||||||
warning={eventNow?.timeWarning}
|
warning={eventNow?.timeWarning}
|
||||||
warningColor={viewSettings.warningColor}
|
warningColor={viewSettings.warningColor}
|
||||||
danger={eventNow?.timeDanger}
|
danger={eventNow?.timeDanger}
|
||||||
dangerColor={viewSettings.dangerColor}
|
dangerColor={viewSettings.dangerColor}
|
||||||
hidden={!showProgress}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!userOptions.hideCards && (
|
{!hideCards && (
|
||||||
<>
|
<>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{eventNow?.title && (
|
{eventNow?.title && (
|
||||||
@@ -253,6 +194,7 @@ export default function Timer(props: TimerProps) {
|
|||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{eventNext?.title && (
|
{eventNext?.title && (
|
||||||
<MotionTitleCard
|
<MotionTitleCard
|
||||||
|
className='event next'
|
||||||
key='next'
|
key='next'
|
||||||
variants={titleVariants}
|
variants={titleVariants}
|
||||||
initial='hidden'
|
initial='hidden'
|
||||||
@@ -261,7 +203,6 @@ export default function Timer(props: TimerProps) {
|
|||||||
label='next'
|
label='next'
|
||||||
title={mainFieldNext}
|
title={mainFieldNext}
|
||||||
secondary={secondaryTextNext}
|
secondary={secondaryTextNext}
|
||||||
className='event next'
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
|
import TitleCard from '../../common/components/title-card/TitleCard';
|
||||||
|
|
||||||
|
export const titleVariants = {
|
||||||
|
hidden: {
|
||||||
|
x: -2500,
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
x: 0,
|
||||||
|
transition: {
|
||||||
|
duration: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
x: -2500,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MotionTitleCard = motion(TitleCard);
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
import { CustomFields } from 'ontime-types';
|
import { useMemo } from 'react';
|
||||||
|
import { useSearchParams } from 'react-router-dom';
|
||||||
|
import { CustomFields, OntimeEvent } from 'ontime-types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getTimeOption,
|
getTimeOption,
|
||||||
@@ -7,6 +9,7 @@ import {
|
|||||||
showLeadingZeros,
|
showLeadingZeros,
|
||||||
} from '../../common/components/view-params-editor/constants';
|
} from '../../common/components/view-params-editor/constants';
|
||||||
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';
|
||||||
|
|
||||||
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' });
|
||||||
@@ -59,17 +62,58 @@ export const getTimerOptions = (timeFormat: string, customFields: CustomFields):
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'hideMessage',
|
id: 'hideMessage',
|
||||||
title: 'Hide Presenter Message',
|
title: 'Hide Timer Message',
|
||||||
description: 'Prevents the screen from displaying messages from the presenter',
|
description: 'Prevents displaying fullscreen messages in the timer',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'hideExternal',
|
id: 'hideExternal',
|
||||||
title: 'Hide External',
|
title: 'Hide Auxiliary timer / External message',
|
||||||
description: 'Prevents the screen from displaying the external field',
|
description: 'Prevents the screen from displaying the secondary timer field',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type TimerOptions = {
|
||||||
|
hideClock: boolean;
|
||||||
|
hideCards: boolean;
|
||||||
|
hideProgress: boolean;
|
||||||
|
hideMessage: boolean;
|
||||||
|
hideExternal: boolean;
|
||||||
|
hideTimerSeconds: boolean;
|
||||||
|
removeLeadingZeros: boolean;
|
||||||
|
mainSource: keyof OntimeEvent | null;
|
||||||
|
secondarySource: keyof OntimeEvent | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility extract the view options from URL Params
|
||||||
|
* the names and fallbacks are manually matched with timerOptions
|
||||||
|
*/
|
||||||
|
function getOptionsFromParams(searchParams: URLSearchParams): TimerOptions {
|
||||||
|
// we manually make an object that matches the key above
|
||||||
|
return {
|
||||||
|
hideClock: isStringBoolean(searchParams.get('hideClock')),
|
||||||
|
hideCards: isStringBoolean(searchParams.get('hideCards')),
|
||||||
|
hideProgress: isStringBoolean(searchParams.get('hideProgress')),
|
||||||
|
hideMessage: isStringBoolean(searchParams.get('hideMessage')),
|
||||||
|
hideExternal: isStringBoolean(searchParams.get('hideExternal')),
|
||||||
|
hideTimerSeconds: isStringBoolean(searchParams.get('hideTimerSeconds')),
|
||||||
|
removeLeadingZeros: !isStringBoolean(searchParams.get('showLeadingZeros')),
|
||||||
|
|
||||||
|
mainSource: searchParams.get('main') as keyof OntimeEvent | null,
|
||||||
|
secondarySource: searchParams.get('secondary-src') as keyof OntimeEvent | null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook exposes the timer view options
|
||||||
|
*/
|
||||||
|
export function useTimerOptions(): TimerOptions {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const options = useMemo(() => getOptionsFromParams(searchParams), [searchParams]);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
import { MaybeNumber, MessageState, Playback, TimerMessage, TimerPhase, TimerType, ViewSettings } from 'ontime-types';
|
||||||
|
|
||||||
|
import { getFormattedTimer } from '../../features/viewers/common/viewUtils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a message should be shown
|
||||||
|
*/
|
||||||
|
export function getShowMessage(message: TimerMessage): boolean {
|
||||||
|
return message.text !== '' && message.visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the playback is playing
|
||||||
|
*/
|
||||||
|
export function getIsPlaying(playback: Playback): boolean {
|
||||||
|
return playback === Playback.Play || playback === Playback.Roll;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the total time from the duration and added time of an event
|
||||||
|
*/
|
||||||
|
export function getTotalTime(duration: MaybeNumber, addedTime: MaybeNumber): number {
|
||||||
|
return (duration ?? 0) + (addedTime ?? 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the progress bar should be shown for this timer type
|
||||||
|
*/
|
||||||
|
export function getShowProgressBar(timerType: TimerType) {
|
||||||
|
return timerType !== TimerType.None && timerType !== TimerType.Clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the clock should be shown with this timer type
|
||||||
|
*/
|
||||||
|
export function getShowClock(timerType: TimerType) {
|
||||||
|
return timerType !== TimerType.Clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fontSizeMap: { [key: number]: number } = {
|
||||||
|
4: 28, // 9:01
|
||||||
|
5: 28, // -9:01, 10:01, 9 min
|
||||||
|
6: 25, // -10:01, 10 min
|
||||||
|
8: 20, // 23:01:01
|
||||||
|
9: 20, // -23:01:01
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds a font size that fits the timer in the screen
|
||||||
|
* Unfortunately hand tweaked
|
||||||
|
*/
|
||||||
|
export function getEstimatedFontSize(stageTimer: string, secondaryContent?: string) {
|
||||||
|
const stageTimerCharacters = stageTimer.length;
|
||||||
|
let timerFontSize = (100 / (stageTimerCharacters - 1)) * 1.25;
|
||||||
|
|
||||||
|
if (fontSizeMap[stageTimerCharacters]) {
|
||||||
|
timerFontSize = fontSizeMap[stageTimerCharacters];
|
||||||
|
}
|
||||||
|
|
||||||
|
let externalFontSize = timerFontSize * 0.2;
|
||||||
|
if (secondaryContent) {
|
||||||
|
// we need to shrink the timer if the external is going to be there
|
||||||
|
// this number has been tweaked to fit in a landscape mobile screen
|
||||||
|
timerFontSize *= 0.6;
|
||||||
|
if (secondaryContent.length > 25) {
|
||||||
|
externalFontSize = (100 / (secondaryContent.length - 1)) * 1.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
timerFontSize,
|
||||||
|
externalFontSize,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* which, if any, modifier should be shown at any time
|
||||||
|
*/
|
||||||
|
export function getShowModifiers(
|
||||||
|
timerType: TimerType,
|
||||||
|
countToEnd: boolean,
|
||||||
|
phase: TimerPhase,
|
||||||
|
viewSettings: ViewSettings,
|
||||||
|
) {
|
||||||
|
const showModifiers = timerType === TimerType.CountDown || countToEnd;
|
||||||
|
const finished = phase === TimerPhase.Overtime;
|
||||||
|
return {
|
||||||
|
showEndMessage: showModifiers && finished && viewSettings.endMessage,
|
||||||
|
showFinished: showModifiers && finished, // ????
|
||||||
|
showWarning: showModifiers && phase === TimerPhase.Warning,
|
||||||
|
showDanger: showModifiers && phase === TimerPhase.Danger,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which colour should the timer have at a given moment
|
||||||
|
*/
|
||||||
|
export function getTimerColour(viewSettings: ViewSettings, showWarning: boolean, showDanger: boolean) {
|
||||||
|
if (showWarning) return viewSettings.warningColor;
|
||||||
|
if (showDanger) return viewSettings.dangerColor;
|
||||||
|
return viewSettings.normalColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What, if anything, should be displayed in the secondary field
|
||||||
|
*/
|
||||||
|
export function getSecondaryDisplay(
|
||||||
|
message: MessageState,
|
||||||
|
currentAux: MaybeNumber,
|
||||||
|
localisedMinutes: string,
|
||||||
|
removeSeconds: boolean,
|
||||||
|
removeLeadingZero: boolean,
|
||||||
|
hideExternal: boolean,
|
||||||
|
): string | undefined {
|
||||||
|
if (hideExternal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message.timer.secondarySource === 'aux') {
|
||||||
|
return getFormattedTimer(currentAux, TimerType.CountDown, localisedMinutes, {
|
||||||
|
removeSeconds,
|
||||||
|
removeLeadingZero,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (message.timer.secondarySource === 'external' && message.external) {
|
||||||
|
return message.external;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user