* feat(key): add user options to minimal timer
* feat(key): register minimal timer in settings
* feat(key): version bump
This commit is contained in:
Carlos Valente
2022-08-03 23:01:03 +02:00
committed by GitHub
parent 18f3f3643b
commit 05be0c1e95
6 changed files with 151 additions and 15 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ontime-ui",
"version": "1.2.3",
"version": "1.3.0",
"private": true,
"dependencies": {
"@chakra-ui/react": "^2.0.0-next.3",
+2
View File
@@ -1,4 +1,5 @@
// Exported viewer link location
const minimalLocation = 'minimal';
const speakerLocation = 'speaker';
const smLocation = 'sm';
const publicLocation = 'public';
@@ -7,6 +8,7 @@ const studioLocation = 'studio';
const cuesheetLocation = 'cuesheet';
export const viewerLocations = [
{ link: minimalLocation, label: 'Minimal timer' },
{ link: speakerLocation, label: 'Speaker Screen' },
{ link: smLocation, label: 'Backstage Screen' },
{ link: publicLocation, label: 'Public Screen' },
@@ -68,6 +68,7 @@ export default function NavLogo(props) {
initial={{ opacity: showNav ? 0.5 : baseOpacity }}
whileHover={{ opacity: 1 }}
className={style.navContainer}
data-testid='nav-logo'
>
<Image alt='' src={navlogo} className={style.logo} onClick={handleClick} />
<AnimatePresence>
@@ -1,4 +1,5 @@
import React, { useEffect } from 'react';
import React from 'react';
import { useSearchParams } from 'react-router-dom';
import PropTypes from 'prop-types';
import NavLogo from '../../../common/components/nav/NavLogo';
@@ -8,26 +9,127 @@ import style from './MinimalTimer.module.scss';
export default function MinimalTimer(props) {
const { pres, time } = props;
const [searchParams] = useSearchParams();
// Set window title
useEffect(() => {
document.title = 'ontime - Minimal Timer';
}, []);
document.title = 'ontime - Minimal Timer';
// get config from url: key, text, font, size, hidenav, hideovertime
// eg. http://localhost:3000/minimal?key=f00&text=fff
// Check for user options
const userOptions = {};
// key: string
// Should be a hex string '#00FF00' with key colour
const key = searchParams.get('key');
if (key) {
userOptions.keyColour = `#${key}`;
}
// textColour: string
// Should be a hex string '#ffffff'
const textColour = searchParams.get('text');
if (textColour) {
userOptions.textColour = `#${textColour}`;
}
// font: string
// Should be a string with a font name 'arial'
const font = searchParams.get('font');
if (font) {
userOptions.font = font;
}
// size: multiplier
// Should be a number 0.0-n
const size = searchParams.get('size');
if (size) {
userOptions.size = size;
}
// alignX: flex justification
// start | center | end
const alignX = searchParams.get('alignx');
if (alignX) {
if (alignX === 'start' || alignX === 'center' || alignX === 'end') {
userOptions.justifyContent = alignX;
}
}
// alignX: flex alignment
// start | center | end
const alignY = searchParams.get('aligny');
if (alignY) {
if (alignY === 'start' || alignY === 'center' || alignY === 'end') {
userOptions.alignItems = alignY;
}
}
// offsetX: position in pixels
// Should be a number 0 - 1920
const offsetX = searchParams.get('offsetx');
if (offsetX) {
const pixels = Number(offsetX);
if (!isNaN(pixels)) {
userOptions.left = `${pixels}px`;
}
}
// offsetX: position in pixels
// Should be a number 0 - 1920
const offsetY = searchParams.get('offsety');
if (offsetY) {
const pixels = Number(offsetY);
if (!isNaN(pixels)) {
userOptions.top = `${pixels}px`;
}
}
const hideNav = searchParams.get('hidenav');
if (hideNav) {
userOptions.hideNav = hideNav;
}
const hideOvertime = searchParams.get('hideovertime');
if (hideOvertime) {
userOptions.hideOvertime = hideOvertime;
}
const hideMessagesOverlay = searchParams.get('hidemessages');
if (hideMessagesOverlay) {
userOptions.hideMessagesOverlay = hideMessagesOverlay;
}
const showOverlay = pres.text !== '' && pres.visible;
const isPlaying = time.playstate !== 'pause';
const timer = formatDisplay(time.running, true);
const clean = timer.replace('/:/g', '');
const finishedStyle = userOptions?.hideOvertime ? style.container : style.containerFinished;
return (
<div className={time.finished ? style.containerFinished : style.container}>
<div className={showOverlay ? style.messageOverlayActive : style.messageOverlay}>
<div className={style.message}>{pres.text}</div>
</div>
<NavLogo />
<div
className={time.finished ? finishedStyle : style.container}
style={{
backgroundColor: userOptions.keyColour,
color: userOptions.textColour,
justifyContent: userOptions.justifyContent,
alignItems: userOptions.alignItems,
}}
data-testid='minimal-timer'
>
{!hideMessagesOverlay && (
<div className={showOverlay ? style.messageOverlayActive : style.messageOverlay}>
<div className={style.message}>{pres.text}</div>
</div>
)}
{!userOptions?.hideNav && <NavLogo />}
<div
style={{ fontSize: `${89 / (clean.length - 1)}vw` }}
className={isPlaying ? style.timer : style.timerPaused}
style={{
fontSize: `${(89 / (clean.length - 1)) * userOptions.size}vw`,
fontFamily: userOptions.font,
top: userOptions.top,
left: userOptions.left,
}}
>
{time.isNegative ? `-${timer}` : timer}
</div>
@@ -5,10 +5,11 @@
background: $bg-black;
height: 100vh;
color: $title-white;
display: grid;
place-content: center;
display: flex;
align-items: center;
justify-content: center;
gap: 1vw;
border: 1vw solid $bg-black;
border: 1vw solid transparent;
}
.containerFinished {
@@ -21,6 +22,7 @@
.timerPaused {
font-family: "Arial Black", sans-serif;
font-size: 20vw;
position: relative;
color: inherit;
opacity: 1;
transition: 0.5s;
@@ -91,4 +91,33 @@ describe('validate routes', () => {
cy.visit('http://localhost:4001/cuelist');
cy.contains('Running Timer');
});
describe('minimal timer and options', () => {
it('renders base route', () => {
// base route
cy.visit('http://localhost:4001/minimal');
cy.get('[data-testid="minimal-timer"]').should('exist');
// ontime fallsback to stage timer on errors, so we check for that
cy.get('.App').should('not.contain', 'Time Now');
});
it('renders with defined options', () => {
// route with options
cy.visit(
'http://localhost:4001/minimal?font=arial=1&size=1.5&text=0f0&key=ff0&hideovertime=true&alignx=start&aligny=end&offsetx=100&offsety=-100&hidemessages=true'
);
cy.get('[data-testid="minimal-timer"]').should('exist');
// ontime fallback to stage timer on errors, so we check for that
cy.get('.App').should('not.contain', 'Time Now');
});
it('hides nav on given option', () => {
// route with options for no nav
cy.visit('http://localhost:4001/minimal?hidenav=true');
cy.get('[data-testid="minimal-timer"]').should('exist');
// ontime fallback to stage timer on errors, so we check for that
cy.get('.App').should('not.contain', 'Time Now');
cy.get('[data-testid="nav-logo"]').should('not.exist');
});
});
});