mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-19 22:24:11 +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, motion } from 'framer-motion';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import {
|
||||
CustomFields,
|
||||
MessageState,
|
||||
OntimeEvent,
|
||||
Playback,
|
||||
ProjectData,
|
||||
Settings,
|
||||
SimpleTimerState,
|
||||
TimerPhase,
|
||||
TimerType,
|
||||
ViewSettings,
|
||||
} from 'ontime-types';
|
||||
|
||||
import { FitText } from '../../common/components/fit-text/FitText';
|
||||
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 ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
|
||||
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
||||
import { ViewExtendedTimer } from '../../common/models/TimeManager.type';
|
||||
import { cx } from '../../common/utils/styleUtils';
|
||||
import { formatTime, getDefaultFormat } from '../../common/utils/time';
|
||||
import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime';
|
||||
import {
|
||||
getFormattedTimer,
|
||||
getPropertyValue,
|
||||
getTimerByType,
|
||||
isStringBoolean,
|
||||
} from '../../features/viewers/common/viewUtils';
|
||||
import { getFormattedTimer, getPropertyValue, getTimerByType } from '../../features/viewers/common/viewUtils';
|
||||
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';
|
||||
|
||||
// motion
|
||||
const titleVariants = {
|
||||
hidden: {
|
||||
x: -2500,
|
||||
},
|
||||
visible: {
|
||||
x: 0,
|
||||
transition: {
|
||||
duration: 1,
|
||||
},
|
||||
},
|
||||
exit: {
|
||||
x: -2500,
|
||||
},
|
||||
};
|
||||
|
||||
export const MotionTitleCard = motion(TitleCard);
|
||||
|
||||
interface TimerProps {
|
||||
auxTimer: SimpleTimerState;
|
||||
customFields: CustomFields;
|
||||
@@ -69,170 +54,126 @@ export default function Timer(props: TimerProps) {
|
||||
const { auxTimer, customFields, eventNow, eventNext, general, isMirrored, message, settings, time, viewSettings } =
|
||||
props;
|
||||
|
||||
const {
|
||||
hideClock,
|
||||
hideCards,
|
||||
hideProgress,
|
||||
hideMessage,
|
||||
hideTimerSeconds,
|
||||
removeLeadingZeros,
|
||||
mainSource,
|
||||
secondarySource,
|
||||
} = useTimerOptions();
|
||||
|
||||
const { getLocalizedString } = useTranslation();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
useWindowTitle('Timer');
|
||||
|
||||
// USER OPTIONS
|
||||
const userOptions = {
|
||||
hideClock: false,
|
||||
hideCards: false,
|
||||
hideProgress: false,
|
||||
hideMessage: false,
|
||||
hideTimerSeconds: false,
|
||||
hideClockSeconds: false,
|
||||
removeLeadingZeros: true,
|
||||
};
|
||||
// gather modifiers
|
||||
const showOverlay = getShowMessage(message.timer);
|
||||
const { showEndMessage, showFinished, showWarning, showDanger } = getShowModifiers(
|
||||
time.timerType,
|
||||
time.countToEnd,
|
||||
time.phase,
|
||||
viewSettings,
|
||||
);
|
||||
const isPlaying = getIsPlaying(time.playback);
|
||||
const showClock = !hideClock && getShowClock(time.timerType);
|
||||
const showProgressBar = !hideProgress && getShowProgressBar(time.timerType);
|
||||
|
||||
const hideClock = searchParams.get('hideClock');
|
||||
userOptions.hideClock = isStringBoolean(hideClock);
|
||||
|
||||
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');
|
||||
// gather card data
|
||||
const mainFieldNow = getPropertyValue(eventNow, mainSource) ?? eventNow?.title ?? '';
|
||||
const mainFieldNext = getPropertyValue(eventNext, mainSource) ?? eventNext?.title ?? '';
|
||||
const secondaryTextNow = getPropertyValue(eventNow, secondarySource);
|
||||
const secondaryTextNext = getPropertyValue(eventNext, secondarySource);
|
||||
|
||||
const main = searchParams.get('main');
|
||||
const mainFieldNow = (main ? getPropertyValue(eventNow, main) : eventNow?.title) ?? '';
|
||||
const mainFieldNext = (main ? getPropertyValue(eventNext, main) : eventNext?.title) ?? '';
|
||||
|
||||
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;
|
||||
|
||||
// gather timer data
|
||||
const totalTime = getTotalTime(time.duration, time.addedTime);
|
||||
const clock = formatTime(time.clock);
|
||||
const stageTimer = getTimerByType(viewSettings.freezeEnd, time);
|
||||
const display = getFormattedTimer(stageTimer, time.timerType, getLocalizedString('common.minutes'), {
|
||||
removeSeconds: userOptions.hideTimerSeconds,
|
||||
removeLeadingZero: userOptions.removeLeadingZeros,
|
||||
removeSeconds: hideTimerSeconds,
|
||||
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' : ''}`;
|
||||
|
||||
let timerFontSize = 89 / (stageTimerCharacters - 1);
|
||||
// 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 presentation styles
|
||||
const timerColour = getTimerColour(viewSettings, showWarning, showDanger);
|
||||
const { timerFontSize, externalFontSize } = getEstimatedFontSize(display, Boolean(secondaryContent));
|
||||
|
||||
// gather option data
|
||||
const defaultFormat = getDefaultFormat(settings?.timeFormat);
|
||||
const timerOptions = getTimerOptions(defaultFormat, customFields);
|
||||
const disableProgress = timerIsTimeOfDay || time.timerType === TimerType.None;
|
||||
|
||||
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' />}
|
||||
|
||||
<ViewParamsEditor viewOptions={timerOptions} />
|
||||
<div className={message.timer.blackout ? 'blackout blackout--active' : 'blackout'} />
|
||||
{!userOptions.hideMessage && (
|
||||
<div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}>
|
||||
<FitText mode='multi' min={32} max={256} className={`message ${message.timer.blink ? 'blink' : ''}`}>
|
||||
|
||||
<div className={cx(['blackout', message.timer.blackout && 'blackout--active'])} />
|
||||
|
||||
{!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}
|
||||
</FitText>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!userOptions.hideClock && (
|
||||
<div className={`clock-container ${showClock ? '' : 'clock-container--hidden'}`}>
|
||||
{showClock && (
|
||||
<div className='clock-container'>
|
||||
<div className='label'>{getLocalizedString('common.time_now')}</div>
|
||||
<SuperscriptTime time={clock} className='clock' />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={timerContainerClasses}>
|
||||
<div className={cx(['timer-container', message.timer.blink && !showOverlay && 'blink'])}>
|
||||
{showEndMessage ? (
|
||||
<div className='end-message'>{viewSettings.endMessage}</div>
|
||||
) : (
|
||||
<div
|
||||
className={timerClasses}
|
||||
className={cx(['timer', !isPlaying && 'timer--paused', showFinished && 'timer--finished'])}
|
||||
style={{
|
||||
fontSize: `${timerFontSize}vw`,
|
||||
'--phase-color': timerColor,
|
||||
'--phase-color': timerColour,
|
||||
}}
|
||||
>
|
||||
{display}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={`secondary${secondaryContent ? '' : ' secondary--hidden'}`}
|
||||
className={cx(['secondary', !secondaryContent && 'secondary--hidden'])}
|
||||
style={{ fontSize: `${externalFontSize}vw` }}
|
||||
>
|
||||
{secondaryContent}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!userOptions.hideProgress && (
|
||||
{showProgressBar && (
|
||||
<MultiPartProgressBar
|
||||
className={isPlaying ? 'progress-container' : 'progress-container progress-container--paused'}
|
||||
now={disableProgress ? null : time.current}
|
||||
className={cx(['progress-container', !isPlaying && 'progress-container--paused'])}
|
||||
now={time.current}
|
||||
complete={totalTime}
|
||||
normalColor={viewSettings.normalColor}
|
||||
warning={eventNow?.timeWarning}
|
||||
warningColor={viewSettings.warningColor}
|
||||
danger={eventNow?.timeDanger}
|
||||
dangerColor={viewSettings.dangerColor}
|
||||
hidden={!showProgress}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!userOptions.hideCards && (
|
||||
{!hideCards && (
|
||||
<>
|
||||
<AnimatePresence>
|
||||
{eventNow?.title && (
|
||||
@@ -253,6 +194,7 @@ export default function Timer(props: TimerProps) {
|
||||
<AnimatePresence>
|
||||
{eventNext?.title && (
|
||||
<MotionTitleCard
|
||||
className='event next'
|
||||
key='next'
|
||||
variants={titleVariants}
|
||||
initial='hidden'
|
||||
@@ -261,7 +203,6 @@ export default function Timer(props: TimerProps) {
|
||||
label='next'
|
||||
title={mainFieldNext}
|
||||
secondary={secondaryTextNext}
|
||||
className='event next'
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
Reference in New Issue
Block a user