Timer/blinking and blackout buttons (#406)

*feat: timer blink

*feat: blackout

---------

Co-authored-by: Fabian Posenau <fabian@fphome.de>
Co-authored-by: cv <34649812+cpvalente@users.noreply.github.com>
This commit is contained in:
Fabian Posenau
2023-06-04 21:25:56 +02:00
committed by GitHub
parent 07737cc8b3
commit 5cb8020ab9
24 changed files with 196 additions and 66 deletions
@@ -3,6 +3,7 @@ import { IoEye } from '@react-icons/all-files/io5/IoEye';
import { IoEyeOffOutline } from '@react-icons/all-files/io5/IoEyeOffOutline';
import TooltipActionBtn from '../../../common/components/buttons/TooltipActionBtn';
import { cx } from '../../../common/utils/styleUtils';
import { tooltipDelayMid } from '../../../ontimeConfig';
import style from './InputRow.module.scss';
@@ -14,17 +15,19 @@ interface InputRowProps {
visible?: boolean;
actionHandler: (action: string, payload: object) => void;
changeHandler: (newValue: string) => void;
className?: string;
}
export default function InputRow(props: InputRowProps) {
const { label, placeholder, text, visible, actionHandler, changeHandler } = props;
const { label, placeholder, text, visible, actionHandler, changeHandler, className } = props;
const handleInputChange = (newValue: string) => {
changeHandler(newValue);
};
const classes = cx([style.inputRow, className]);
return (
<div className={style.inputRow}>
<div className={classes}>
<label className={`${style.label} ${visible ? style.active : ''}`}>{label}</label>
<div className={style.inputItems}>
<Input
@@ -39,7 +42,7 @@ export default function InputRow(props: InputRowProps) {
tooltip={visible ? 'Make invisible' : 'Make visible'}
aria-label={`Toggle ${label}`}
openDelay={tooltipDelayMid}
icon={visible? <IoEye size='18px' /> : <IoEyeOffOutline size='18px' />}
icon={visible ? <IoEye size='18px' /> : <IoEyeOffOutline size='18px' />}
variant={visible ? 'ontime-filled' : 'ontime-subtle'}
size='sm'
/>
@@ -7,12 +7,18 @@
}
.onAirSection {
margin-top: $section-spacing;
display: flex;
flex-direction: column;
gap: $element-spacing;
}
.buttonSection {
display: grid;
grid-template-columns: 1fr 1fr;
gap: $element-spacing;
margin-top: -0.5rem;
}
.label {
font-size: $inner-section-text-size;
color: $label-gray;
@@ -21,3 +27,7 @@
color: $action-text-color;
}
}
.padTop {
margin-top: $section-spacing;
}
@@ -1,6 +1,10 @@
import { Button } from '@chakra-ui/react';
import { IoEye } from '@react-icons/all-files/io5/IoEye';
import { IoEyeOffOutline } from '@react-icons/all-files/io5/IoEyeOffOutline';
import { IoMicOffOutline } from '@react-icons/all-files/io5/IoMicOffOutline';
import { IoMicSharp } from '@react-icons/all-files/io5/IoMicSharp';
import { IoSunny } from '@react-icons/all-files/io5/IoSunny';
import { IoSunnyOutline } from '@react-icons/all-files/io5/IoSunnyOutline';
import { setMessage, useMessageControl } from '../../../common/hooks/useSocket';
@@ -13,14 +17,6 @@ export default function MessageControl() {
return (
<div className={style.messageContainer}>
<InputRow
label='Timer screen message'
placeholder='Shown in stage timer'
text={data.timerMessage.text || ''}
visible={data.timerMessage.visible || false}
changeHandler={(newValue) => setMessage.presenterText(newValue)}
actionHandler={() => setMessage.presenterVisible(!data.timerMessage.visible)}
/>
<InputRow
label='Public / Backstage screen message'
placeholder='Shown in public and backstage screens'
@@ -37,7 +33,33 @@ export default function MessageControl() {
changeHandler={(newValue) => setMessage.lowerText(newValue)}
actionHandler={() => setMessage.lowerVisible(!data.lowerMessage.visible)}
/>
<div className={style.onAirSection}>
<InputRow
label='Timer message'
placeholder='Shown in stage timer'
text={data.timerMessage.text || ''}
visible={data.timerMessage.visible || false}
changeHandler={(newValue) => setMessage.presenterText(newValue)}
actionHandler={() => setMessage.presenterVisible(!data.timerMessage.visible)}
/>
<div className={style.buttonSection}>
<label className={style.label}>Timer messsage blink</label>
<label className={style.label}>Blackout timer screens</label>
<Button
className={`${data.timerMessage.timerBlink ? style.blink : ''}`}
variant={data.timerMessage.timerBlink ? 'ontime-filled' : 'ontime-subtle'}
leftIcon={data.timerMessage.timerBlink ? <IoSunny size='24px' /> : <IoSunnyOutline size='24px' />}
onClick={() => setMessage.timerBlink(!data.timerMessage.timerBlink)}
data-testid='toggle timer blink'
/>
<Button
className={style.blackoutButton}
variant={data.timerMessage.timerBlackout ? 'ontime-filled' : 'ontime-subtle'}
leftIcon={data.timerMessage.timerBlackout ? <IoEye size='24px' /> : <IoEyeOffOutline size='24px' />}
onClick={() => setMessage.timerBlackout(!data.timerMessage.timerBlackout)}
data-testid='toggle timer blackout'
/>
</div>
<div className={`${style.onAirSection} ${style.padTop}`}>
<label className={style.label}>Toggle On Air state</label>
<Button
variant={data.onAir ? 'ontime-filled' : 'ontime-subtle'}
@@ -6,6 +6,7 @@
overflow: hidden;
width: 100%; /* restrict the page width to viewport */
height: 100vh;
transition: opacity 0.5s ease-in-out;
background: var(--background-color-override, $viewer-background-color);
color: var(--color-override, $viewer-color);
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import { EventData, Message, Playback, TimerType, ViewSettings } from 'ontime-types';
import { EventData, Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types';
import { overrideStylesURL } from '../../../common/api/apiConstants';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
@@ -14,7 +14,7 @@ import './MinimalTimer.scss';
interface MinimalTimerProps {
isMirrored: boolean;
pres: Message;
pres: TimerMessage;
time: TimeManagerType;
viewSettings: ViewSettings;
general: EventData;
@@ -136,6 +136,9 @@ export default function MinimalTimer(props: MinimalTimerProps) {
const showProgress = time.playback !== Playback.Stop;
const showWarning = (time.current ?? 1) < viewSettings.warningThreshold;
const showDanger = (time.current ?? 1) < viewSettings.dangerThreshold;
const showBlinking = pres.timerBlink;
const showBlackout = pres.timerBlackout;
const timerColor = userOptions.textColour
? userOptions.textColour
: showProgress && showDanger
@@ -153,8 +156,10 @@ export default function MinimalTimer(props: MinimalTimerProps) {
const timerFontSize = (89 / (stageTimerCharacters - 1)) * (userOptions.size || 1);
const timerClasses = `timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''}`;
const baseClasses = `minimal-timer ${isMirrored ? 'mirror' : ''}`;
const timerClasses = `timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''} ${
showBlinking ? (showOverlay ? '' : 'blink') : ''
}`;
const baseClasses = `minimal-timer ${isMirrored ? 'mirror' : ''} ${showBlackout ? 'blackout' : ''}`;
return (
<div
@@ -169,11 +174,13 @@ export default function MinimalTimer(props: MinimalTimerProps) {
<NavigationMenu />
{!hideMessagesOverlay && (
<div className={showOverlay ? 'message-overlay message-overlay--active' : 'message-overlay'}>
<div className='message'>{pres.text}</div>
<div className={`message ${showBlinking ? 'blink' : ''}`}>{pres.text}</div>
</div>
)}
{showEndMessage ? (
<div className='end-message'>{viewSettings.endMessage}</div>
<div className={`end-message ${showBlinking ? (showOverlay ? '' : 'blink') : ''}`}>
{viewSettings.endMessage}
</div>
) : (
<div
className={timerClasses}
@@ -6,6 +6,7 @@
overflow: hidden;
width: 100%; /* restrict the page width to viewport */
height: 100vh;
transition: opacity 0.5s ease-in-out;
font-family: var(--font-family-override, $viewer-font-family);
background: var(--background-color-override, $viewer-background-color);
@@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { EventData, Message, Playback, TimerType, ViewSettings } from 'ontime-types';
import { EventData, Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types';
import { overrideStylesURL } from '../../../common/api/apiConstants';
import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar';
@@ -39,7 +39,7 @@ const titleVariants = {
interface TimerProps {
isMirrored: boolean;
general: EventData;
pres: Message;
pres: TimerMessage;
title: TitleManager;
time: TimeManagerType;
viewSettings: ViewSettings;
@@ -73,14 +73,16 @@ export default function Timer(props: TimerProps) {
const showFinished = finished && (time.timerType !== TimerType.Clock || showEndMessage);
const showWarning = (time.current ?? 1) < viewSettings.warningThreshold;
const showDanger = (time.current ?? 1) < viewSettings.dangerThreshold;
const showBlinking = pres.timerBlink;
const showBlackout = pres.timerBlackout;
const showClock = time.timerType !== TimerType.Clock;
const timerColor =
showProgress && showDanger
? viewSettings.dangerColor
: showProgress && showWarning
? viewSettings.warningColor
: viewSettings.normalColor;
const showClock = time.timerType !== TimerType.Clock;
const baseClasses = `stage-timer ${isMirrored ? 'mirror' : ''}`;
const stageTimer = getTimerByType(time);
let display = formatTimerDisplay(stageTimer);
@@ -89,6 +91,7 @@ export default function Timer(props: TimerProps) {
display = `-${display}`;
}
const baseClasses = `stage-timer ${isMirrored ? 'mirror' : ''} ${showBlackout ? 'blackout' : ''}`;
const timerFontSize = 89 / (stageTimerCharacters - 1);
const timerClasses = `timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''}`;
@@ -96,7 +99,7 @@ export default function Timer(props: TimerProps) {
<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 className={`message ${showBlinking ? 'blink' : ''}`}>{pres.text}</div>
</div>
<div className={`clock-container ${showClock ? '' : 'clock-container--hidden'}`}>
@@ -104,7 +107,7 @@ export default function Timer(props: TimerProps) {
<div className='clock'>{clock}</div>
</div>
<div className='timer-container'>
<div className={`timer-container ${showBlinking ? (showOverlay ? '' : 'blink') : ''}`}>
{showEndMessage ? (
<div className='end-message'>{viewSettings.endMessage}</div>
) : (