mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-11 10:23:54 +00:00
refactor: increase update rate (#430)
* refactor: remove unused components * refactor: increase timer refresh rate
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
export const queryClientMock = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
// Exported viewer link location
|
||||
const minimalLocation = 'minimal';
|
||||
const speakerLocation = 'speaker';
|
||||
const smLocation = 'sm';
|
||||
const publicLocation = 'public';
|
||||
const studioLocation = 'studio';
|
||||
const cuesheetLocation = 'cuesheet';
|
||||
const countdownLocation = 'countdown';
|
||||
const clockLocation = 'clock';
|
||||
const lowerLocation = 'lower';
|
||||
|
||||
export const viewerLocations = [
|
||||
{ link: speakerLocation, label: 'Stage timer' },
|
||||
{ link: clockLocation, label: 'Clock' },
|
||||
{ link: minimalLocation, label: 'Minimal timer' },
|
||||
{ link: smLocation, label: 'Backstage screen' },
|
||||
{ link: publicLocation, label: 'Public screen' },
|
||||
{ link: lowerLocation, label: 'Lower thirds' },
|
||||
{ link: studioLocation, label: 'Studio clock' },
|
||||
{ link: countdownLocation, label: 'Countdown' },
|
||||
{ link: cuesheetLocation, label: 'Cuesheet' },
|
||||
];
|
||||
@@ -1,26 +0,0 @@
|
||||
import { Button } from '@chakra-ui/react';
|
||||
import { IoCheckmarkSharp } from '@react-icons/all-files/io5/IoCheckmarkSharp';
|
||||
import { IoCloseSharp } from '@react-icons/all-files/io5/IoCloseSharp';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function EnableBtn(props) {
|
||||
const { active, text, actionHandler, size = 'xs' } = props;
|
||||
return (
|
||||
<Button
|
||||
size={size}
|
||||
leftIcon={active ? <IoCheckmarkSharp /> : <IoCloseSharp />}
|
||||
colorScheme='blue'
|
||||
variant={active ? 'solid' : 'outline'}
|
||||
onClick={actionHandler}
|
||||
>
|
||||
{text}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
EnableBtn.propTypes = {
|
||||
active: PropTypes.bool,
|
||||
text: PropTypes.string,
|
||||
actionHandler: PropTypes.func,
|
||||
size: PropTypes.oneOf(['xs', 'sm', 'md', 'lg']),
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import { IoPause } from '@react-icons/all-files/io5/IoPause';
|
||||
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
interface PauseIconBtnProps {
|
||||
clickhandler: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
active: boolean;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
export default function PauseIconBtn(props: PauseIconBtnProps) {
|
||||
const { clickhandler, active, disabled, ...rest } = props;
|
||||
return (
|
||||
<Tooltip label='Pause timer' openDelay={tooltipDelayMid} shouldWrapChildren={disabled}>
|
||||
<IconButton
|
||||
icon={<IoPause size='24px' />}
|
||||
colorScheme='orange'
|
||||
variant={active ? 'solid' : 'outline'}
|
||||
onClick={clickhandler}
|
||||
width={120}
|
||||
disabled={disabled}
|
||||
aria-label='Pause playback'
|
||||
{...rest}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import { FiUsers } from '@react-icons/all-files/fi/FiUsers';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function PublicIconBtn(props) {
|
||||
const { actionHandler, active, size = 'xs', ...rest } = props;
|
||||
return (
|
||||
<Tooltip label={active ? 'Make event private' : 'Make event public'}>
|
||||
<IconButton
|
||||
size={size}
|
||||
icon={<FiUsers />}
|
||||
colorScheme='blue'
|
||||
variant={active ? 'solid' : 'outline'}
|
||||
onClick={() => actionHandler('update', { field: 'isPublic', value: !active })}
|
||||
{...rest}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
PublicIconBtn.propTypes = {
|
||||
actionHandler: PropTypes.func,
|
||||
active: PropTypes.bool,
|
||||
size: PropTypes.oneOf(['xs', 'sm', 'md', 'lg']),
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
import { IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import { IoTimeOutline } from '@react-icons/all-files/io5/IoTimeOutline';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
export default function RollIconBtn(props) {
|
||||
const { clickhandler, active, disabled, ...rest } = props;
|
||||
return (
|
||||
<Tooltip label='Roll mode' openDelay={tooltipDelayMid} shouldWrapChildren={disabled}>
|
||||
<IconButton
|
||||
icon={<IoTimeOutline size='24px' />}
|
||||
colorScheme='blue'
|
||||
variant={active ? 'solid' : 'outline'}
|
||||
onClick={clickhandler}
|
||||
width={120}
|
||||
disabled={disabled}
|
||||
{...rest}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
RollIconBtn.propTypes = {
|
||||
clickhandler: PropTypes.func,
|
||||
active: PropTypes.bool,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
@@ -1,28 +0,0 @@
|
||||
import { IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
export default function StartIconBtn(props) {
|
||||
const { clickhandler, active, disabled, ...rest } = props;
|
||||
return (
|
||||
<Tooltip label='Start timer' openDelay={tooltipDelayMid} shouldWrapChildren={disabled}>
|
||||
<IconButton
|
||||
icon={<IoPlay size='24px' />}
|
||||
colorScheme='green'
|
||||
variant={active ? 'solid' : 'outline'}
|
||||
onClick={clickhandler}
|
||||
width={120}
|
||||
disabled={disabled}
|
||||
{...rest}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
StartIconBtn.propTypes = {
|
||||
clickhandler: PropTypes.func,
|
||||
active: PropTypes.bool,
|
||||
disabled: PropTypes.bool
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
export default function TransportIconBtn(props) {
|
||||
const { clickHandler, icon, tooltip, disabled, ...rest } = props;
|
||||
return (
|
||||
<Tooltip label={tooltip} openDelay={tooltipDelayMid} shouldWrapChildren={disabled}>
|
||||
<IconButton
|
||||
icon={icon}
|
||||
colorScheme='white'
|
||||
variant='outline'
|
||||
_hover={!disabled && { bg: '#ebedf0', color: '#333' }}
|
||||
onClick={clickHandler}
|
||||
width={90}
|
||||
disabled={disabled}
|
||||
{...rest}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
TransportIconBtn.propTypes = {
|
||||
clickHandler: PropTypes.func,
|
||||
icon: PropTypes.element,
|
||||
tooltip: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
import { IconButton, Tooltip } from '@chakra-ui/react';
|
||||
import { IoStop } from '@react-icons/all-files/io5/IoStop';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
export default function UnloadIconBtn(props) {
|
||||
const { clickHandler, disabled, ...rest } = props;
|
||||
return (
|
||||
<Tooltip label='Unload event' openDelay={tooltipDelayMid} shouldWrapChildren={disabled}>
|
||||
<IconButton
|
||||
icon={<IoStop size='22px' />}
|
||||
colorScheme='red'
|
||||
variant='outline'
|
||||
onClick={clickHandler}
|
||||
width={90}
|
||||
disabled={disabled}
|
||||
{...rest}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
UnloadIconBtn.propTypes = {
|
||||
clickHandler: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Alias } from 'ontime-types';
|
||||
|
||||
export const aliasPlaceholder: Alias = {
|
||||
enabled: false,
|
||||
alias: '',
|
||||
pathAndParams: '',
|
||||
};
|
||||
@@ -1,30 +0,0 @@
|
||||
export const ontimeVars = [
|
||||
{
|
||||
name: '$timer',
|
||||
description: 'Current running timer',
|
||||
},
|
||||
{
|
||||
name: '$title',
|
||||
description: 'Current title',
|
||||
},
|
||||
{
|
||||
name: '$presenter',
|
||||
description: 'Current timer',
|
||||
},
|
||||
{
|
||||
name: '$subtitle',
|
||||
description: 'Current subtitle',
|
||||
},
|
||||
{
|
||||
name: '$next-title',
|
||||
description: 'Next title',
|
||||
},
|
||||
{
|
||||
name: '$next-presenter',
|
||||
description: 'Next timer',
|
||||
},
|
||||
{
|
||||
name: '$next-subtitle',
|
||||
description: 'Next subtitle',
|
||||
},
|
||||
];
|
||||
@@ -33,7 +33,7 @@ const sectionText: { [key in TimerLifeCycle]: { title: string; subtitle: string
|
||||
},
|
||||
onUpdate: {
|
||||
title: 'On Every Second',
|
||||
subtitle: 'Triggers when timers are updated (at least once a second, can be more)',
|
||||
subtitle: 'Triggers when a running timer is updated (at least once a second, can be more)',
|
||||
},
|
||||
onFinish: {
|
||||
title: 'On Finish',
|
||||
|
||||
@@ -2,10 +2,3 @@ export const inputProps = {
|
||||
size: 'sm',
|
||||
autoComplete: 'off',
|
||||
};
|
||||
|
||||
export const portInputProps = {
|
||||
...inputProps,
|
||||
type: 'number',
|
||||
min: '1024',
|
||||
max: '65535',
|
||||
};
|
||||
|
||||
@@ -10,6 +10,8 @@ import { clock } from './Clock.js';
|
||||
|
||||
export class TimerService {
|
||||
private readonly _interval: NodeJS.Timer;
|
||||
private _updateInterval: number;
|
||||
private _lastUpdate: number | null;
|
||||
|
||||
playback: Playback;
|
||||
timer: TimerState;
|
||||
@@ -23,10 +25,12 @@ export class TimerService {
|
||||
* @constructor
|
||||
* @param {object} [timerConfig]
|
||||
* @param {number} [timerConfig.refresh]
|
||||
* @param {number} [timerConfig.updateInterval]
|
||||
*/
|
||||
constructor(timerConfig?) {
|
||||
constructor(timerConfig: { refresh?: number; updateInterval?: number } = {}) {
|
||||
this._clear();
|
||||
this._interval = setInterval(() => this.update(), timerConfig?.refresh || 1000);
|
||||
this._interval = setInterval(() => this.update(), timerConfig?.refresh ?? 1000);
|
||||
this._updateInterval = timerConfig?.updateInterval ?? 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -53,6 +57,8 @@ export class TimerService {
|
||||
this.pausedTime = 0;
|
||||
this.pausedAt = null;
|
||||
this.secondaryTarget = null;
|
||||
|
||||
this._lastUpdate = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,7 +99,7 @@ export class TimerService {
|
||||
if (this.timer.startedAt === null) {
|
||||
this.timer.current = timer.duration;
|
||||
}
|
||||
this.update();
|
||||
this.update(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,77 +246,95 @@ export class TimerService {
|
||||
}
|
||||
|
||||
// force an update
|
||||
this.update();
|
||||
this.update(true);
|
||||
}
|
||||
|
||||
update() {
|
||||
private updateRoll() {
|
||||
const tempCurrentTimer = {
|
||||
selectedEventId: this.loadedTimerId,
|
||||
current: this.timer.current,
|
||||
// safeguard on midnight rollover
|
||||
_finishAt:
|
||||
this.timer.expectedFinish >= this.timer.startedAt
|
||||
? this.timer.expectedFinish
|
||||
: this.timer.expectedFinish + DAY_TO_MS,
|
||||
|
||||
clock: this.timer.clock,
|
||||
secondaryTimer: this.timer.secondaryTimer,
|
||||
secondaryTarget: this.secondaryTarget,
|
||||
};
|
||||
const { updatedTimer, updatedSecondaryTimer, doRollLoad, isFinished } = updateRoll(tempCurrentTimer);
|
||||
|
||||
this.timer.current = updatedTimer;
|
||||
this.timer.secondaryTimer = updatedSecondaryTimer;
|
||||
this.timer.elapsed = getElapsed(this.timer.startedAt, this.timer.clock);
|
||||
|
||||
if (isFinished) {
|
||||
this.timer.selectedEventId = null;
|
||||
this.loadedTimerId = null;
|
||||
this._onFinish();
|
||||
}
|
||||
|
||||
// to load the next event we have to escalate to parent service
|
||||
if (doRollLoad) {
|
||||
PlaybackService.roll();
|
||||
}
|
||||
}
|
||||
|
||||
private updatePlay() {
|
||||
if (this.playback === Playback.Pause) {
|
||||
this.pausedTime = this.timer.clock - this.pausedAt;
|
||||
}
|
||||
|
||||
if (this.playback === Playback.Play && this.timer.current <= 0 && this.timer.finishedAt === null) {
|
||||
this.timer.finishedAt = this.timer.clock;
|
||||
this._onFinish();
|
||||
} else {
|
||||
this.timer.expectedFinish = getExpectedFinish(
|
||||
this.timer.startedAt,
|
||||
this.timer.finishedAt,
|
||||
this.timer.duration,
|
||||
this.pausedTime,
|
||||
this.timer.addedTime,
|
||||
);
|
||||
}
|
||||
this.timer.current = getCurrent(
|
||||
this.timer.startedAt,
|
||||
this.timer.duration,
|
||||
this.timer.addedTime,
|
||||
this.pausedTime,
|
||||
this.timer.clock,
|
||||
);
|
||||
this.timer.elapsed = getElapsed(this.timer.startedAt, this.timer.clock);
|
||||
}
|
||||
|
||||
update(force = false) {
|
||||
this.timer.clock = clock.timeNow();
|
||||
|
||||
// we call integrations if we update timers
|
||||
let shouldNotify = false;
|
||||
if (this.playback === Playback.Roll) {
|
||||
const tempCurrentTimer = {
|
||||
selectedEventId: this.loadedTimerId,
|
||||
current: this.timer.current,
|
||||
// safeguard on midnight rollover
|
||||
_finishAt:
|
||||
this.timer.expectedFinish >= this.timer.startedAt
|
||||
? this.timer.expectedFinish
|
||||
: this.timer.expectedFinish + DAY_TO_MS,
|
||||
|
||||
clock: this.timer.clock,
|
||||
secondaryTimer: this.timer.secondaryTimer,
|
||||
secondaryTarget: this.secondaryTarget,
|
||||
};
|
||||
const { updatedTimer, updatedSecondaryTimer, doRollLoad, isFinished } = updateRoll(tempCurrentTimer);
|
||||
|
||||
this.timer.current = updatedTimer;
|
||||
this.timer.secondaryTimer = updatedSecondaryTimer;
|
||||
this.timer.elapsed = getElapsed(this.timer.startedAt, this.timer.clock);
|
||||
|
||||
if (isFinished) {
|
||||
this.timer.selectedEventId = null;
|
||||
this.loadedTimerId = null;
|
||||
this._onFinish();
|
||||
}
|
||||
|
||||
// to load the next event we have to escalate to parent service
|
||||
if (doRollLoad) {
|
||||
PlaybackService.roll();
|
||||
}
|
||||
} else {
|
||||
shouldNotify = true;
|
||||
this.updateRoll();
|
||||
} else if (this.timer.startedAt !== null) {
|
||||
// we only update timer if a timer has been started
|
||||
if (this.timer.startedAt !== null) {
|
||||
if (this.playback === Playback.Pause) {
|
||||
this.pausedTime = this.timer.clock - this.pausedAt;
|
||||
}
|
||||
|
||||
if (this.playback === Playback.Play && this.timer.current <= 0 && this.timer.finishedAt === null) {
|
||||
this.timer.finishedAt = this.timer.clock;
|
||||
this._onFinish();
|
||||
} else {
|
||||
this.timer.expectedFinish = getExpectedFinish(
|
||||
this.timer.startedAt,
|
||||
this.timer.finishedAt,
|
||||
this.timer.duration,
|
||||
this.pausedTime,
|
||||
this.timer.addedTime,
|
||||
);
|
||||
}
|
||||
this.timer.current = getCurrent(
|
||||
this.timer.startedAt,
|
||||
this.timer.duration,
|
||||
this.timer.addedTime,
|
||||
this.pausedTime,
|
||||
this.timer.clock,
|
||||
);
|
||||
this.timer.elapsed = getElapsed(this.timer.startedAt, this.timer.clock);
|
||||
}
|
||||
shouldNotify = true;
|
||||
this.updatePlay();
|
||||
}
|
||||
|
||||
// we only update the store at the updateInterval
|
||||
// side effects such as onFinish will still be triggered in the update functions
|
||||
if (force || this.timer.clock > this._lastUpdate + this._updateInterval) {
|
||||
this._lastUpdate = this.timer.clock;
|
||||
this._onUpdate(shouldNotify);
|
||||
}
|
||||
this._onUpdate();
|
||||
}
|
||||
|
||||
_onUpdate() {
|
||||
_onUpdate(shouldNotify: boolean) {
|
||||
eventStore.set('timer', this.timer);
|
||||
integrationService.dispatch(TimerLifeCycle.onUpdate);
|
||||
if (shouldNotify) {
|
||||
integrationService.dispatch(TimerLifeCycle.onUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
_onFinish() {
|
||||
@@ -358,7 +382,7 @@ export class TimerService {
|
||||
}
|
||||
this.playback = Playback.Roll;
|
||||
this._onRoll();
|
||||
this.update();
|
||||
this.update(true);
|
||||
}
|
||||
|
||||
_onRoll() {
|
||||
@@ -370,4 +394,5 @@ export class TimerService {
|
||||
}
|
||||
}
|
||||
|
||||
export const eventTimer = new TimerService();
|
||||
// calculate at 30fps, refresh at 1fps
|
||||
export const eventTimer = new TimerService({ refresh: 32, updateInterval: 1000 });
|
||||
|
||||
Reference in New Issue
Block a user