mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-19 06:04:05 +00:00
Feat/120 (#164)
* feat/120: calculate delay to single event * feat/120 Countdown * feat/120 prevent parsing NaN * feat/120: version bump * feat/120: update README.md * feat/120: show delayed countdown timer
This commit is contained in:
+4
-4
@@ -2,9 +2,9 @@ import React, { memo } from 'react';
|
||||
import { formatDisplay } from 'common/utils/dateConfig';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import styles from './Countdown.module.scss';
|
||||
import styles from './TimerDisplay.module.scss';
|
||||
|
||||
const Countdown = ({ time, small, isNegative, hideZeroHours }) => {
|
||||
const TimerDisplay = ({ time, small, isNegative, hideZeroHours }) => {
|
||||
// prepare display string
|
||||
const display =
|
||||
time != null && !isNaN(time) ? formatDisplay(time, hideZeroHours) : '-- : -- : --';
|
||||
@@ -19,9 +19,9 @@ const Countdown = ({ time, small, isNegative, hideZeroHours }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Countdown);
|
||||
export default memo(TimerDisplay);
|
||||
|
||||
Countdown.propTypes = {
|
||||
TimerDisplay.propTypes = {
|
||||
time: PropTypes.number,
|
||||
small: PropTypes.bool,
|
||||
isNegative: PropTypes.bool,
|
||||
@@ -6,6 +6,7 @@ const navigatorConstants = [
|
||||
{ url: '/lower', label: 'Lower Thirds' },
|
||||
{ url: '/pip', label: 'PiP' },
|
||||
{ url: '/studio', label: 'Studio Clock' },
|
||||
{ url: '/countdown', label: 'Countdown' },
|
||||
];
|
||||
|
||||
export default navigatorConstants;
|
||||
|
||||
@@ -5,9 +5,9 @@ import PropTypes from 'prop-types';
|
||||
import style from './Empty.module.scss';
|
||||
|
||||
export default function Empty(props) {
|
||||
const { text, ...rest } = props;
|
||||
const { text, dark, ...rest } = props;
|
||||
return (
|
||||
<div className={style.emptyContainer} {...rest}>
|
||||
<div className={`${style.emptyContainer} ${dark ? style.dark : ''}`} {...rest}>
|
||||
<Emptyimage className={style.empty} />
|
||||
<span className={style.text}>{text}</span>
|
||||
</div>
|
||||
@@ -16,4 +16,5 @@ export default function Empty(props) {
|
||||
|
||||
Empty.propTypes = {
|
||||
text: PropTypes.string,
|
||||
dark: PropTypes.bool,
|
||||
}
|
||||
@@ -2,16 +2,26 @@
|
||||
|
||||
.emptyContainer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty {
|
||||
width: 100%;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.text {
|
||||
color: $bg-black-300;
|
||||
font-weight: 600;
|
||||
font-size: 2em;
|
||||
|
||||
.empty {
|
||||
width: 100%;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-weight: 600;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
&.dark {
|
||||
background: $bg-black;
|
||||
color: $bg-gray-500;
|
||||
|
||||
.empty {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,11 @@ describe('test string from formatDisplay function', () => {
|
||||
expect(formatDisplay(t.val, false)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with not numbers', () => {
|
||||
const t = { val: 'test', result: '00:00:00' };
|
||||
expect(formatDisplay(t.val, false)).toBe(t.result);
|
||||
});
|
||||
|
||||
it('test with valid millis', () => {
|
||||
const t = { val: 3600, result: '01:00:00' };
|
||||
expect(formatDisplay(t.val, false)).toBe(t.result);
|
||||
@@ -59,7 +64,7 @@ describe('test formatDisplay handles partial secs', () => {
|
||||
|
||||
describe('test string from formatDisplay function with hidezero', () => {
|
||||
it('test with null values', () => {
|
||||
const t = { val: null, result: '00:00' };
|
||||
const t = { val: null, result: '00:00:00' };
|
||||
expect(formatDisplay(t.val, true)).toBe(t.result);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import getDelayTo from '../getDelayTo';
|
||||
|
||||
describe('getDelayTo function', () => {
|
||||
it('handles list with delays', () => {
|
||||
const delayDuration = 100;
|
||||
const events = [
|
||||
{ type: 'event' },
|
||||
{ type: 'delay', duration: delayDuration },
|
||||
{ type: 'event' },
|
||||
];
|
||||
|
||||
const notDelayed = getDelayTo(events, 0);
|
||||
expect(notDelayed).toBe(0);
|
||||
const delayedEvent = getDelayTo(events, 2);
|
||||
expect(delayedEvent).toBe(delayDuration);
|
||||
});
|
||||
it('handles list without delays', () => {
|
||||
const events = [{ type: 'event' }, { type: 'event' }];
|
||||
const notDelayed = getDelayTo(events, 1);
|
||||
expect(notDelayed).toBe(0);
|
||||
});
|
||||
|
||||
it('handles list with multiple delays', () => {
|
||||
const delayDuration = 100;
|
||||
const events = [
|
||||
{ type: 'event' },
|
||||
{ type: 'delay', duration: delayDuration },
|
||||
{ type: 'event' },
|
||||
{ type: 'delay', duration: delayDuration },
|
||||
{ type: 'event' },
|
||||
];
|
||||
const doubleDelay = getDelayTo(events, 4);
|
||||
expect(doubleDelay).toBe(delayDuration * 2);
|
||||
});
|
||||
it('handles list with blocks', () => {
|
||||
const events = [
|
||||
{ type: 'event' },
|
||||
{ type: 'delay', duration: 100 },
|
||||
{ type: 'event' },
|
||||
{ type: 'block' },
|
||||
{ type: 'event' },
|
||||
];
|
||||
const notDelayed = getDelayTo(events, 4);
|
||||
expect(notDelayed).toBe(0);
|
||||
});
|
||||
it('handles index greater than list', () => {
|
||||
const events = [{ type: 'event' }, { type: 'delay', duration: 100 }, { type: 'event' }];
|
||||
const notDelayed = getDelayTo(events, 3);
|
||||
expect(notDelayed).toBe(0);
|
||||
});
|
||||
it('handles negative index (not found)', () => {
|
||||
const events = [{ type: 'event' }, { type: 'delay', duration: 100 }, { type: 'event' }];
|
||||
const notDelayed = getDelayTo(events, -1);
|
||||
expect(notDelayed).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -13,6 +13,10 @@ const mth = 1000 * 60 * 60; // millis to hours
|
||||
* @returns {string} String representing absolute time 00:12:02
|
||||
*/
|
||||
export function formatDisplay(seconds, hideZero = false) {
|
||||
if (typeof seconds !== 'number') {
|
||||
return '00:00:00';
|
||||
}
|
||||
|
||||
// add an extra 0 if necessary
|
||||
const format = (val) => `0${Math.floor(val)}`.slice(-2);
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @description calculates delay to a given event
|
||||
* @param {array} events
|
||||
* @param {number} eventIndex
|
||||
* @return {number} - delay value of given event
|
||||
*/
|
||||
export default function getDelayTo(events, eventIndex) {
|
||||
let delay = 0;
|
||||
let index = 0;
|
||||
if (eventIndex >= 0) {
|
||||
for (const event of events) {
|
||||
if (eventIndex === index) {
|
||||
return delay;
|
||||
}
|
||||
|
||||
if (event.type === 'delay') {
|
||||
delay += event.duration;
|
||||
} else if (event.type === 'block') {
|
||||
delay = 0;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { memo } from 'react';
|
||||
import { Button } from '@chakra-ui/button';
|
||||
import { Tooltip } from '@chakra-ui/react';
|
||||
import Countdown from 'common/components/countdown/Countdown';
|
||||
import TimerDisplay from 'common/components/countdown/TimerDisplay';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { stringFromMillis } from '../../../common/utils/time';
|
||||
@@ -45,7 +45,7 @@ const PlaybackTimer = (props) => {
|
||||
<div className={style.indDelay} />
|
||||
</div>
|
||||
<div className={style.timer}>
|
||||
<Countdown
|
||||
<TimerDisplay
|
||||
time={isWaiting ? timer.secondary : timer.running}
|
||||
isNegative={timer.isNegative}
|
||||
small
|
||||
|
||||
@@ -222,6 +222,7 @@ const withSocket = (Component) => {
|
||||
...timer,
|
||||
finished: playback === 'start' && timer.isNegative && timer.startedAt,
|
||||
clock: stringFromMillis(timer.clock),
|
||||
clockMs: timer.clock,
|
||||
clockNoSeconds: stringFromMillis(timer.clock, false),
|
||||
playstate: playback,
|
||||
};
|
||||
|
||||
@@ -1,13 +1,126 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import NavLogo from '../../../common/components/nav/NavLogo';
|
||||
import Empty from '../../../common/state/Empty';
|
||||
import { formatDisplay, millisToSeconds } from '../../../common/utils/dateConfig';
|
||||
import getDelayTo from '../../../common/utils/getDelayTo';
|
||||
import { stringFromMillis } from '../../../common/utils/time';
|
||||
|
||||
import { fetchTimerData, sanitiseTitle } from './countdown.helpers';
|
||||
|
||||
import style from './Countdown.module.scss';
|
||||
|
||||
export default function Countdown(props) {
|
||||
const [searchParams] = useSearchParams();
|
||||
const { backstageEvents, time, selectedId } = props;
|
||||
const [follow, setFollow] = useState(null);
|
||||
const [runningTimer, setRunningTimer] = useState(0);
|
||||
const [runningMessage, setRunningMessage] = useState('');
|
||||
const [delay, setDelay] = useState(0);
|
||||
|
||||
// Set window title
|
||||
useEffect(() => {
|
||||
document.title = 'ontime - Countdown';
|
||||
}, []);
|
||||
|
||||
// eg. http://localhost:4001/countdown?eventId=ei0us
|
||||
// Check for user options
|
||||
useEffect(() => {
|
||||
if (!backstageEvents) {
|
||||
return;
|
||||
}
|
||||
|
||||
const eventId = searchParams.get('eventid');
|
||||
const eventIndex = searchParams.get('event');
|
||||
|
||||
let followThis = undefined;
|
||||
const events = [...backstageEvents].filter((event) => event.type === 'event');
|
||||
|
||||
if (eventId !== null) {
|
||||
followThis = events.find((event) => event.id === eventId);
|
||||
} else if (eventIndex !== null) {
|
||||
followThis = events?.[eventIndex - 1];
|
||||
}
|
||||
if (typeof followThis !== 'undefined') {
|
||||
setFollow(followThis);
|
||||
const idx = backstageEvents.findIndex((event) => event.id === followThis.id);
|
||||
const delay = getDelayTo(backstageEvents, idx);
|
||||
setDelay(delay);
|
||||
}
|
||||
}, [backstageEvents, searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!follow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { message, timer } = fetchTimerData(time, follow, selectedId);
|
||||
setRunningMessage(message);
|
||||
setRunningTimer(timer);
|
||||
}, [follow, selectedId, time]);
|
||||
|
||||
const standby = time.playstate !== 'start' && selectedId === follow?.id;
|
||||
|
||||
return (
|
||||
<div>
|
||||
Countdown
|
||||
<div className={style.container}>
|
||||
<NavLogo />
|
||||
{follow === null ? (
|
||||
<div className={style.eventSelect}>
|
||||
<span className={style.actionTitle}>Select an event to follow</span>
|
||||
<ul className={style.events}>
|
||||
{backstageEvents.length === 0 ? (
|
||||
<Empty dark text='No events in database' />
|
||||
) : (
|
||||
backstageEvents
|
||||
.filter((e) => e.type === 'event')
|
||||
.map((event, index) => (
|
||||
<li key={event.id}>
|
||||
<Link to={`/countdown?eventid=${event.id}`}>
|
||||
{`${index + 1}. ${sanitiseTitle(event.title)}`}
|
||||
</Link>
|
||||
</li>
|
||||
))
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
) : (
|
||||
<div className={style.countdownContainer}>
|
||||
<div className={style.timers}>
|
||||
<div className={style.timer}>
|
||||
<div className={style.label}>Time Now</div>
|
||||
<span className={style.value}>{time.clock}</span>
|
||||
</div>
|
||||
<div className={style.timer}>
|
||||
<div className={style.label}>Start Time</div>
|
||||
<span className={`${style.value} ${delay > 0 ? style.delayed : ''}`}>
|
||||
{stringFromMillis(follow.timeStart + delay)}
|
||||
</span>
|
||||
</div>
|
||||
<div className={style.timer}>
|
||||
<div className={style.label}>End Time</div>
|
||||
<span className={`${style.value} ${delay > 0 ? style.delayed : ''}`}>
|
||||
{stringFromMillis(follow.timeEnd + delay)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={style.status}>{runningMessage}</div>
|
||||
<span className={`${style.countdownClock} ${standby ? style.standby : ''}`}>
|
||||
{formatDisplay(
|
||||
time.running ? runningTimer : runningTimer + millisToSeconds(delay),
|
||||
time.running || time.waiting
|
||||
)}
|
||||
</span>
|
||||
<div className={style.title}>{follow.title || 'Untitled Event'}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Countdown.propTypes={
|
||||
|
||||
}
|
||||
Countdown.propTypes = {
|
||||
backstageEvents: PropTypes.array,
|
||||
time: PropTypes.object,
|
||||
selectedId: PropTypes.string,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
@use '../../../styles/main' as *;
|
||||
|
||||
.container {
|
||||
margin: 0;
|
||||
box-sizing: border-box; /* reset */
|
||||
overflow: hidden;
|
||||
width: 100%; /* restrict the page width to viewport */
|
||||
|
||||
background: $bg-black;
|
||||
height: 100vh;
|
||||
color: $title-white;
|
||||
padding: 1vw;
|
||||
|
||||
.eventSelect {
|
||||
display: flex;
|
||||
margin-top: 8vh;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: max(1.3vw, 12px);
|
||||
flex-direction: column;
|
||||
|
||||
.actionTitle {
|
||||
font-size: max(2vw, 14px);
|
||||
}
|
||||
|
||||
.events {
|
||||
margin-top: 1em;
|
||||
overflow-y: auto;
|
||||
height: 70vh;
|
||||
width: 60vw;
|
||||
}
|
||||
}
|
||||
|
||||
.countdownContainer {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto auto auto 1fr auto;
|
||||
grid-template-columns: 100%;
|
||||
grid-template-areas:
|
||||
'title'
|
||||
'status'
|
||||
'clock'
|
||||
'y'
|
||||
'timers';
|
||||
gap: 1vw;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
|
||||
.timers {
|
||||
grid-area: timers;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
align-items: flex-end;
|
||||
|
||||
.timer {
|
||||
text-align: center;
|
||||
|
||||
.label {
|
||||
font-size: max(1.3vw, 12px);
|
||||
color: $ontime-pink;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size: max(2vw, 14px);
|
||||
letter-spacing: 0.3px;
|
||||
color: #ddd;
|
||||
|
||||
&.delayed {
|
||||
color: $block-delay-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
grid-area: title;
|
||||
font-size: max(4vw, 18px);
|
||||
align-self: center;
|
||||
color: $ontime-pink;
|
||||
background-color: $bg-gray-1000;
|
||||
padding: 1vh 2vw;
|
||||
border-radius: 1vw;
|
||||
}
|
||||
|
||||
.status {
|
||||
grid-area: status;
|
||||
padding-left: 5vw;
|
||||
font-size: max(2.5vw, 14px);
|
||||
justify-self: flex-start;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.countdownClock {
|
||||
grid-area: clock;
|
||||
line-height: 20vw;
|
||||
font-size: 21vw;
|
||||
text-align: center;
|
||||
letter-spacing: 0.4vw;
|
||||
align-self: flex-start;
|
||||
opacity: 1.0;
|
||||
transition: opacity 0.5s;
|
||||
|
||||
&.standby {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import { DAY_TO_MS } from '../../../../../../server/src/classes/classUtils';
|
||||
import { millisToSeconds } from '../../../../common/utils/dateConfig';
|
||||
import { fetchTimerData, sanitiseTitle, timerMessages } from '../countdown.helpers';
|
||||
|
||||
describe('sanitiseTitle() function', () => {
|
||||
test('should return a title when valid', () => {
|
||||
const validTitles = ['Test', 'test', 'test000', '...', 'test0999', 'test%&'];
|
||||
|
||||
for (const title of validTitles) {
|
||||
expect(sanitiseTitle(title)).toBe(title);
|
||||
}
|
||||
});
|
||||
|
||||
test('should return {no title} when invalid', () => {
|
||||
const invalidTitles = ['', undefined, null];
|
||||
for (const title of invalidTitles) {
|
||||
expect(sanitiseTitle(title)).toBe('{no title}');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchTimerData() function', () => {
|
||||
it('shows running timer if current is the one we follow', () => {
|
||||
const followId = 'testId';
|
||||
const runningMockValue = 13;
|
||||
const follow = { id: followId };
|
||||
const time = { running: runningMockValue };
|
||||
|
||||
const { message, timer } = fetchTimerData(time, follow, followId);
|
||||
expect(message).toBe(timerMessages.running);
|
||||
expect(timer).toBe(runningMockValue);
|
||||
});
|
||||
|
||||
it('shows the countdown to an upcoming event', () => {
|
||||
const startMockValue = 10000;
|
||||
const timeNow = 1000;
|
||||
const follow = { id: 'anotherevent', timeStart: startMockValue };
|
||||
const time = { clockMs: timeNow };
|
||||
|
||||
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
|
||||
expect(message).toBe(timerMessages.toStart);
|
||||
expect(timer).toBe(millisToSeconds(startMockValue - timeNow));
|
||||
});
|
||||
|
||||
it('shows the timer of a scheduled event that hasnt started', () => {
|
||||
const startMockValue = 10000;
|
||||
const endMockValue = 20000;
|
||||
const timeNow = 15000;
|
||||
const followId = 'testId';
|
||||
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
|
||||
const time = { clockMs: timeNow, running: endMockValue - startMockValue };
|
||||
|
||||
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
|
||||
expect(message).toBe(timerMessages.waiting);
|
||||
expect(timer).toBe(endMockValue - startMockValue);
|
||||
});
|
||||
|
||||
it('shows the end time of a finished event', () => {
|
||||
const startMockValue = 10000;
|
||||
const endMockValue = 20000;
|
||||
const timeNow = 30000;
|
||||
const followId = 'testId';
|
||||
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
|
||||
const time = { clockMs: timeNow, running: endMockValue - startMockValue };
|
||||
|
||||
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
|
||||
expect(message).toBe(timerMessages.ended);
|
||||
expect(timer).toBe(millisToSeconds(endMockValue));
|
||||
});
|
||||
|
||||
it('handle an idle event that finishes after midnight', () => {
|
||||
const startMockValue = 10000;
|
||||
const endMockValue = 1000;
|
||||
const timeNow = 15000;
|
||||
const followId = 'testId';
|
||||
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
|
||||
const time = { clockMs: timeNow, running: DAY_TO_MS + endMockValue - startMockValue };
|
||||
|
||||
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
|
||||
expect(message).toBe(timerMessages.waiting);
|
||||
expect(timer).toBe(DAY_TO_MS + endMockValue - startMockValue);
|
||||
});
|
||||
|
||||
it('handle an running event that finishes after midnight', () => {
|
||||
const startMockValue = 10000;
|
||||
const endMockValue = 1000;
|
||||
const timeNow = 15000;
|
||||
const followId = 'testId';
|
||||
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
|
||||
const time = { clockMs: timeNow, running: DAY_TO_MS + endMockValue - startMockValue };
|
||||
|
||||
const { message, timer } = fetchTimerData(time, follow, followId);
|
||||
expect(message).toBe(timerMessages.running);
|
||||
expect(timer).toBe(DAY_TO_MS + endMockValue - startMockValue);
|
||||
});
|
||||
|
||||
it('handle an event that finishes after midnight but hasnt started', () => {
|
||||
const startMockValue = 10000;
|
||||
const endMockValue = 1000;
|
||||
const timeNow = 2000;
|
||||
const followId = 'testId';
|
||||
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
|
||||
const time = { clockMs: timeNow, running: DAY_TO_MS + endMockValue - startMockValue };
|
||||
|
||||
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
|
||||
expect(message).toBe(timerMessages.toStart);
|
||||
expect(timer).toBe(millisToSeconds(startMockValue - timeNow));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
import { millisToSeconds } from '../../../common/utils/dateConfig';
|
||||
|
||||
/**
|
||||
* @description parses string as a title
|
||||
* @param {string|null} title
|
||||
* @return {string}
|
||||
*/
|
||||
export const sanitiseTitle = (title) =>
|
||||
title === null || title === '' || typeof title === 'undefined' ? '{no title}' : title;
|
||||
|
||||
/**
|
||||
* @description object with possible timer messages
|
||||
* @type {{running: string, toStart: string, waiting: string, ended: string}}
|
||||
*/
|
||||
export const timerMessages = {
|
||||
toStart: 'Time to start',
|
||||
waiting: 'Waiting for event start',
|
||||
running: 'Event running',
|
||||
ended: 'Event ended at',
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Returns a parsed timer and relevant status message
|
||||
* @param {object} time
|
||||
* @param {object} follow
|
||||
* @param {string} selectedId
|
||||
* @return {{timer: number, message: string}}
|
||||
*/
|
||||
export const fetchTimerData = (time, follow, selectedId) => {
|
||||
let message = "";
|
||||
let timer = 0;
|
||||
|
||||
if (selectedId === follow.id) {
|
||||
// check that is not running
|
||||
message = time.playstate === 'pause' ? timerMessages.waiting : timerMessages.running;
|
||||
timer = time.running;
|
||||
} else if (time.clockMs < follow.timeStart) {
|
||||
// if it hasnt started, we count to start
|
||||
message = timerMessages.toStart;
|
||||
timer = millisToSeconds(follow.timeStart - time.clockMs);
|
||||
} else if (follow.timeStart <= time.clockMs && time.clockMs <= follow.timeEnd) {
|
||||
// if it has started, we show running timer
|
||||
message = timerMessages.waiting;
|
||||
timer = time.running;
|
||||
} else {
|
||||
if (follow.timeStart > follow.timeEnd) {
|
||||
// ends day after
|
||||
if (follow.timeStart > time.clockMs ) {
|
||||
// if it hasnt started, we count to start
|
||||
message = timerMessages.toStart;
|
||||
timer = millisToSeconds(follow.timeStart - time.clockMs);
|
||||
} else if (follow.timeStart <= time.clockMs) {
|
||||
// if it has started, we show running timer
|
||||
message = timerMessages.waiting;
|
||||
timer = time.running;
|
||||
} else {
|
||||
// if it has ended, we show how long ago
|
||||
message = timerMessages.ended;
|
||||
timer = millisToSeconds(follow.timeEnd);
|
||||
}
|
||||
} else {
|
||||
// if it has ended, we show how long ago
|
||||
message = timerMessages.ended;
|
||||
timer = millisToSeconds(follow.timeEnd);
|
||||
}
|
||||
}
|
||||
return { message, timer };
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import Countdown from 'common/components/countdown/Countdown';
|
||||
import TimerDisplay from 'common/components/countdown/TimerDisplay';
|
||||
import MyProgressBar from 'common/components/myProgressBar/MyProgressBar';
|
||||
import NavLogo from 'common/components/nav/NavLogo';
|
||||
import TitleCard from 'common/components/views/TitleCard';
|
||||
@@ -39,7 +39,7 @@ export default function Timer(props) {
|
||||
// show timer if end message is empty
|
||||
const endMessage =
|
||||
general.endMessage == null || general.endMessage === '' ? (
|
||||
<Countdown
|
||||
<TimerDisplay
|
||||
time={time.running}
|
||||
isNegative={time.isNegative}
|
||||
hideZeroHours
|
||||
@@ -90,7 +90,7 @@ export default function Timer(props) {
|
||||
<div className={style.finished}>{endMessage}</div>
|
||||
) : (
|
||||
<div className={isPlaying ? style.countdown : style.countdownPaused}>
|
||||
<Countdown time={normalisedTime} hideZeroHours />
|
||||
<TimerDisplay time={normalisedTime} hideZeroHours />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user