Files
ontime/apps/client/src/features/viewers/timer/Timer.tsx
T
Carlos Valente 5e481d49da V2 beta1 (#326)
* style: labels on added time

* style: remove mentions of PiP

* refactor: unify usage of ms for timers

* refactor: create events with 0 duration

* style: several small tweaks

* refactor: keep block when applying delays

* feat: blocks have titles

* style: improvements in time entry warnings

* style: override progress bar styles

* style: prevent overflow

* feat: show character count in editor

* refactor: provide initial payload

* refactor: lower test boundary

* refactor: get colour from swatches

* style: rename title block

* chore: version bump
2023-04-05 22:08:13 +02:00

155 lines
4.8 KiB
TypeScript

import { useEffect } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { EventData, Message, Playback, TimerType, ViewSettings } from 'ontime-types';
import { overrideStylesURL } from '../../../common/api/apiConstants';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import ProgressBar from '../../../common/components/progress-bar/ProgressBar';
import TitleCard from '../../../common/components/title-card/TitleCard';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { formatTime } from '../../../common/utils/time';
import { useTranslation } from '../../../translation/TranslationProvider';
import { formatTimerDisplay, getTimerByType } from '../common/viewerUtils';
import { TitleManager } from '../ViewWrapper';
import './Timer.scss';
const formatOptions = {
showSeconds: true,
format: 'hh:mm:ss a',
};
// motion
const titleVariants = {
hidden: {
y: 500,
},
visible: {
y: 0,
transition: {
duration: 1,
},
},
exit: {
y: 500,
},
};
interface TimerProps {
isMirrored: boolean;
general: EventData;
pres: Message;
title: TitleManager;
time: TimeManagerType;
viewSettings: ViewSettings;
}
export default function Timer(props: TimerProps) {
const { isMirrored, general, pres, title, time, viewSettings } = props;
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
const { getLocalizedString } = useTranslation();
useEffect(() => {
document.title = 'ontime - Timer';
}, []);
// defer rendering until we load stylesheets
if (!shouldRender) {
return null;
}
const clock = formatTime(time.clock, formatOptions);
const showOverlay = pres.text !== '' && pres.visible;
const isPlaying = time.playback !== Playback.Pause;
const isNegative =
(time.current ?? 0) < 0 && time.timerType !== TimerType.Clock && time.timerType !== TimerType.CountUp;
const showEndMessage = (time.current ?? 1) < 0 && general.endMessage;
const showProgress = time.playback !== Playback.Stop;
const showFinished = time.finished && (time.timerType !== TimerType.Clock || showEndMessage);
const showClock = time.timerType !== TimerType.Clock;
const baseClasses = `stage-timer ${isMirrored ? 'mirror' : ''}`;
const stageTimer = getTimerByType(time);
let display = formatTimerDisplay(stageTimer);
const stageTimerCharacters = display.replace('/:/g', '').length;
if (isNegative) {
display = `-${display}`;
}
const timerFontSize = 89 / (stageTimerCharacters - 1);
const timerClasses = `timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''}`;
return (
<div className={showFinished ? `${baseClasses} stage-timer--finished` : baseClasses} data-testid='timer-view'>
<NavigationMenu />
<div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}>
<div className='message'>{pres.text}</div>
</div>
<div className={`clock-container ${showClock ? '' : 'clock-container--hidden'}`}>
<div className='label'>{getLocalizedString('common.time_now')}</div>
<div className='clock'>{clock}</div>
</div>
<div className='timer-container'>
{showEndMessage ? (
<div className='end-message'>{general.endMessage}</div>
) : (
<div
className={timerClasses}
style={{
fontSize: `${timerFontSize}vw`,
}}
>
{display}
</div>
)}
</div>
<ProgressBar
className={isPlaying ? 'progress-container' : 'progress-container progress-container--paused'}
now={time.current || 0}
complete={time.duration || 0}
hidden={!showProgress}
/>
<AnimatePresence>
{title.showNow && !time.finished && (
<motion.div
className='event now'
key='now'
variants={titleVariants}
initial='hidden'
animate='visible'
exit='exit'
>
<TitleCard label='now' title={title.titleNow} subtitle={title.subtitleNow} presenter={title.presenterNow} />
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
{title.showNext && (
<motion.div
className='event next'
key='next'
variants={titleVariants}
initial='hidden'
animate='visible'
exit='exit'
>
<TitleCard
label='next'
title={title.titleNext}
subtitle={title.subtitleNext}
presenter={title.presenterNext}
/>
</motion.div>
)}
</AnimatePresence>
</div>
);
}