mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-16 21:03:29 +00:00
@@ -1,5 +1,3 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-nocheck -- working on it
|
||||
import { useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { FormControl, Input, Switch } from '@chakra-ui/react';
|
||||
@@ -59,7 +57,6 @@ export default function OscSettings() {
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
// @ts-expect-error -- we know the types dont match
|
||||
reset(data);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import QRCode from 'react-qr-code';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { Message, OntimeEvent, ProjectData, Settings, SupportedEvent, ViewSettings } from 'ontime-types';
|
||||
import { formatDisplay } from 'ontime-utils';
|
||||
@@ -16,17 +17,13 @@ import ViewParamsEditor from '../../../common/components/view-params-editor/View
|
||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||
import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import { formatTime } from '../../../common/utils/time';
|
||||
import { isStringBoolean } from '../../../common/utils/viewUtils';
|
||||
import { useTranslation } from '../../../translation/TranslationProvider';
|
||||
import { titleVariants } from '../common/animation';
|
||||
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
|
||||
|
||||
import './Backstage.scss';
|
||||
|
||||
const formatOptions = {
|
||||
showSeconds: true,
|
||||
format: 'hh:mm:ss a',
|
||||
};
|
||||
|
||||
interface BackstageProps {
|
||||
isMirrored: boolean;
|
||||
publ: Message;
|
||||
@@ -43,6 +40,9 @@ interface BackstageProps {
|
||||
export default function Backstage(props: BackstageProps) {
|
||||
const { isMirrored, publ, eventNow, eventNext, time, backstageEvents, selectedId, general, viewSettings, settings } =
|
||||
props;
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
||||
const { getLocalizedString } = useTranslation();
|
||||
const [blinkClass, setBlinkClass] = useState(false);
|
||||
@@ -68,6 +68,12 @@ export default function Backstage(props: BackstageProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hideSeconds = isStringBoolean(searchParams.get('hideSeconds'));
|
||||
const formatOptions = {
|
||||
showSeconds: !hideSeconds,
|
||||
format: 'hh:mm:ss a',
|
||||
};
|
||||
|
||||
const clock = formatTime(time.clock, formatOptions);
|
||||
const startedAt = formatTime(time.startedAt, formatOptions);
|
||||
const isNegative = (time.current ?? 0) < 0;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { removePrependedZero } from '../viewerUtils.js';
|
||||
|
||||
describe('removePrependedZero', () => {
|
||||
it('should remove "00:" from the start of the string', () => {
|
||||
const timer = '00:10:10';
|
||||
const result = removePrependedZero(timer);
|
||||
expect(result).toBe('10:10');
|
||||
});
|
||||
|
||||
it('should remove "00:0:" from the start of the string', () => {
|
||||
const timer = '00:01:10';
|
||||
const result = removePrependedZero(timer);
|
||||
expect(result).toBe('1:10');
|
||||
});
|
||||
|
||||
it('should not modify the string if it does not start with "00:" or "00:0:"', () => {
|
||||
const timer = '10:10:10';
|
||||
const result = removePrependedZero(timer);
|
||||
expect(result).toBe(timer);
|
||||
});
|
||||
});
|
||||
@@ -8,6 +8,9 @@ interface SuperscriptTimeProps {
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description receives a string like 12:00 AM and adds the period part to the superscript
|
||||
*/
|
||||
export default function SuperscriptTime(props: SuperscriptTimeProps) {
|
||||
const { time, className, style } = props;
|
||||
|
||||
|
||||
@@ -1,43 +1,39 @@
|
||||
import { TimerType } from 'ontime-types';
|
||||
import { formatDisplay } from 'ontime-utils';
|
||||
|
||||
import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import { formatTime } from '../../../common/utils/time';
|
||||
|
||||
const formatOptions = {
|
||||
showSeconds: true,
|
||||
format: 'hh:mm:ss a',
|
||||
};
|
||||
import type { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
|
||||
type TimerTypeParams = Pick<TimeManagerType, 'timerType' | 'current' | 'elapsed' | 'clock'>;
|
||||
|
||||
export function getTimerByType(timerObject?: TimerTypeParams): string | number | null {
|
||||
let timer = null;
|
||||
export function getTimerByType(timerObject?: TimerTypeParams): number | null {
|
||||
if (!timerObject) {
|
||||
return timer;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (timerObject.timerType === TimerType.CountDown || timerObject.timerType === TimerType.TimeToEnd) {
|
||||
timer = timerObject.current;
|
||||
} else if (timerObject.timerType === TimerType.CountUp) {
|
||||
timer = timerObject.elapsed;
|
||||
} else if (timerObject.timerType === TimerType.Clock) {
|
||||
timer = formatTime(timerObject.clock, formatOptions);
|
||||
switch (timerObject.timerType) {
|
||||
case TimerType.CountDown:
|
||||
case TimerType.TimeToEnd:
|
||||
return timerObject.current;
|
||||
case TimerType.CountUp:
|
||||
return Math.abs(timerObject.elapsed ?? 0);
|
||||
case TimerType.Clock:
|
||||
return timerObject.clock;
|
||||
default: {
|
||||
const exhaustiveCheck: never = timerObject.timerType;
|
||||
return exhaustiveCheck;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a string such as 00:10:10 and removes the hours field if it is 00
|
||||
* @param timer
|
||||
*/
|
||||
export const removePrependedZero = (timer: string): string => {
|
||||
if (timer.startsWith('00:0')) {
|
||||
return timer.slice(4);
|
||||
}
|
||||
if (timer.startsWith('00:')) {
|
||||
return timer.slice(3);
|
||||
}
|
||||
return timer;
|
||||
}
|
||||
|
||||
export function formatTimerDisplay(timer?: string | number | null): string {
|
||||
let display = '';
|
||||
|
||||
if (typeof timer === 'string') {
|
||||
display = timer;
|
||||
} else if (timer === null || typeof timer === 'undefined' || isNaN(timer)) {
|
||||
display = '-- : -- : --';
|
||||
} else {
|
||||
display = formatDisplay(timer, true);
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,11 +5,12 @@ import { formatDisplay } from 'ontime-utils';
|
||||
|
||||
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
||||
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
|
||||
import { getTimeOption } from '../../../common/components/view-params-editor/constants';
|
||||
import { getCountdownOptions } from '../../../common/components/view-params-editor/constants';
|
||||
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
|
||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||
import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import { formatTime } from '../../../common/utils/time';
|
||||
import { isStringBoolean } from '../../../common/utils/viewUtils';
|
||||
import { useTranslation } from '../../../translation/TranslationProvider';
|
||||
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
|
||||
|
||||
@@ -18,11 +19,6 @@ import CountdownSelect from './CountdownSelect';
|
||||
|
||||
import './Countdown.scss';
|
||||
|
||||
const formatOptions = {
|
||||
showSeconds: true,
|
||||
format: 'hh:mm:ss a',
|
||||
};
|
||||
|
||||
const formatOptionsFinished = {
|
||||
showSeconds: false,
|
||||
format: 'hh:mm a',
|
||||
@@ -99,6 +95,12 @@ export default function Countdown(props: CountdownProps) {
|
||||
const isSelected = runningMessage === TimerMessage.running;
|
||||
const delayedTimerStyles = delay > 0 ? 'aux-timers__value--delayed' : '';
|
||||
|
||||
const hideSeconds = isStringBoolean(searchParams.get('hideSeconds'));
|
||||
const formatOptions = {
|
||||
showSeconds: !hideSeconds,
|
||||
format: 'hh:mm:ss a',
|
||||
};
|
||||
|
||||
const clock = formatTime(time.clock, formatOptions);
|
||||
const startTime = follow === null ? '...' : formatTime(follow.timeStart + delay, formatOptions);
|
||||
const endTime = follow === null ? '...' : formatTime(follow.timeEnd + delay, formatOptions);
|
||||
@@ -110,12 +112,12 @@ export default function Countdown(props: CountdownProps) {
|
||||
isSelected || runningMessage === TimerMessage.waiting,
|
||||
);
|
||||
|
||||
const timeOption = getTimeOption(settings?.timeFormat ?? '24');
|
||||
const timeOption = getCountdownOptions(settings?.timeFormat ?? '24');
|
||||
|
||||
return (
|
||||
<div className={`countdown ${isMirrored ? 'mirror' : ''}`} data-testid='countdown-view'>
|
||||
<NavigationMenu />
|
||||
<ViewParamsEditor paramFields={[timeOption]} />
|
||||
<ViewParamsEditor paramFields={timeOption} />
|
||||
{follow === null ? (
|
||||
<CountdownSelect events={backstageEvents} />
|
||||
) : (
|
||||
|
||||
@@ -9,8 +9,10 @@ import ViewParamsEditor from '../../../common/components/view-params-editor/View
|
||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||
import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import { OverridableOptions } from '../../../common/models/View.types';
|
||||
import { formatTime } from '../../../common/utils/time';
|
||||
import { isStringBoolean } from '../../../common/utils/viewUtils';
|
||||
import { formatTimerDisplay, getTimerByType } from '../common/viewerUtils';
|
||||
import { useTranslation } from '../../../translation/TranslationProvider';
|
||||
import { getTimerByType, removePrependedZero } from '../common/viewerUtils';
|
||||
|
||||
import './MinimalTimer.scss';
|
||||
|
||||
@@ -24,6 +26,7 @@ interface MinimalTimerProps {
|
||||
export default function MinimalTimer(props: MinimalTimerProps) {
|
||||
const { isMirrored, pres, time, viewSettings } = props;
|
||||
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
||||
const { getLocalizedString } = useTranslation();
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -35,9 +38,8 @@ export default function MinimalTimer(props: MinimalTimerProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// get config from url: key, text, font, size, hideovertime
|
||||
// eg. http://localhost:3000/minimal?key=f00&text=fff
|
||||
// Check for user options
|
||||
// TODO: this should be tied to the params
|
||||
// USER OPTIONS
|
||||
const userOptions: OverridableOptions = {
|
||||
size: 1,
|
||||
};
|
||||
@@ -126,10 +128,12 @@ export default function MinimalTimer(props: MinimalTimerProps) {
|
||||
const hideEndMessage = searchParams.get('hideendmessage');
|
||||
userOptions.hideEndMessage = isStringBoolean(hideEndMessage);
|
||||
|
||||
const hideTimerSeconds = searchParams.get('hideTimerSeconds');
|
||||
userOptions.hideTimerSeconds = isStringBoolean(hideTimerSeconds);
|
||||
|
||||
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 ?? 0) < 0 && viewSettings.endMessage && !hideEndMessage;
|
||||
const finished = time.playback === Playback.Play && (time.current ?? 0) < 0 && time.startedAt;
|
||||
const showFinished = finished && !userOptions?.hideOvertime && (time.timerType !== TimerType.Clock || showEndMessage);
|
||||
@@ -149,9 +153,16 @@ export default function MinimalTimer(props: MinimalTimerProps) {
|
||||
: viewSettings.normalColor;
|
||||
|
||||
const stageTimer = getTimerByType(time);
|
||||
let display = formatTimerDisplay(stageTimer);
|
||||
if (isNegative) {
|
||||
display = `-${display}`;
|
||||
let display = '-- : -- : --';
|
||||
if (stageTimer !== null) {
|
||||
display = formatTime(stageTimer, {
|
||||
showSeconds: !userOptions.hideTimerSeconds,
|
||||
format: 'hh:mm:ss a',
|
||||
});
|
||||
display = removePrependedZero(display);
|
||||
if (display.length < 3) {
|
||||
display = `${display} ${getLocalizedString('common.minutes')}`;
|
||||
}
|
||||
}
|
||||
const stageTimerCharacters = display.replace('/:/g', '').length;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import QRCode from 'react-qr-code';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { Message, OntimeEvent, ProjectData, Settings, ViewSettings } from 'ontime-types';
|
||||
|
||||
@@ -14,17 +15,13 @@ import ViewParamsEditor from '../../../common/components/view-params-editor/View
|
||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||
import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import { formatTime } from '../../../common/utils/time';
|
||||
import { isStringBoolean } from '../../../common/utils/viewUtils';
|
||||
import { useTranslation } from '../../../translation/TranslationProvider';
|
||||
import { titleVariants } from '../common/animation';
|
||||
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
|
||||
|
||||
import './Public.scss';
|
||||
|
||||
const formatOptions = {
|
||||
showSeconds: true,
|
||||
format: 'hh:mm:ss a',
|
||||
};
|
||||
|
||||
interface BackstageProps {
|
||||
isMirrored: boolean;
|
||||
publ: Message;
|
||||
@@ -51,6 +48,9 @@ export default function Public(props: BackstageProps) {
|
||||
viewSettings,
|
||||
settings,
|
||||
} = props;
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
||||
const { getLocalizedString } = useTranslation();
|
||||
|
||||
@@ -64,6 +64,13 @@ export default function Public(props: BackstageProps) {
|
||||
}
|
||||
|
||||
const showPublicMessage = publ.text && publ.visible;
|
||||
|
||||
const hideSeconds = isStringBoolean(searchParams.get('hideSeconds'));
|
||||
const formatOptions = {
|
||||
showSeconds: !hideSeconds,
|
||||
format: 'hh:mm:ss a',
|
||||
};
|
||||
|
||||
const clock = formatTime(time.clock, formatOptions);
|
||||
const qrSize = Math.max(window.innerWidth / 15, 128);
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ $orange-active: #f60;
|
||||
}
|
||||
}
|
||||
|
||||
.ampmIndicator {
|
||||
.clock__ampm {
|
||||
font-family: monospace;
|
||||
font-size: calc(var(--clock-size) / 20);
|
||||
position: relative;
|
||||
|
||||
@@ -14,17 +14,13 @@ import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
import { secondsInMillis } from '../../../common/utils/dateConfig';
|
||||
import { formatTime, resolveTimeFormat } from '../../../common/utils/time';
|
||||
import { mth } from '../../../common/utils/timeConstants';
|
||||
import { isStringBoolean } from '../../../common/utils/viewUtils';
|
||||
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
|
||||
|
||||
import { trimRundown } from './studioClock.utils';
|
||||
|
||||
import './StudioClock.scss';
|
||||
|
||||
const formatOptions = {
|
||||
showSeconds: false,
|
||||
format: 'hh:mm',
|
||||
};
|
||||
|
||||
interface StudioClockProps {
|
||||
isMirrored: boolean;
|
||||
eventNext: OntimeEvent | null;
|
||||
@@ -51,15 +47,17 @@ export default function StudioClock(props: StudioClockProps) {
|
||||
const MAX_TITLES = 11;
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const showSeconds = searchParams.get('seconds');
|
||||
const timeFormat = resolveTimeFormat();
|
||||
formatOptions.showSeconds = Boolean(showSeconds);
|
||||
formatOptions.format = `hh:mm${formatOptions.showSeconds ? ':ss' : ''}`;
|
||||
|
||||
useEffect(() => {
|
||||
document.title = 'ontime - Studio Clock';
|
||||
}, []);
|
||||
|
||||
const hideSeconds = isStringBoolean(searchParams.get('hideSeconds'));
|
||||
const formatOptions = {
|
||||
showSeconds: !hideSeconds,
|
||||
format: 'hh:mm:ss',
|
||||
};
|
||||
|
||||
const clock = formatTime(time.clock, formatOptions);
|
||||
const secondsNow = secondsInMillis(time.clock);
|
||||
const isNegative = (time.current ?? 0) < 0;
|
||||
@@ -70,14 +68,15 @@ export default function StudioClock(props: StudioClockProps) {
|
||||
const delayed = backstageEvents.filter((event) => isOntimeEvent(event)) as OntimeEvent[];
|
||||
const trimmedRundown = trimRundown(delayed, selectedId, MAX_TITLES);
|
||||
const isAm = time.clock / (mth * 12) > 12;
|
||||
const timeFormat = resolveTimeFormat();
|
||||
|
||||
return (
|
||||
<div className={`studio-clock ${isMirrored ? 'mirror' : ''}`} data-testid='studio-view'>
|
||||
<NavigationMenu />
|
||||
<ViewParamsEditor paramFields={studioClockOptions} />
|
||||
<div className='clock-container'>
|
||||
{timeFormat == '12' && <div className='ampmIndicator'>{isAm ? 'am' : 'pm'}</div>}
|
||||
<div className={`studio-timer ${showSeconds ? 'studio-timer--with-seconds' : ''}`}>{clock}</div>
|
||||
{timeFormat == '12' && <div className='clock__ampm'>{isAm ? 'am' : 'pm'}</div>}
|
||||
<div className={`studio-timer ${formatOptions.showSeconds ? 'studio-timer--with-seconds' : ''}`}>{clock}</div>
|
||||
<div
|
||||
ref={titleRef}
|
||||
className='next-title'
|
||||
|
||||
@@ -15,15 +15,10 @@ import { formatTime } from '../../../common/utils/time';
|
||||
import { isStringBoolean } from '../../../common/utils/viewUtils';
|
||||
import { useTranslation } from '../../../translation/TranslationProvider';
|
||||
import SuperscriptTime from '../common/superscript-time/SuperscriptTime';
|
||||
import { formatTimerDisplay, getTimerByType } from '../common/viewerUtils';
|
||||
import { getTimerByType, removePrependedZero } from '../common/viewerUtils';
|
||||
|
||||
import './Timer.scss';
|
||||
|
||||
const formatOptions = {
|
||||
showSeconds: true,
|
||||
format: 'hh:mm:ss a',
|
||||
};
|
||||
|
||||
// motion
|
||||
const titleVariants = {
|
||||
hidden: {
|
||||
@@ -72,6 +67,8 @@ export default function Timer(props: TimerProps) {
|
||||
hideCards: false,
|
||||
hideProgress: false,
|
||||
hideMessage: false,
|
||||
hideTimerSeconds: false,
|
||||
hideClockSeconds: false,
|
||||
};
|
||||
|
||||
const hideClock = searchParams.get('hideClock');
|
||||
@@ -86,12 +83,19 @@ export default function Timer(props: TimerProps) {
|
||||
const hideMessage = searchParams.get('hideMessage');
|
||||
userOptions.hideMessage = isStringBoolean(hideMessage);
|
||||
|
||||
const clock = formatTime(time.clock, formatOptions);
|
||||
const hideClockSeconds = searchParams.get('hideClockSeconds');
|
||||
userOptions.hideClockSeconds = isStringBoolean(hideClockSeconds);
|
||||
const clock = formatTime(time.clock, {
|
||||
showSeconds: !userOptions.hideClockSeconds,
|
||||
format: 'hh:mm:ss a',
|
||||
});
|
||||
|
||||
const hideTimerSeconds = searchParams.get('hideTimerSeconds');
|
||||
userOptions.hideTimerSeconds = isStringBoolean(hideTimerSeconds);
|
||||
|
||||
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 finished = time.playback === Playback.Play && (time.current ?? 0) < 0 && time.startedAt;
|
||||
const totalTime = (time.duration ?? 0) + (time.addedTime ?? 0);
|
||||
|
||||
@@ -113,11 +117,18 @@ export default function Timer(props: TimerProps) {
|
||||
: viewSettings.normalColor;
|
||||
|
||||
const stageTimer = getTimerByType(time);
|
||||
let display = formatTimerDisplay(stageTimer);
|
||||
const stageTimerCharacters = display.replace('/:/g', '').length;
|
||||
if (isNegative) {
|
||||
display = `-${display}`;
|
||||
let display = '-- : -- : --';
|
||||
if (stageTimer !== null) {
|
||||
display = formatTime(stageTimer, {
|
||||
showSeconds: !userOptions.hideTimerSeconds,
|
||||
format: 'hh:mm:ss a',
|
||||
});
|
||||
display = removePrependedZero(display);
|
||||
if (display.length < 3) {
|
||||
display = `${display} ${getLocalizedString('common.minutes')}`;
|
||||
}
|
||||
}
|
||||
const stageTimerCharacters = display.replace('/:/g', '').length;
|
||||
|
||||
const baseClasses = `stage-timer ${isMirrored ? 'mirror' : ''} ${showBlackout ? 'blackout' : ''}`;
|
||||
let timerFontSize = 89 / (stageTimerCharacters - 1);
|
||||
|
||||
Reference in New Issue
Block a user