mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-05 23:43:57 +00:00
v2 event timer (#274)
* lint: cleanup unused * refactor: rename component to avoid conflict * style: gap in element row * refactor: rename playstate -> playback * refactor: cleanup usages of timer and prepare integration manager * style: re-arrange button order * refactor: improve event loading * feat(timer-service): hot reload * fix: issue with duration input * chore: cleanup debug * refactor: resolve poll from runtime store * refactor: cleanup merge * refactor: small improvements in timer hot-reload
This commit is contained in:
@@ -33,7 +33,6 @@ export default function DelayInput(props: DelayInputProps) {
|
||||
*/
|
||||
const validate = useCallback(
|
||||
(newValue?: string) => {
|
||||
console.log('debug', newValue, typeof newValue);
|
||||
if (newValue === '') setValue(0);
|
||||
const delayValue = clamp(Number(newValue), -60, 60);
|
||||
|
||||
|
||||
+29
-26
@@ -1,20 +1,31 @@
|
||||
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { KeyboardEvent, useCallback, useContext, useEffect, useRef, useState } from 'react';
|
||||
import { Button, Input, InputGroup, InputLeftElement, Tooltip } from '@chakra-ui/react';
|
||||
import { LoggingContext } from 'common/context/LoggingContext';
|
||||
import { forgivingStringToMillis } from 'common/utils/dateConfig';
|
||||
import { stringFromMillis } from 'common/utils/time';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { EventEditorSubmitActions } from '../../../../features/event-editor/EventEditor';
|
||||
import { tooltipDelayFast } from '../../../../ontimeConfig';
|
||||
import { TimeEntryField } from '../../../utils/timesManager';
|
||||
|
||||
import style from './TimeInput.module.scss';
|
||||
|
||||
export default function TimeInput(props) {
|
||||
interface TimeInputProps {
|
||||
name: TimeEntryField;
|
||||
submitHandler: (field: EventEditorSubmitActions, value: number) => void;
|
||||
time?: number;
|
||||
delay?: number;
|
||||
placeholder: string;
|
||||
validationHandler: (entry: TimeEntryField, val: number) => boolean;
|
||||
previousEnd?: number;
|
||||
}
|
||||
|
||||
export default function TimeInput(props: TimeInputProps) {
|
||||
const {
|
||||
name, submitHandler, time = 0, delay, placeholder, validationHandler, previousEnd,
|
||||
name, submitHandler, time = 0, delay = 0, placeholder, validationHandler, previousEnd = 0,
|
||||
} = props;
|
||||
const { emitError } = useContext(LoggingContext);
|
||||
const inputRef = useRef(null);
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
/**
|
||||
@@ -25,7 +36,7 @@ export default function TimeInput(props) {
|
||||
try {
|
||||
setValue(stringFromMillis(time + delay));
|
||||
} catch (error) {
|
||||
emitError(`Unable to parse date: ${error.text}`);
|
||||
emitError(`Unable to parse date: ${error}`);
|
||||
}
|
||||
}, [delay, emitError, time]);
|
||||
|
||||
@@ -33,14 +44,14 @@ export default function TimeInput(props) {
|
||||
* @description Selects input text on focus
|
||||
*/
|
||||
const handleFocus = useCallback(() => {
|
||||
inputRef.current.select();
|
||||
inputRef.current?.select();
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* @description Submit handler
|
||||
* @param {string} newValue
|
||||
*/
|
||||
const handleSubmit = useCallback((newValue) => {
|
||||
const handleSubmit = useCallback((newValue: string) => {
|
||||
// Check if there is anything there
|
||||
if (newValue === '') {
|
||||
return false;
|
||||
@@ -82,7 +93,7 @@ export default function TimeInput(props) {
|
||||
* @description Prepare time fields
|
||||
* @param {string} value string to be parsed
|
||||
*/
|
||||
const validateAndSubmit = useCallback((newValue) => {
|
||||
const validateAndSubmit = useCallback((newValue: string) => {
|
||||
const success = handleSubmit(newValue);
|
||||
if (success) {
|
||||
const ms = forgivingStringToMillis(newValue);
|
||||
@@ -96,15 +107,15 @@ export default function TimeInput(props) {
|
||||
* @description Handles common keys for submit and cancel
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
const onKeyDownHandler = useCallback((event) => {
|
||||
const onKeyDownHandler = useCallback((event:KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === 'Enter') {
|
||||
inputRef.current.blur();
|
||||
validateAndSubmit(event.target.value);
|
||||
inputRef.current?.blur();
|
||||
validateAndSubmit((event.target as HTMLInputElement).value);
|
||||
} else if (event.key === 'Tab') {
|
||||
validateAndSubmit(event.target.value);
|
||||
validateAndSubmit((event.target as HTMLInputElement).value);
|
||||
}
|
||||
if (event.key === 'Escape') {
|
||||
inputRef.current.blur();
|
||||
inputRef.current?.blur();
|
||||
resetValue();
|
||||
}
|
||||
}, [resetValue, validateAndSubmit]);
|
||||
@@ -119,13 +130,15 @@ export default function TimeInput(props) {
|
||||
const ButtonInitial = () => {
|
||||
if (name === 'timeStart') return 'S';
|
||||
if (name === 'timeEnd') return 'E';
|
||||
if (name === 'duration') return 'D';
|
||||
if (name === 'durationOverride') return 'D';
|
||||
return '';
|
||||
};
|
||||
|
||||
const ButtonTooltip = () => {
|
||||
if (name === 'timeStart') return 'Start';
|
||||
if (name === 'timeEnd') return 'End';
|
||||
if (name === 'duration') return 'Duration';
|
||||
if (name === 'durationOverride') return 'Duration';
|
||||
return '';
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -162,13 +175,3 @@ export default function TimeInput(props) {
|
||||
</InputGroup>
|
||||
);
|
||||
}
|
||||
|
||||
TimeInput.propTypes = {
|
||||
name: PropTypes.string,
|
||||
submitHandler: PropTypes.func,
|
||||
time: PropTypes.number,
|
||||
delay: PropTypes.number,
|
||||
placeholder: PropTypes.string,
|
||||
validationHandler: PropTypes.func,
|
||||
previousEnd: PropTypes.number,
|
||||
};
|
||||
@@ -71,7 +71,6 @@ export const useEventAction = () => {
|
||||
}
|
||||
|
||||
if (applicationOptions.startTimeIsLastEnd && applicationOptions?.lastEventId) {
|
||||
console.log('debug got here', applicationOptions.startTimeIsLastEnd, typeof applicationOptions.startTimeIsLastEnd);
|
||||
const rundown = queryClient.getQueryData(RUNDOWN_TABLE) as OntimeRundown;
|
||||
const previousEvent = rundown.find((event) => event.id === applicationOptions.lastEventId);
|
||||
if (typeof previousEvent !== 'undefined' && previousEvent.type === 'event') {
|
||||
|
||||
@@ -71,7 +71,6 @@ export const emptyPlaybackControl = {
|
||||
export const usePlaybackControl = createSocketHook(FEAT_PLAYBACKCONTROL, emptyPlaybackControl);
|
||||
export const resetPlayback = () => {
|
||||
const cacheData = queryClient.getQueryData([FEAT_PLAYBACKCONTROL]) as Record<string, unknown>;
|
||||
|
||||
queryClient.setQueryData([FEAT_PLAYBACKCONTROL], {
|
||||
...cacheData,
|
||||
playback: 'stop',
|
||||
@@ -84,19 +83,15 @@ export const setPlayback = {
|
||||
roll: () => socket.emit('set-roll'),
|
||||
previous: () => {
|
||||
socket.emit('set-previous');
|
||||
resetPlayback();
|
||||
},
|
||||
next: () => {
|
||||
socket.emit('set-next');
|
||||
resetPlayback();
|
||||
},
|
||||
stop: () => {
|
||||
socket.emit('set-stop');
|
||||
resetPlayback();
|
||||
},
|
||||
reload: () => {
|
||||
socket.emit('set-reload');
|
||||
resetPlayback();
|
||||
},
|
||||
delay: (amount: number) => {
|
||||
socket.emit('set-delay', amount);
|
||||
|
||||
@@ -26,9 +26,9 @@ export type OntimeEvent = OntimeBaseEvent & {
|
||||
subtitle: string,
|
||||
presenter: string,
|
||||
note: string,
|
||||
timeType?: string,
|
||||
timeStart: number,
|
||||
timeEnd: number,
|
||||
timeType?: string,
|
||||
duration: number,
|
||||
isPublic: boolean,
|
||||
skip: boolean,
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export type Playstate = 'roll' | 'start' | 'pause' | 'stop';
|
||||
export type Playback = 'roll' | 'play' | 'pause' | 'stop' | 'armed';
|
||||
export type TimeFormat = '12' | '24';
|
||||
|
||||
@@ -10,7 +10,6 @@ describe('cx()', () => {
|
||||
test('ignores falsy values', () => {
|
||||
const falsyStuff = false;
|
||||
const merged = cx([undefined, false, 0, null, falsyStuff ? style.test : null]);
|
||||
console.log(merged)
|
||||
expect(merged).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,19 +35,11 @@ export const handleTimeEntry = (field: TimeEntryField, val: number, timeStart: n
|
||||
export const validateEntry = (field: TimeEntryField, value: number, timeStart: number, timeEnd: number): { value: boolean, catch: string } => {
|
||||
const validate = { value: true, catch: '' };
|
||||
|
||||
// 1. if one of times is not entered, anything goes
|
||||
if (value == null || timeStart == null || timeEnd == null) return validate;
|
||||
if (timeStart === 0) return validate;
|
||||
const { start, end } = handleTimeEntry(field, value, timeStart, timeEnd);
|
||||
|
||||
// 2. find out what's what
|
||||
const { start, end, durationOverride } = handleTimeEntry(field, value, timeStart, timeEnd);
|
||||
if (durationOverride !== null) {
|
||||
return validate;
|
||||
}
|
||||
|
||||
// 3. validation rules
|
||||
if (start > end) {
|
||||
if (end < start) {
|
||||
validate.catch = 'Start time later than end time';
|
||||
}
|
||||
|
||||
return validate;
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
import { Playback } from '../../../common/models/OntimeTypes';
|
||||
|
||||
import Playback from './Playback';
|
||||
import PlaybackDisplay from './PlaybackDisplay';
|
||||
import Transport from './Transport';
|
||||
|
||||
interface PlaybackButtonsProps {
|
||||
playback: Playstate;
|
||||
playback: Playback;
|
||||
selectedId: string | null;
|
||||
noEvents: boolean;
|
||||
}
|
||||
@@ -13,7 +13,7 @@ export default function PlaybackButtons(props: PlaybackButtonsProps) {
|
||||
const { playback, selectedId, noEvents } = props;
|
||||
return (
|
||||
<>
|
||||
<Playback
|
||||
<PlaybackDisplay
|
||||
playback={playback}
|
||||
selectedId={selectedId}
|
||||
noEvents={noEvents}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { usePlaybackControl } from '../../../common/hooks/useSocket';
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
import { Playback } from '../../../common/models/OntimeTypes';
|
||||
|
||||
import PlaybackButtons from './PlaybackButtons';
|
||||
import PlaybackTimer from './PlaybackTimer';
|
||||
@@ -12,7 +12,7 @@ export default function PlaybackControl() {
|
||||
return (
|
||||
<div className={style.mainContainer}>
|
||||
<PlaybackTimer
|
||||
playback={data.playback as Playstate}
|
||||
playback={data.playback as Playback}
|
||||
selectedId={data.selectedEventId}
|
||||
/>
|
||||
<PlaybackButtons
|
||||
|
||||
+8
-7
@@ -3,30 +3,31 @@ import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
|
||||
import { IoTimeOutline } from '@react-icons/all-files/io5/IoTimeOutline';
|
||||
|
||||
import { setPlayback } from '../../../common/hooks/useSocket';
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
import { Playback } from '../../../common/models/OntimeTypes';
|
||||
|
||||
import TapButton from './TapButton';
|
||||
|
||||
import style from './PlaybackControl.module.scss';
|
||||
|
||||
interface PlaybackProps {
|
||||
playback: Playstate;
|
||||
playback: Playback;
|
||||
selectedId: string | null;
|
||||
noEvents: boolean;
|
||||
}
|
||||
|
||||
export default function Playback(props: PlaybackProps) {
|
||||
export default function PlaybackDisplay(props: PlaybackProps) {
|
||||
const { playback, selectedId, noEvents } = props;
|
||||
const isRolling = playback === 'roll';
|
||||
const isPlaying = playback === 'start';
|
||||
const isPlaying = playback === 'play';
|
||||
const isPaused = playback === 'pause';
|
||||
const isArmed = playback === 'armed';
|
||||
|
||||
return (
|
||||
<div className={style.playbackContainer}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.start()}
|
||||
disabled={!selectedId || isRolling || noEvents}
|
||||
theme='start'
|
||||
disabled={!selectedId || isRolling}
|
||||
theme='play'
|
||||
active={isPlaying}
|
||||
>
|
||||
<IoPlay />
|
||||
@@ -34,7 +35,7 @@ export default function Playback(props: PlaybackProps) {
|
||||
|
||||
<TapButton
|
||||
onClick={() => setPlayback.pause()}
|
||||
disabled={!selectedId || isRolling || noEvents}
|
||||
disabled={!selectedId || isRolling || isArmed}
|
||||
theme='pause'
|
||||
active={isPaused}
|
||||
>
|
||||
@@ -2,7 +2,7 @@ import { Tooltip } from '@chakra-ui/react';
|
||||
import TimerDisplay from 'common/components/countdown/TimerDisplay';
|
||||
|
||||
import { setPlayback, useTimer } from '../../../common/hooks/useSocket';
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
import { Playback } from '../../../common/models/OntimeTypes';
|
||||
import { millisToSeconds } from '../../../common/utils/dateConfig';
|
||||
import { stringFromMillis } from '../../../common/utils/time';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
@@ -12,7 +12,7 @@ import TapButton from './TapButton';
|
||||
import style from './PlaybackControl.module.scss';
|
||||
|
||||
interface PlaybackTimerProps {
|
||||
playback: Playstate;
|
||||
playback: Playback;
|
||||
selectedId: string | null;
|
||||
}
|
||||
|
||||
@@ -59,24 +59,6 @@ export default function PlaybackTimer(props: PlaybackTimerProps) {
|
||||
</>
|
||||
)}
|
||||
<div className={style.btn}>
|
||||
<Tooltip label='Remove 5 minutes' openDelay={tooltipDelayMid}
|
||||
shouldWrapChildren={disableButtons}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.delay(-5)}
|
||||
disabled={disableButtons}
|
||||
square>
|
||||
-5
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
<Tooltip label='Add 5 minutes' openDelay={tooltipDelayMid}
|
||||
shouldWrapChildren={disableButtons}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.delay(+5)}
|
||||
disabled={disableButtons}
|
||||
square>
|
||||
5
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
<Tooltip label='Remove 1 minute' openDelay={tooltipDelayMid}
|
||||
shouldWrapChildren={disableButtons}>
|
||||
<TapButton
|
||||
@@ -92,7 +74,25 @@ export default function PlaybackTimer(props: PlaybackTimerProps) {
|
||||
onClick={() => setPlayback.delay(1)}
|
||||
disabled={disableButtons}
|
||||
square>
|
||||
1
|
||||
+1
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
<Tooltip label='Remove 5 minutes' openDelay={tooltipDelayMid}
|
||||
shouldWrapChildren={disableButtons}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.delay(-5)}
|
||||
disabled={disableButtons}
|
||||
square>
|
||||
-5
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
<Tooltip label='Add 5 minutes' openDelay={tooltipDelayMid}
|
||||
shouldWrapChildren={disableButtons}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.delay(+5)}
|
||||
disabled={disableButtons}
|
||||
square>
|
||||
+5
|
||||
</TapButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,7 @@ $button-color-white: $gray-50;
|
||||
transition-duration: $transition-time-feedback;
|
||||
display: grid;
|
||||
place-content: center;
|
||||
letter-spacing: 0.3px;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
background-color: $button-bg-gray;
|
||||
color: $theme-color;
|
||||
@@ -58,7 +58,7 @@ $button-color-white: $gray-50;
|
||||
}
|
||||
}
|
||||
|
||||
.tapButton.start {
|
||||
.tapButton.play {
|
||||
@include tap-factory($playback-start);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ForwardedRef, forwardRef, PropsWithChildren } from 'react';
|
||||
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
import { Playback } from '../../../common/models/OntimeTypes';
|
||||
|
||||
import style from './TapButton.module.scss';
|
||||
|
||||
@@ -8,7 +8,7 @@ interface TapButtonProps {
|
||||
disabled?: boolean;
|
||||
square?: boolean;
|
||||
onClick: () => void;
|
||||
theme?: Playstate | 'neutral';
|
||||
theme?: Playback | 'neutral';
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { IoReload } from '@react-icons/all-files/io5/IoReload';
|
||||
import { IoStop } from '@react-icons/all-files/io5/IoStop';
|
||||
|
||||
import { setPlayback } from '../../../common/hooks/useSocket';
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
import { Playback } from '../../../common/models/OntimeTypes';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
|
||||
import TapButton from './TapButton';
|
||||
@@ -13,7 +13,7 @@ import TapButton from './TapButton';
|
||||
import style from './PlaybackControl.module.scss';
|
||||
|
||||
interface TransportProps {
|
||||
playback: Playstate;
|
||||
playback: Playback;
|
||||
selectedId: string | null;
|
||||
noEvents: boolean;
|
||||
}
|
||||
@@ -43,7 +43,7 @@ export default function Transport(props: TransportProps) {
|
||||
<Tooltip label='Reload event' openDelay={tooltipDelayMid}>
|
||||
<TapButton
|
||||
onClick={() => setPlayback.reload()}
|
||||
disabled={!selectedId || noEvents}
|
||||
disabled={!selectedId || isRolling}
|
||||
>
|
||||
<IoReload className={style.invertX} />
|
||||
</TapButton>
|
||||
|
||||
@@ -21,6 +21,7 @@ import style from './EventEditor.module.scss';
|
||||
|
||||
export type EventEditorSubmitActions = keyof OntimeEvent | 'durationOverride';
|
||||
|
||||
// Todo: add previous end to TimeInput fields
|
||||
export default function EventEditor() {
|
||||
const [openId] = useAtom(editorEventId);
|
||||
const { data } = useRundown();
|
||||
@@ -45,7 +46,7 @@ export default function EventEditor() {
|
||||
}, [data, event, openId]);
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
(field: EventEditorSubmitActions, value: any) => {
|
||||
(field: EventEditorSubmitActions, value: string | number) => {
|
||||
if (event === null) {
|
||||
return;
|
||||
}
|
||||
@@ -53,7 +54,8 @@ export default function EventEditor() {
|
||||
switch (field) {
|
||||
case 'durationOverride': {
|
||||
// duration defines timeEnd
|
||||
newEventData.timeEnd = event.timeStart += value as number;
|
||||
newEventData.duration = value as number;
|
||||
newEventData.timeEnd = event.timeStart + (value as number);
|
||||
break;
|
||||
}
|
||||
case 'timeStart': {
|
||||
@@ -149,11 +151,10 @@ export default function EventEditor() {
|
||||
/>
|
||||
<label className={style.inputLabel}>Duration</label>
|
||||
<TimeInput
|
||||
name='duration'
|
||||
name='durationOverride'
|
||||
submitHandler={handleSubmit}
|
||||
validationHandler={timerValidationHandler}
|
||||
time={event.duration}
|
||||
delay={delay}
|
||||
placeholder='Duration'
|
||||
/>
|
||||
</div>
|
||||
@@ -206,8 +207,8 @@ export default function EventEditor() {
|
||||
<label className={style.inputLabel}>Colour</label>
|
||||
<div className={style.inline}>
|
||||
<ColourInput
|
||||
name="colour"
|
||||
value={event?.colour}
|
||||
name='colour'
|
||||
handleChange={handleSubmit}
|
||||
/>
|
||||
<Button
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: $section-spacing;
|
||||
row-gap: $element-inner-spacing;
|
||||
|
||||
.interface {
|
||||
@include action-link;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
import { LoggingContext } from 'common/context/LoggingContext';
|
||||
import { useEventAction } from 'common/hooks/useEventAction';
|
||||
import { OntimeEvent, OntimeRundownEntry, SupportedEvent } from 'common/models/EventTypes';
|
||||
import { Playstate } from 'common/models/OntimeTypes';
|
||||
import { Playback } from 'common/models/OntimeTypes';
|
||||
import { cloneEvent } from 'common/utils/eventsManager';
|
||||
import { calculateDuration } from 'common/utils/timesManager';
|
||||
import { useAtom, useAtomValue } from 'jotai';
|
||||
@@ -38,7 +38,7 @@ interface RundownEntryProps {
|
||||
delay: number;
|
||||
previousEnd: number;
|
||||
previousEventId?: string;
|
||||
playback?: Playstate; // we only care about this if this event is playing
|
||||
playback?: Playback; // we only care about this if this event is playing
|
||||
}
|
||||
|
||||
export default function RundownEntry(props: RundownEntryProps) {
|
||||
@@ -63,7 +63,7 @@ export default function RundownEntry(props: RundownEntryProps) {
|
||||
|
||||
// Create / delete new events
|
||||
type FieldValue = {
|
||||
field: keyof OntimeEvent;
|
||||
field: keyof Omit<OntimeEvent, 'duration'> | 'durationOverride';
|
||||
value: unknown;
|
||||
}
|
||||
const actionHandler = useCallback(
|
||||
@@ -109,9 +109,10 @@ export default function RundownEntry(props: RundownEntryProps) {
|
||||
const { field, value } = payload as FieldValue;
|
||||
const newData: Partial<OntimeEvent> = { id: data.id };
|
||||
|
||||
if (field === 'duration' && data.type === SupportedEvent.Event) {
|
||||
if (field === 'durationOverride' && data.type === SupportedEvent.Event) {
|
||||
// duration defines timeEnd
|
||||
newData.timeEnd = data.timeStart += value as number;
|
||||
newData.duration = value as number;
|
||||
newData.timeEnd = data.timeStart + (value as number);
|
||||
updateEvent(newData);
|
||||
} else if (field === 'timeStart' && data.type === SupportedEvent.Event) {
|
||||
newData.duration = calculateDuration(value as number, data.timeEnd);
|
||||
|
||||
@@ -17,7 +17,7 @@ import { useAtom } from 'jotai';
|
||||
|
||||
import { useEventAction } from '../../../common/hooks/useEventAction';
|
||||
import { setEventPlayback } from '../../../common/hooks/useSocket';
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
import { Playback } from '../../../common/models/OntimeTypes';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
import { EventItemActions } from '../RundownEntry';
|
||||
|
||||
@@ -52,7 +52,7 @@ interface EventBlockProps {
|
||||
skip: boolean;
|
||||
selected: boolean;
|
||||
hasCursor: boolean;
|
||||
playback?: Playstate;
|
||||
playback?: Playback;
|
||||
actionHandler: (action: EventItemActions, payload?: any) => void;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
[title, updateEvent, eventId],
|
||||
);
|
||||
|
||||
const eventIsPlaying = selected && playback === 'start';
|
||||
const eventIsPlaying = selected && playback === 'play';
|
||||
const playBtnStyles = { _hover: {} };
|
||||
if (!skip && eventIsPlaying) {
|
||||
playBtnStyles._hover = { bg: '#c05621' };
|
||||
@@ -246,7 +246,7 @@ export default function EventBlock(props: EventBlockProps) {
|
||||
showDelay
|
||||
showBlock
|
||||
showClone
|
||||
enableDelete={!selected}
|
||||
enableDelete
|
||||
actionHandler={actionHandler}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
transition: 1s linear;
|
||||
transition-property: width;
|
||||
|
||||
&.start {
|
||||
&.play {
|
||||
background-color: $ontime-accent;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useTimer } from '../../../../common/hooks/useSocket';
|
||||
import { Playstate } from '../../../../common/models/OntimeTypes';
|
||||
import { Playback } from '../../../../common/models/OntimeTypes';
|
||||
import { clamp } from '../../../../common/utils/math';
|
||||
|
||||
import style from './EventBlockProgressBar.module.scss';
|
||||
|
||||
interface EventBlockProgressBarProps {
|
||||
playback?: Playstate;
|
||||
playback?: Playback;
|
||||
}
|
||||
|
||||
export default function EventBlockProgressBar(props: EventBlockProgressBarProps) {
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function EventBlockTimers(props) {
|
||||
const handleValidation = useCallback(
|
||||
(field, value) => {
|
||||
const valid = validateEntry(field, value, timeStart, timeEnd);
|
||||
if (!valid.value) {
|
||||
if (valid.catch) {
|
||||
emitWarning(`Time Input Warning: ${valid.catch}`);
|
||||
}
|
||||
return valid.value;
|
||||
@@ -60,11 +60,10 @@ export default function EventBlockTimers(props) {
|
||||
previousEnd={previousEnd}
|
||||
/>
|
||||
<TimeInput
|
||||
name='duration'
|
||||
name='durationOverride'
|
||||
submitHandler={handleSubmit}
|
||||
validationHandler={handleValidation}
|
||||
time={duration}
|
||||
delay={delay}
|
||||
placeholder='Duration'
|
||||
previousEnd={previousEnd}
|
||||
/>
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
align-items: center;
|
||||
margin: 4px 2px;
|
||||
margin: 4px 0;
|
||||
font-size: 12px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.btnRow {
|
||||
|
||||
@@ -42,7 +42,7 @@ const withSocket = (Component) => {
|
||||
});
|
||||
const [selectedId] = useSubscription('selected-id', null);
|
||||
const [nextId] = useSubscription('next-id', null);
|
||||
const [playback] = useSubscription('playstate', null);
|
||||
const [playback] = useSubscription('playback', null);
|
||||
|
||||
// Ask for update on load
|
||||
useEffect(() => {
|
||||
@@ -107,8 +107,8 @@ const withSocket = (Component) => {
|
||||
// get clock string
|
||||
const TimeManagerType = {
|
||||
...timer,
|
||||
finished: playback === 'start' && timer.isNegative && timer.startedAt,
|
||||
playstate: playback,
|
||||
finished: playback === 'play' && timer.isNegative && timer.startedAt,
|
||||
playback,
|
||||
};
|
||||
|
||||
// prevent render until we get all the data we need
|
||||
|
||||
@@ -77,7 +77,7 @@ export default function Countdown(props) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const standby = time.playstate !== 'start' && selectedId === follow?.id;
|
||||
const standby = time.playback !== 'start' && selectedId === follow?.id;
|
||||
const isRunningFinished = time.finished && runningMessage === timerMessages.running;
|
||||
const isSelected = runningMessage === timerMessages.running;
|
||||
const delayedTimerStyles = delay > 0 ? 'aux-timers__value--delayed' : '';
|
||||
|
||||
@@ -32,7 +32,7 @@ export const fetchTimerData = (time, follow, selectedId) => {
|
||||
|
||||
if (selectedId === follow.id) {
|
||||
// check that is not running
|
||||
message = time.playstate === 'pause' ? timerMessages.waiting : timerMessages.running;
|
||||
message = time.playback === 'pause' ? timerMessages.waiting : timerMessages.running;
|
||||
timer = time.running;
|
||||
} else if (time.clock < follow.timeStart) {
|
||||
// if it hasnt started, we count to start
|
||||
|
||||
@@ -124,7 +124,7 @@ export default function MinimalTimer(props: MinimalTimerProps) {
|
||||
userOptions.hideMessagesOverlay = Boolean(hideMessagesOverlay);
|
||||
|
||||
const showOverlay = pres.text !== '' && pres.visible;
|
||||
const isPlaying = time.playstate !== 'pause';
|
||||
const isPlaying = time.playback !== 'pause';
|
||||
const timer = formatDisplay(time.running, true);
|
||||
const clean = timer.replace('/:/g', '');
|
||||
const showFinished = time.isNegative && !userOptions?.hideOvertime;
|
||||
|
||||
+5
-337
@@ -2108,14 +2108,6 @@ abab@^2.0.6:
|
||||
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
|
||||
integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
|
||||
|
||||
accepts@~1.3.8:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
|
||||
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
|
||||
dependencies:
|
||||
mime-types "~2.1.34"
|
||||
negotiator "0.6.3"
|
||||
|
||||
acorn-globals@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
|
||||
@@ -2243,11 +2235,6 @@ aria-query@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c"
|
||||
integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==
|
||||
|
||||
array-flatten@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
|
||||
|
||||
array-includes@^3.1.3:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9"
|
||||
@@ -2348,24 +2335,6 @@ binary-extensions@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
body-parser@1.20.1:
|
||||
version "1.20.1"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
|
||||
integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
content-type "~1.0.4"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
destroy "1.2.0"
|
||||
http-errors "2.0.0"
|
||||
iconv-lite "0.4.24"
|
||||
on-finished "2.4.1"
|
||||
qs "6.11.0"
|
||||
raw-body "2.5.1"
|
||||
type-is "~1.6.18"
|
||||
unpipe "1.0.0"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
@@ -2396,11 +2365,6 @@ browserslist@^4.21.3:
|
||||
node-releases "^2.0.6"
|
||||
update-browserslist-db "^1.0.9"
|
||||
|
||||
bytes@3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
|
||||
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
|
||||
|
||||
call-bind@^1.0.0, call-bind@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
|
||||
@@ -2598,18 +2562,6 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
|
||||
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
|
||||
|
||||
content-disposition@0.5.4:
|
||||
version "0.5.4"
|
||||
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
|
||||
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
|
||||
dependencies:
|
||||
safe-buffer "5.2.1"
|
||||
|
||||
content-type@~1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||
|
||||
convert-source-map@^1.5.0, convert-source-map@^1.7.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
|
||||
@@ -2617,16 +2569,6 @@ convert-source-map@^1.5.0, convert-source-map@^1.7.0:
|
||||
dependencies:
|
||||
safe-buffer "~5.1.1"
|
||||
|
||||
cookie-signature@1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
|
||||
|
||||
cookie@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
|
||||
cookie@^0.4.1:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
|
||||
@@ -2743,13 +2685,6 @@ data-urls@^3.0.2:
|
||||
whatwg-mimetype "^3.0.0"
|
||||
whatwg-url "^11.0.0"
|
||||
|
||||
debug@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
@@ -2831,16 +2766,6 @@ delegates@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
|
||||
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
|
||||
|
||||
depd@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
|
||||
|
||||
destroy@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
|
||||
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
|
||||
|
||||
detect-node-es@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
|
||||
@@ -2884,11 +2809,6 @@ domexception@^4.0.0:
|
||||
dependencies:
|
||||
webidl-conversions "^7.0.0"
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
||||
|
||||
electron-to-chromium@^1.4.251:
|
||||
version "1.4.255"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.255.tgz#dc52d1095b876ed8acf25865db10265b02b1d6e1"
|
||||
@@ -2899,11 +2819,6 @@ emoji-regex@^8.0.0:
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
||||
|
||||
encodeurl@~1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
||||
|
||||
engine.io-client@~6.2.3:
|
||||
version "6.2.3"
|
||||
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.3.tgz#a8cbdab003162529db85e9de31575097f6d29458"
|
||||
@@ -3138,11 +3053,6 @@ escalade@^3.1.1:
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
|
||||
|
||||
escape-html@~1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
|
||||
|
||||
escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
@@ -3339,11 +3249,6 @@ esutils@^2.0.2:
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
|
||||
|
||||
etag@~1.8.1:
|
||||
version "1.8.1"
|
||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
|
||||
|
||||
execall@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45"
|
||||
@@ -3351,43 +3256,6 @@ execall@^2.0.0:
|
||||
dependencies:
|
||||
clone-regexp "^2.1.0"
|
||||
|
||||
express@^4.18.2:
|
||||
version "4.18.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
|
||||
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
|
||||
dependencies:
|
||||
accepts "~1.3.8"
|
||||
array-flatten "1.1.1"
|
||||
body-parser "1.20.1"
|
||||
content-disposition "0.5.4"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.5.0"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
finalhandler "1.2.0"
|
||||
fresh "0.5.2"
|
||||
http-errors "2.0.0"
|
||||
merge-descriptors "1.0.1"
|
||||
methods "~1.1.2"
|
||||
on-finished "2.4.1"
|
||||
parseurl "~1.3.3"
|
||||
path-to-regexp "0.1.7"
|
||||
proxy-addr "~2.0.7"
|
||||
qs "6.11.0"
|
||||
range-parser "~1.2.1"
|
||||
safe-buffer "5.2.1"
|
||||
send "0.18.0"
|
||||
serve-static "1.15.0"
|
||||
setprototypeof "1.2.0"
|
||||
statuses "2.0.1"
|
||||
type-is "~1.6.18"
|
||||
utils-merge "1.0.1"
|
||||
vary "~1.1.2"
|
||||
|
||||
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
@@ -3440,19 +3308,6 @@ fill-range@^7.0.1:
|
||||
dependencies:
|
||||
to-regex-range "^5.0.1"
|
||||
|
||||
finalhandler@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
|
||||
integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
on-finished "2.4.1"
|
||||
parseurl "~1.3.3"
|
||||
statuses "2.0.1"
|
||||
unpipe "~1.0.0"
|
||||
|
||||
find-root@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
|
||||
@@ -3508,11 +3363,6 @@ form-data@^4.0.0:
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
forwarded@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
||||
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
|
||||
|
||||
framer-motion@^7.5.3:
|
||||
version "7.6.12"
|
||||
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-7.6.12.tgz#a1f228e00da03ceb78482ae4a7fba63be39d34e4"
|
||||
@@ -3541,11 +3391,6 @@ framesync@6.1.2:
|
||||
dependencies:
|
||||
tslib "2.4.0"
|
||||
|
||||
fresh@0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
@@ -3826,17 +3671,6 @@ html-tags@^3.2.0:
|
||||
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961"
|
||||
integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==
|
||||
|
||||
http-errors@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
|
||||
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
|
||||
dependencies:
|
||||
depd "2.0.0"
|
||||
inherits "2.0.4"
|
||||
setprototypeof "1.2.0"
|
||||
statuses "2.0.1"
|
||||
toidentifier "1.0.1"
|
||||
|
||||
http-proxy-agent@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
|
||||
@@ -3854,13 +3688,6 @@ https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1:
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
iconv-lite@0.4.24:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
iconv-lite@0.6.3:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
||||
@@ -3909,7 +3736,7 @@ inflight@^1.0.4:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2, inherits@2.0.4, inherits@^2.0.4, inherits@~2.0.3:
|
||||
inherits@2, inherits@^2.0.4, inherits@~2.0.3:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
@@ -3935,11 +3762,6 @@ invariant@^2.2.4:
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
ipaddr.js@1.9.1:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
|
||||
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
|
||||
|
||||
is-arrayish@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||
@@ -4365,11 +4187,6 @@ mathml-tag-names@^2.1.3:
|
||||
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
|
||||
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
||||
|
||||
media-typer@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
|
||||
|
||||
memoize-one@^5.1.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
||||
@@ -4393,21 +4210,11 @@ meow@^9.0.0:
|
||||
type-fest "^0.18.0"
|
||||
yargs-parser "^20.2.3"
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
|
||||
|
||||
merge2@^1.3.0, merge2@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
||||
|
||||
methods@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
|
||||
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
|
||||
|
||||
micromatch@^4.0.4, micromatch@^4.0.5:
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
|
||||
@@ -4421,18 +4228,13 @@ mime-db@1.52.0:
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||
|
||||
mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
|
||||
mime-types@^2.1.12:
|
||||
version "2.1.35"
|
||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||
dependencies:
|
||||
mime-db "1.52.0"
|
||||
|
||||
mime@1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
||||
min-indent@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
|
||||
@@ -4466,21 +4268,11 @@ mkdirp@^0.5.5:
|
||||
dependencies:
|
||||
minimist "^1.2.6"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
|
||||
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
ms@2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
nanoid@^3.3.4:
|
||||
version "3.3.4"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
|
||||
@@ -4491,11 +4283,6 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
|
||||
|
||||
negotiator@0.6.3:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
|
||||
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
|
||||
|
||||
node-fetch@^2.6.7:
|
||||
version "2.6.7"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
||||
@@ -4628,13 +4415,6 @@ object.values@^1.1.5:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.19.1"
|
||||
|
||||
on-finished@2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
|
||||
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
|
||||
dependencies:
|
||||
ee-first "1.1.1"
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
@@ -4723,11 +4503,6 @@ parse5@^7.0.0:
|
||||
dependencies:
|
||||
entities "^4.4.0"
|
||||
|
||||
parseurl@~1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
||||
|
||||
path-exists@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||
@@ -4748,11 +4523,6 @@ path-parse@^1.0.6, path-parse@^1.0.7:
|
||||
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
|
||||
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
|
||||
|
||||
path-to-regexp@0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
|
||||
|
||||
path-type@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
@@ -4899,14 +4669,6 @@ prop-types@^15.8.1:
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.13.1"
|
||||
|
||||
proxy-addr@~2.0.7:
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
|
||||
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
|
||||
dependencies:
|
||||
forwarded "0.2.0"
|
||||
ipaddr.js "1.9.1"
|
||||
|
||||
proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
@@ -4927,13 +4689,6 @@ qr.js@0.0.0:
|
||||
resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f"
|
||||
integrity sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8=
|
||||
|
||||
qs@6.11.0:
|
||||
version "6.11.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
|
||||
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
|
||||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
queue-microtask@^1.2.2:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||
@@ -4949,21 +4704,6 @@ raf-schd@^4.0.2:
|
||||
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a"
|
||||
integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==
|
||||
|
||||
range-parser@~1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
||||
|
||||
raw-body@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
|
||||
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
http-errors "2.0.0"
|
||||
iconv-lite "0.4.24"
|
||||
unpipe "1.0.0"
|
||||
|
||||
react-beautiful-dnd@^13.1.1:
|
||||
version "13.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz#b0f3087a5840920abf8bb2325f1ffa46d8c4d0a2"
|
||||
@@ -4984,7 +4724,7 @@ react-clientside-effect@^1.2.6:
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.13"
|
||||
|
||||
react-dom@^18.2.0:
|
||||
react-dom@^18.1.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
||||
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
||||
@@ -5121,7 +4861,7 @@ react-test-renderer@^18.1.0:
|
||||
react-shallow-renderer "^16.15.0"
|
||||
scheduler "^0.22.0"
|
||||
|
||||
react@^18.2.0:
|
||||
react@^18.1.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
||||
@@ -5291,17 +5031,12 @@ run-parallel@^1.1.9:
|
||||
dependencies:
|
||||
queue-microtask "^1.2.2"
|
||||
|
||||
safe-buffer@5.2.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
|
||||
"safer-buffer@>= 2.1.2 < 3.0.0":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
@@ -5360,45 +5095,11 @@ semver@^7.3.5:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
send@0.18.0:
|
||||
version "0.18.0"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
|
||||
integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
destroy "1.2.0"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
fresh "0.5.2"
|
||||
http-errors "2.0.0"
|
||||
mime "1.6.0"
|
||||
ms "2.1.3"
|
||||
on-finished "2.4.1"
|
||||
range-parser "~1.2.1"
|
||||
statuses "2.0.1"
|
||||
|
||||
serve-static@1.15.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
|
||||
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
|
||||
dependencies:
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
parseurl "~1.3.3"
|
||||
send "0.18.0"
|
||||
|
||||
set-blocking@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
|
||||
|
||||
setprototypeof@1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
|
||||
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||
@@ -5518,11 +5219,6 @@ spdx-license-ids@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95"
|
||||
integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==
|
||||
|
||||
statuses@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
|
||||
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
|
||||
|
||||
string-width@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
|
||||
@@ -5845,11 +5541,6 @@ toggle-selection@^1.0.6:
|
||||
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
|
||||
integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI=
|
||||
|
||||
toidentifier@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
|
||||
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
|
||||
|
||||
tough-cookie@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
|
||||
@@ -5956,14 +5647,6 @@ type-fest@^0.8.1:
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
|
||||
|
||||
type-is@~1.6.18:
|
||||
version "1.6.18"
|
||||
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
|
||||
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
|
||||
dependencies:
|
||||
media-typer "0.3.0"
|
||||
mime-types "~2.1.24"
|
||||
|
||||
typeface-open-sans@^1.1.13:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/typeface-open-sans/-/typeface-open-sans-1.1.13.tgz#32a09ebd7df59601e01ad81216f98ce641eeafd1"
|
||||
@@ -5999,11 +5682,6 @@ universalify@^0.1.2:
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
|
||||
|
||||
unplugin@0.10.1:
|
||||
version "0.10.1"
|
||||
resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-0.10.1.tgz#e00dc951c1901aef4124121057102a8c290e28b3"
|
||||
@@ -6059,11 +5737,6 @@ util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
utils-merge@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
||||
|
||||
v8-compile-cache@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
|
||||
@@ -6077,11 +5750,6 @@ validate-npm-package-license@^3.0.1:
|
||||
spdx-correct "^3.0.0"
|
||||
spdx-expression-parse "^3.0.0"
|
||||
|
||||
vary@~1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
|
||||
|
||||
vite-plugin-svgr@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-2.2.2.tgz#c5c9cb573bf455bb079550531847ddc5d2e122af"
|
||||
|
||||
@@ -49,7 +49,6 @@ let tray = null;
|
||||
// Start OSC Server
|
||||
await startOSCServer();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
loaded = error;
|
||||
}
|
||||
})();
|
||||
|
||||
+30
-33
@@ -1,33 +1,26 @@
|
||||
// get environment vars
|
||||
import 'dotenv/config';
|
||||
|
||||
// import config
|
||||
import { config } from './config/config.js';
|
||||
|
||||
// import dependencies
|
||||
import { dirname, join, resolve } from 'path';
|
||||
|
||||
// dependencies
|
||||
import express from 'express';
|
||||
import http from 'http';
|
||||
import cors from 'cors';
|
||||
|
||||
// import utils
|
||||
import { config } from './config/config.js';
|
||||
import { initiateOSC, shutdownOSCServer } from './controllers/OscController.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { ONTIME_VERSION } from './version.js';
|
||||
import { initSentry } from './modules/sentry.js';
|
||||
import { dirname, join, resolve } from 'path';
|
||||
|
||||
// Import Routes
|
||||
import { router as rundownRouter } from './routes/rundownRouter.js';
|
||||
import { router as eventRouter } from './routes/eventRouter.js';
|
||||
import { router as ontimeRouter } from './routes/ontimeRouter.js';
|
||||
import { router as playbackRouter } from './routes/playbackRouter.js';
|
||||
|
||||
// Global Objects
|
||||
import { EventTimer } from './classes/timer/EventTimer.js';
|
||||
import { socketProvider } from './classes/socket/SocketController.js';
|
||||
|
||||
// Start OSC server
|
||||
import { initiateOSC, shutdownOSCServer } from './controllers/OscController.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
// Services
|
||||
import { DataProvider } from './classes/data-provider/DataProvider.js';
|
||||
import { ONTIME_VERSION } from './version.js';
|
||||
import { initSentry } from './modules/sentry.js';
|
||||
import { socketProvider } from './classes/socket/SocketController.js';
|
||||
import { eventTimer } from './services/TimerService.js';
|
||||
|
||||
// get environment
|
||||
const env = process.env.NODE_ENV || 'production';
|
||||
@@ -65,7 +58,7 @@ app.use('/playback', playbackRouter);
|
||||
// serve static - css
|
||||
app.use('/external', express.static(join(__dirname, 'external')));
|
||||
|
||||
// serve static - react, in test mode we fetch the react app from module
|
||||
// serve static - react, in test mode we fetch the React app from module
|
||||
const resolvedPath = () => {
|
||||
const sameModule = '../';
|
||||
const siblingModule = '../../';
|
||||
@@ -74,6 +67,7 @@ const resolvedPath = () => {
|
||||
}
|
||||
return siblingModule;
|
||||
};
|
||||
|
||||
app.use(express.static(join(__dirname, resolvedPath(), 'client/build')));
|
||||
|
||||
app.get('*', (req, res) => {
|
||||
@@ -81,7 +75,7 @@ app.get('*', (req, res) => {
|
||||
});
|
||||
|
||||
// Implement catch all
|
||||
app.use((error, response, _next) => {
|
||||
app.use((error, response) => {
|
||||
response.status(400).send('Unhandled request');
|
||||
});
|
||||
|
||||
@@ -128,13 +122,10 @@ export const startOSCServer = async (overrideConfig = null) => {
|
||||
const server = http.createServer(app);
|
||||
|
||||
/**
|
||||
* @description Starts all necessary services
|
||||
* @param overrideConfig
|
||||
* Starts servers
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
export const startServer = async (overrideConfig = null) => {
|
||||
const { http } = DataProvider.getData();
|
||||
|
||||
export const startServer = async () => {
|
||||
// Start server
|
||||
const returnMessage = `Ontime is listening on port ${serverPort}`;
|
||||
server.listen(serverPort, '0.0.0.0');
|
||||
@@ -143,18 +134,24 @@ export const startServer = async (overrideConfig = null) => {
|
||||
await socket.initServer(server);
|
||||
socket.info('SERVER', 'Socket initialised');
|
||||
|
||||
socket.info('SERVER', returnMessage);
|
||||
socket.startListener();
|
||||
return returnMessage;
|
||||
};
|
||||
|
||||
/**
|
||||
* starts integrations
|
||||
* @param overrideConfig
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
export const startIntegrations = async (overrideConfig = null) => {
|
||||
const { http } = DataProvider.getData();
|
||||
|
||||
// OSC Config
|
||||
const oscConfig = {
|
||||
ip: oscIP,
|
||||
port: overrideConfig?.port || oscOutPort,
|
||||
};
|
||||
|
||||
// init timer
|
||||
global.timer = new EventTimer(socket, config.timer, oscConfig, http);
|
||||
|
||||
socket.info('SERVER', returnMessage);
|
||||
socket.startListener();
|
||||
return returnMessage;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -166,7 +163,7 @@ export const shutdown = async () => {
|
||||
server.close();
|
||||
|
||||
shutdownOSCServer();
|
||||
global.timer.shutdown();
|
||||
eventTimer.shutdown();
|
||||
socket.shutdown();
|
||||
};
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ export class DataProvider {
|
||||
await this.persist();
|
||||
}
|
||||
|
||||
static getRundownLenght() {
|
||||
static getRundownLength() {
|
||||
return data.rundown.length;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ export class DataProvider {
|
||||
* @param entry
|
||||
* @param index
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
static async insertEventAt(entry, index) {
|
||||
// get events
|
||||
@@ -91,7 +90,6 @@ export class DataProvider {
|
||||
* @param entry
|
||||
* @param id
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
static async insertEventAfterId(entry, id) {
|
||||
const index = [...data.rundown].findIndex((event) => event.id === id);
|
||||
|
||||
@@ -80,7 +80,7 @@ export class EventLoader {
|
||||
*/
|
||||
loadById(eventId) {
|
||||
const event = EventLoader.getEventWithId(eventId);
|
||||
return this._loadEvent(event);
|
||||
return this.loadEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,12 +90,12 @@ export class EventLoader {
|
||||
*/
|
||||
loadByIndex(eventIndex) {
|
||||
const event = EventLoader.getEventAtIndex(eventIndex);
|
||||
return this._loadEvent(event);
|
||||
return this.loadEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* finds the ID of the previous event
|
||||
* @returns {{id: string}|null}
|
||||
* finds the previous event
|
||||
* @return {object | undefined}
|
||||
*/
|
||||
findPrevious() {
|
||||
const timedEvents = EventLoader.getPlayableEvents();
|
||||
@@ -105,15 +105,16 @@ export class EventLoader {
|
||||
|
||||
// if there is no event running, go to first
|
||||
if (this.selectedEventIndex === null) {
|
||||
return { id: timedEvents[0].id };
|
||||
return timedEvents[0];
|
||||
}
|
||||
|
||||
const newIndex = this.selectedEventIndex - 1;
|
||||
return { id: timedEvents?.[newIndex].id };
|
||||
return timedEvents?.[newIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* finds the ID of the next event
|
||||
* @returns {{id: string}|null}
|
||||
* finds the next event
|
||||
* @return {object | undefined}
|
||||
*/
|
||||
findNext() {
|
||||
const timedEvents = EventLoader.getPlayableEvents();
|
||||
@@ -127,10 +128,10 @@ export class EventLoader {
|
||||
|
||||
// if there is no event running, go to first
|
||||
if (this.selectedEventIndex === null) {
|
||||
return { id: timedEvents[0].id };
|
||||
return timedEvents[0];
|
||||
}
|
||||
const newIndex = this.selectedEventIndex + 1;
|
||||
return { id: timedEvents?.[newIndex].id };
|
||||
return timedEvents?.[newIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +194,7 @@ export class EventLoader {
|
||||
* loads an event given its id
|
||||
* @param {object} event
|
||||
*/
|
||||
_loadEvent(event) {
|
||||
loadEvent(event) {
|
||||
if (typeof event === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import { messageManager } from '../message-manager/MessageManager.js';
|
||||
import { PlaybackService } from '../../services/playbackService.js';
|
||||
|
||||
import { ADDRESS_MESSAGE_CONTROL } from './socketConfig.js';
|
||||
import { eventTimer } from '../../services/TimerService.js';
|
||||
import { EventLoader, eventLoader } from '../event-loader/EventLoader.js';
|
||||
|
||||
class SocketController {
|
||||
constructor() {
|
||||
@@ -61,12 +63,16 @@ class SocketController {
|
||||
|
||||
// Todo: review in favour of features
|
||||
// send state
|
||||
socket.emit('timer', global.timer.getTimeObject());
|
||||
socket.emit('playstate', global.timer.state);
|
||||
socket.emit('selected-id', global.timer.selectedEventId);
|
||||
socket.emit('next-id', global.timer.nextEventId);
|
||||
socket.emit('publicselected-id', global.timer.selectedPublicEventId);
|
||||
socket.emit('publicnext-id', global.timer.nextPublicEventId);
|
||||
socket.emit('timer', eventTimer.timer);
|
||||
socket.emit('playback', eventTimer.playback);
|
||||
socket.emit('selected', {
|
||||
id: eventLoader.selectedEventId,
|
||||
index: eventLoader.selectedEventIndex,
|
||||
total: eventLoader.numEvents,
|
||||
});
|
||||
socket.emit('next-id', eventLoader.nextEventId);
|
||||
socket.emit('publicselected-id', eventLoader.selectedPublicEventId);
|
||||
socket.emit('publicnext-id', eventLoader.nextPublicEventId);
|
||||
|
||||
/**
|
||||
* @description handle disconnecting a user
|
||||
@@ -167,16 +173,15 @@ class SocketController {
|
||||
// general playback state, useful for external sync
|
||||
// Todo: add delayed value (will come from rundownService)
|
||||
socket.on('ontime-poll', () => {
|
||||
const timerPoll = global.timer.poll();
|
||||
const timerPoll = eventTimer.timer;
|
||||
const isDelayed = false;
|
||||
const colour = '';
|
||||
socket.emit('ontime-poll', { isDelayed, colour, ...timerPoll });
|
||||
});
|
||||
|
||||
/*******************************************/
|
||||
// playstate
|
||||
socket.on('get-playstate', () => {
|
||||
socket.emit('playstate', global.timer.state);
|
||||
socket.on('get-playback', () => {
|
||||
socket.emit('playback', eventTimer.playback);
|
||||
});
|
||||
|
||||
socket.on('get-onAir', () => {
|
||||
@@ -184,30 +189,20 @@ class SocketController {
|
||||
});
|
||||
|
||||
/*******************************************/
|
||||
// selection data
|
||||
socket.on('get-selected', () => {
|
||||
socket.emit('selected', {
|
||||
id: global.timer.selectedEventId,
|
||||
index: global.timer.selectedEventIndex,
|
||||
total: global.timer._eventlist.length,
|
||||
id: eventLoader.selectedEventId,
|
||||
index: eventLoader.selectedEventIndex,
|
||||
total: eventLoader.numEvents,
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('get-selected-id', () => {
|
||||
socket.emit('selected-id', global.timer.selectedEventId);
|
||||
});
|
||||
|
||||
socket.on('get-next-id', () => {
|
||||
socket.emit('next-id', global.timer.nextEventId);
|
||||
});
|
||||
|
||||
// title data
|
||||
socket.on('get-titles', () => {
|
||||
socket.emit('titles', global.timer.titles);
|
||||
socket.emit('titles', eventLoader.titles);
|
||||
});
|
||||
|
||||
socket.on('get-publictitles', () => {
|
||||
socket.emit('publictitles', global.timer.titlesPublic);
|
||||
socket.emit('publictitles', eventLoader.titlesPublic);
|
||||
});
|
||||
|
||||
/***********************************/
|
||||
@@ -294,33 +289,32 @@ class SocketController {
|
||||
|
||||
// 1. RUNDOWN
|
||||
socket.on('get-feat-rundown', () => {
|
||||
global.timer._broadcastFeatureRundown();
|
||||
this.broadcastFeatureRundown();
|
||||
});
|
||||
|
||||
// 2. MESSAGE CONTROL
|
||||
socket.on('get-feat-messagecontrol', () => {
|
||||
const featureData = messageManager.getAll();
|
||||
this.socket.emit(ADDRESS_MESSAGE_CONTROL, featureData);
|
||||
this.broadcastFeatureMessageControl();
|
||||
});
|
||||
|
||||
// 3. PLAYBACK CONTROL
|
||||
socket.on('get-feat-playbackcontrol', () => {
|
||||
global.timer._broadcastFeaturePlaybackControl();
|
||||
this.broadcastFeaturePlaybackControl();
|
||||
});
|
||||
|
||||
// 4. INFO
|
||||
socket.on('get-feat-info', () => {
|
||||
global.timer._broadcastFeatureInfo();
|
||||
this.broadcastFeatureInfo();
|
||||
});
|
||||
|
||||
// 5. CUE SHEET
|
||||
socket.on('get-feat-cuesheet', () => {
|
||||
global.timer._broadcastFeatureCuesheet();
|
||||
this.broadcastFeatureCuesheet();
|
||||
});
|
||||
|
||||
// 6. TIMER
|
||||
socket.on('get-ontime-timer', () => {
|
||||
global.timer._broadcastFeatureTimer();
|
||||
this.broadcastTimer();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -363,6 +357,83 @@ class SocketController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast data for Event List feature
|
||||
*/
|
||||
broadcastFeatureRundown() {
|
||||
const featureData = {
|
||||
selectedEventId: eventLoader.selectedEventId,
|
||||
nextEventId: eventLoader.nextEventId,
|
||||
playback: eventTimer.playback,
|
||||
};
|
||||
this.send('feat-rundown', featureData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast data for Message Control feature
|
||||
*/
|
||||
broadcastFeatureMessageControl() {
|
||||
const featureData = messageManager.getAll();
|
||||
this.send(ADDRESS_MESSAGE_CONTROL, featureData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast data for Playback Control feature
|
||||
*/
|
||||
broadcastFeaturePlaybackControl() {
|
||||
const featureData = {
|
||||
playback: eventTimer.playback,
|
||||
selectedEventId: eventLoader.selectedEventId,
|
||||
numEvents: EventLoader.getNumEvents(),
|
||||
};
|
||||
this.send('feat-playbackcontrol', featureData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast data for Info feature
|
||||
*/
|
||||
broadcastFeatureInfo() {
|
||||
const featureData = {
|
||||
titles: eventLoader.titles,
|
||||
playback: eventTimer.playback,
|
||||
selectedEventId: eventLoader.selectedEventId,
|
||||
selectedEventIndex: eventLoader.selectedEventIndex,
|
||||
numEvents: EventLoader.getNumEvents(),
|
||||
};
|
||||
this.send('feat-info', featureData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast data for Cuesheet feature
|
||||
*/
|
||||
broadcastFeatureCuesheet() {
|
||||
const featureData = {
|
||||
playback: eventTimer.playback,
|
||||
selectedEventId: eventLoader.selectedEventId,
|
||||
selectedEventIndex: eventLoader.selectedEventIndex,
|
||||
numEvents: EventLoader.getNumEvents(),
|
||||
titleNow: eventLoader.titles.titleNow,
|
||||
};
|
||||
this.send('feat-cuesheet', featureData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast Timer feature
|
||||
*/
|
||||
broadcastTimer() {
|
||||
const featureData = eventTimer.timer;
|
||||
this.send('ontime-timer', featureData);
|
||||
}
|
||||
|
||||
broadcastState() {
|
||||
this.broadcastFeatureRundown();
|
||||
this.broadcastFeatureMessageControl();
|
||||
this.broadcastFeaturePlaybackControl();
|
||||
this.broadcastFeatureInfo();
|
||||
this.broadcastFeatureCuesheet();
|
||||
this.broadcastTimer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message with level LOG
|
||||
* @param {string} origin
|
||||
|
||||
@@ -7,12 +7,13 @@ import { DataProvider } from '../classes/data-provider/DataProvider.js';
|
||||
import { failEmptyObjects, failIsNotArray } from '../utils/routerUtils.js';
|
||||
import { mergeObject } from '../utils/parserUtils.js';
|
||||
import { PlaybackService } from '../services/playbackService.js';
|
||||
import { runtimeState } from '../stores/EventStore.js';
|
||||
|
||||
// Create controller for GET request to '/ontime/poll'
|
||||
// Returns data for current state
|
||||
export const poll = async (req, res) => {
|
||||
try {
|
||||
const s = global.timer.poll();
|
||||
const s = runtimeState.poll();
|
||||
res.status(200).send(s);
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
|
||||
@@ -3,9 +3,9 @@ export const event = {
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
note: '',
|
||||
timeType: 'start-end',
|
||||
timeStart: 0,
|
||||
timeEnd: 0,
|
||||
timeType: 'start-end',
|
||||
duration: 0,
|
||||
isPublic: false,
|
||||
skip: false,
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
import { runtimeState } from '../stores/EventStore.js';
|
||||
|
||||
class TimerService {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {object} [timerConfig]
|
||||
* @param {number} [timerConfig.refresh]
|
||||
*/
|
||||
constructor(timerConfig) {
|
||||
this._clear();
|
||||
this._interval = setInterval(() => this.update(), timerConfig?.refresh || 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current time in ms from midnight
|
||||
* @static
|
||||
* @return {number}
|
||||
*/
|
||||
static getCurrentTime() {
|
||||
const now = new Date();
|
||||
|
||||
// extract milliseconds since midnight
|
||||
let elapsed = now.getHours() * 3600000;
|
||||
elapsed += now.getMinutes() * 60000;
|
||||
elapsed += now.getSeconds() * 1000;
|
||||
elapsed += now.getMilliseconds();
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns expected time finish
|
||||
* @private
|
||||
*/
|
||||
_getExpectedFinish() {
|
||||
if (this.timer.startedAt === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.timer.finishedAt) {
|
||||
return this.timer.finishedAt;
|
||||
}
|
||||
|
||||
return Math.max(
|
||||
this.timer.startedAt + this.timer.duration + this._pausedInterval + this.timer.addedTime,
|
||||
this.timer.startedAt
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears internal state
|
||||
* @private
|
||||
*/
|
||||
_clear() {
|
||||
this.playback = 'stop';
|
||||
this.timer = {
|
||||
clock: TimerService.getCurrentTime(),
|
||||
current: null,
|
||||
elapsed: null,
|
||||
expectedFinish: null,
|
||||
addedTime: 0,
|
||||
startedAt: null,
|
||||
finishedAt: null,
|
||||
secondaryTimer: null,
|
||||
};
|
||||
this.loadedTimer = null;
|
||||
this.loadedTimerId = null;
|
||||
this._pausedInterval = 0;
|
||||
this._pausedAt = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads information for currently running timer
|
||||
* @param timer
|
||||
*/
|
||||
hotReload(timer) {
|
||||
if (timer?.id !== this.loadedTimerId) {
|
||||
return;
|
||||
}
|
||||
if (timer?.skip) {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
// update relevant information and force update
|
||||
this.loadedTimer = timer;
|
||||
this.timer.duration = timer.duration;
|
||||
if (this.timer.startedAt === null) {
|
||||
this.timer.current = timer.duration;
|
||||
}
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads given timer to object
|
||||
* @param {object} timer
|
||||
* @param {number} timer.id
|
||||
* @param {number} timer.timeStart
|
||||
* @param {number} timer.timeEnd
|
||||
* @param {number} timer.duration
|
||||
* @param {string} timer.timeType
|
||||
* @param {boolean} timer.skip
|
||||
*/
|
||||
load(timer) {
|
||||
if (timer.skip) {
|
||||
throw new Error('Refuse load of skipped event');
|
||||
}
|
||||
|
||||
this._clear();
|
||||
|
||||
this.loadedTimer = timer;
|
||||
this.loadedTimerId = timer.id;
|
||||
this.timer.duration = timer.duration;
|
||||
this.timer.current = timer.duration;
|
||||
this.playback = 'armed';
|
||||
this._pausedInterval = 0;
|
||||
this._pausedAt = 0;
|
||||
|
||||
this._onLoad();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles side effects related to onLoad event
|
||||
* @private
|
||||
*/
|
||||
_onLoad() {
|
||||
runtimeState.set('playback', this.playback);
|
||||
runtimeState.set('ontime-timer', this.timer);
|
||||
}
|
||||
|
||||
start() {
|
||||
if (!this.loadedTimerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.playback === 'play') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.timer.clock = TimerService.getCurrentTime();
|
||||
|
||||
// add paused time
|
||||
if (this._pausedInterval) {
|
||||
this.timer.addedTime += this._pausedInterval;
|
||||
this._pausedAt = null;
|
||||
this._pausedInterval = 0;
|
||||
} else {
|
||||
this.timer.startedAt = this.timer.clock;
|
||||
}
|
||||
|
||||
this.playback = 'play';
|
||||
this.timer.expectedFinish = this._getExpectedFinish();
|
||||
this._onStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles side effects related to onStart event
|
||||
* @private
|
||||
*/
|
||||
_onStart() {
|
||||
runtimeState.set('playback', this.playback);
|
||||
runtimeState.set('ontime-timer', this.timer);
|
||||
}
|
||||
|
||||
pause() {
|
||||
if (this.playback !== 'play') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playback = 'pause';
|
||||
this.timer.clock = TimerService.getCurrentTime();
|
||||
this._pausedAt = this.timer.clock;
|
||||
this._onPause();
|
||||
}
|
||||
|
||||
_onPause() {
|
||||
runtimeState.set('playback', this.playback);
|
||||
runtimeState.set('ontime-timer', this.timer);
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.playback === 'stop') {
|
||||
return;
|
||||
}
|
||||
|
||||
this._clear();
|
||||
this._onStop();
|
||||
}
|
||||
|
||||
_onStop() {
|
||||
runtimeState.set('playback', this.playback);
|
||||
runtimeState.set('ontime-timer', this.timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delays running timer by given amount
|
||||
* @param {number} amount
|
||||
*/
|
||||
delay(amount) {
|
||||
if (!this.loadedTimerId) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.timer.addedTime += amount;
|
||||
this.timer.current += amount;
|
||||
this.timer.elapsed += amount;
|
||||
|
||||
// handle edge cases
|
||||
if (amount < 0 && Math.abs(amount) > this.timer.current) {
|
||||
if (this.timer.finishedAt === null) {
|
||||
// if we will make the clock negative
|
||||
this.timer.finishedAt = TimerService.getCurrentTime();
|
||||
}
|
||||
} else if (this.timer.current < 0 && this.timer.current + amount > 0) {
|
||||
// clock will go from negative to positive
|
||||
this.timer.finishedAt = null;
|
||||
}
|
||||
|
||||
// force an update
|
||||
this.update();
|
||||
}
|
||||
|
||||
update() {
|
||||
this.timer.clock = TimerService.getCurrentTime();
|
||||
|
||||
// we only update timer if a timer has been started
|
||||
if (this.timer.startedAt !== null) {
|
||||
if (this.playback === 'pause') {
|
||||
this._pausedInterval = this.timer.clock - this._pausedAt;
|
||||
}
|
||||
|
||||
this.timer.current =
|
||||
this.timer.startedAt +
|
||||
this.timer.duration +
|
||||
this.timer.addedTime +
|
||||
this._pausedInterval -
|
||||
this.timer.clock;
|
||||
this.timer.elapsed = this.timer.duration - this.timer.current;
|
||||
|
||||
if (this.playback === 'play' && this.timer.current <= 0 && this.timer.finishedAt === null) {
|
||||
this.timer.finishedAt = this.timer.clock;
|
||||
this._onFinish();
|
||||
} else {
|
||||
this.timer.finishedAt = null;
|
||||
}
|
||||
this.timer.expectedFinish = this._getExpectedFinish();
|
||||
}
|
||||
this._onUpdate();
|
||||
}
|
||||
|
||||
_onUpdate() {
|
||||
runtimeState.set('playback', this.playback);
|
||||
runtimeState.set('ontime-timer', this.timer);
|
||||
}
|
||||
|
||||
_onFinish() {
|
||||
runtimeState.set('playback', this.playback);
|
||||
runtimeState.set('ontime-timer', this.timer);
|
||||
}
|
||||
|
||||
roll() {
|
||||
this._onRoll();
|
||||
}
|
||||
|
||||
_onRoll() {
|
||||
throw new Error('Roll not implemented');
|
||||
}
|
||||
|
||||
shutdown() {
|
||||
clearInterval(this._interval);
|
||||
}
|
||||
}
|
||||
|
||||
export const eventTimer = new TimerService();
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
import { socketProvider } from '../classes/socket/SocketController.js';
|
||||
import { eventLoader, EventLoader } from '../classes/event-loader/EventLoader.js';
|
||||
import { eventTimer } from './TimerService.js';
|
||||
|
||||
/**
|
||||
* Service manages playback status of app
|
||||
@@ -15,18 +16,18 @@ export class PlaybackService {
|
||||
* @return {boolean} success
|
||||
*/
|
||||
static loadEvent(event) {
|
||||
let success = false;
|
||||
if (!event) {
|
||||
socketProvider.error('PLAYBACK', 'No event found');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.skip) {
|
||||
} else if (event.skip) {
|
||||
socketProvider.warning('PLAYBACK', `Refused playback of skipped event ID ${event.id}`);
|
||||
return false;
|
||||
} else {
|
||||
eventLoader.loadEvent(event);
|
||||
eventTimer.load(event);
|
||||
success = true;
|
||||
}
|
||||
global.timer.pause();
|
||||
global.timer.loadEvent(event);
|
||||
return true;
|
||||
socketProvider.broadcastState();
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,8 +94,10 @@ export class PlaybackService {
|
||||
static loadPrevious() {
|
||||
const previousEvent = eventLoader.findPrevious();
|
||||
if (previousEvent) {
|
||||
PlaybackService.loadById(previousEvent.id);
|
||||
global.timer.previous();
|
||||
const success = PlaybackService.loadEvent(previousEvent);
|
||||
if (success) {
|
||||
socketProvider.info('PLAYBACK', `Loaded event with ID ${previousEvent.id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,8 +107,10 @@ export class PlaybackService {
|
||||
static loadNext() {
|
||||
const nextEvent = eventLoader.findNext();
|
||||
if (nextEvent) {
|
||||
PlaybackService.loadById(nextEvent.id);
|
||||
global.timer.next();
|
||||
const success = PlaybackService.loadEvent(nextEvent);
|
||||
if (success) {
|
||||
socketProvider.info('PLAYBACK', `Loaded event with ID ${nextEvent.id}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,86 +118,72 @@ export class PlaybackService {
|
||||
* Starts playback on selected event
|
||||
*/
|
||||
static start() {
|
||||
if (!eventLoader.selectedEventId) {
|
||||
return;
|
||||
if (eventLoader.selectedEventId) {
|
||||
eventTimer.start();
|
||||
const newState = eventTimer.playback;
|
||||
socketProvider.info('PLAYBACK', `Play Mode ${newState}`);
|
||||
}
|
||||
const newState = global.timer.start();
|
||||
if (newState === 'start') {
|
||||
socketProvider.info('PLAYBACK', 'Play Mode Start');
|
||||
}
|
||||
socketProvider.send('playstate', newState);
|
||||
socketProvider.broadcastState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses playback on selected event
|
||||
*/
|
||||
static pause() {
|
||||
if (!eventLoader.selectedEventId) {
|
||||
return;
|
||||
if (eventLoader.selectedEventId) {
|
||||
eventTimer.pause();
|
||||
const newState = eventTimer.playback;
|
||||
socketProvider.info('PLAYBACK', `Play Mode ${newState}`);
|
||||
}
|
||||
const newState = global.timer.pause();
|
||||
if (newState === 'pause') {
|
||||
socketProvider.info('PLAYBACK', 'Play Mode Paused');
|
||||
}
|
||||
socketProvider.send('playstate', newState);
|
||||
socketProvider.broadcastState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops timer and unloads any events
|
||||
*/
|
||||
static stop() {
|
||||
if (!eventLoader.selectedEventId && global.timer.state !== 'roll') {
|
||||
return;
|
||||
if (eventLoader.selectedEventId || eventTimer.playback === 'roll') {
|
||||
eventLoader.reset();
|
||||
eventTimer.stop();
|
||||
const newState = eventTimer.playback;
|
||||
socketProvider.info('PLAYBACK', `Play Mode ${newState}`);
|
||||
}
|
||||
eventLoader.reset();
|
||||
const newState = global.timer.stop();
|
||||
if (newState === 'stop') {
|
||||
socketProvider.info('PLAYBACK', 'Play Mode Stopped');
|
||||
}
|
||||
socketProvider.send('playstate', newState);
|
||||
socketProvider.broadcastState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads current event
|
||||
*/
|
||||
static reload() {
|
||||
if (!eventLoader.selectedEventId) {
|
||||
return;
|
||||
if (eventLoader.selectedEventId) {
|
||||
this.loadById(eventLoader.selectedEventId);
|
||||
}
|
||||
const newState = global.timer.reload();
|
||||
socketProvider.info('PLAYBACK', 'Reloaded event');
|
||||
socketProvider.send('playstate', newState);
|
||||
socketProvider.broadcastState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets playback to roll
|
||||
*/
|
||||
static roll() {
|
||||
if (!EventLoader.getNumEvents()) {
|
||||
return;
|
||||
if (EventLoader.getNumEvents() && eventTimer.playback !== 'roll') {
|
||||
eventTimer.roll();
|
||||
const newState = eventTimer.playback;
|
||||
socketProvider.info('PLAYBACK', `Play Mode ${newState}`);
|
||||
socketProvider.send('playback', newState);
|
||||
}
|
||||
|
||||
if (global.timer.state === 'roll') {
|
||||
return;
|
||||
}
|
||||
|
||||
const newState = global.timer.roll();
|
||||
if (newState === 'roll') {
|
||||
socketProvider.info('PLAYBACK', 'Play Mode Roll');
|
||||
}
|
||||
socketProvider.send('playstate', newState);
|
||||
socketProvider.broadcastState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds delay to current event
|
||||
* @param {number} delayTime time in ms
|
||||
* @param {number} delayTime time in minutes
|
||||
*/
|
||||
static setDelay(delayTime) {
|
||||
if (!eventLoader.selectedEventId) {
|
||||
return;
|
||||
if (eventLoader.selectedEventId) {
|
||||
const delayInMs = delayTime * 1000 * 60;
|
||||
eventTimer.delay(delayInMs);
|
||||
socketProvider.info('PLAYBACK', `Added ${delayTime} min delay`);
|
||||
}
|
||||
const delayInMs = delayTime * 1000 * 60;
|
||||
global.timer.increment(delayInMs);
|
||||
socketProvider.info('PLAYBACK', `Added ${delayTime} min delay`);
|
||||
socketProvider.broadcastState();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,14 +7,20 @@ import {
|
||||
} from '../models/eventsDefinition.js';
|
||||
import { MAX_EVENTS } from '../settings.js';
|
||||
import { EventLoader, eventLoader } from '../classes/event-loader/EventLoader.js';
|
||||
import { eventTimer } from './TimerService.js';
|
||||
import { socketProvider } from '../classes/socket/SocketController.js';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param affectedIds
|
||||
* @returns boolean
|
||||
*/
|
||||
const affectedLoaded = (affectedIds) => {
|
||||
const now = eventLoader.selectedEventId;
|
||||
const nowPublic = eventLoader.selectedPublicEventId;
|
||||
const next = eventLoader.nextEventId;
|
||||
const nextPublic = eventLoader.nextPublicEventId;
|
||||
return (
|
||||
affectedIds.includes(now) ||
|
||||
affectedIds.includes(now) ||
|
||||
affectedIds.includes(nowPublic) ||
|
||||
affectedIds.includes(next) ||
|
||||
@@ -65,24 +71,41 @@ const isNewNext = () => {
|
||||
*/
|
||||
export function updateTimer(affectedIds) {
|
||||
const runningEventId = eventLoader.selectedEventId;
|
||||
|
||||
if (runningEventId === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// we need to reload in a few scenarios:
|
||||
// 1. we are not confident that changes do not affect running event
|
||||
// 2. the edited event is currently being used (now or next)
|
||||
// 3. the edited event replaces one of the previous (next)
|
||||
if (typeof affectedIds === 'undefined') {
|
||||
global.timer.syncLoaded(runningEventId);
|
||||
const safeOption = typeof affectedIds === 'undefined';
|
||||
// 2. the edited event is in memory (now or next) running
|
||||
const eventInMemory = safeOption ? false : affectedLoaded(affectedIds);
|
||||
// 3. the edited event replaces next event
|
||||
const isNext = isNewNext();
|
||||
|
||||
if (safeOption) {
|
||||
eventLoader.reset();
|
||||
const loadedEvent = eventLoader.loadById(runningEventId);
|
||||
eventTimer.hotReload(loadedEvent);
|
||||
return true;
|
||||
}
|
||||
if (affectedLoaded(affectedIds)) {
|
||||
global.timer.syncLoaded(runningEventId);
|
||||
|
||||
if (eventInMemory) {
|
||||
const loadedEvent = eventLoader.loadById(runningEventId);
|
||||
if (!loadedEvent) {
|
||||
// event was deleted
|
||||
eventLoader.reset();
|
||||
eventTimer.stop();
|
||||
} else {
|
||||
eventTimer.hotReload(loadedEvent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (isNewNext()) {
|
||||
global.timer.syncLoaded(runningEventId);
|
||||
|
||||
if (isNext) {
|
||||
const loadedEvent = eventLoader.loadById(runningEventId);
|
||||
eventTimer.hotReload(loadedEvent);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -94,7 +117,7 @@ export function updateTimer(affectedIds) {
|
||||
* @return {unknown[]}
|
||||
*/
|
||||
export async function addEvent(eventData) {
|
||||
const numEvents = DataProvider.getRundownLenght();
|
||||
const numEvents = DataProvider.getRundownLength();
|
||||
if (numEvents > MAX_EVENTS) {
|
||||
throw new Error(`ERROR: Reached limit number of ${MAX_EVENTS} events`);
|
||||
}
|
||||
@@ -126,6 +149,7 @@ export async function addEvent(eventData) {
|
||||
throw new Error(error);
|
||||
}
|
||||
updateTimer([id]);
|
||||
socketProvider.broadcastState();
|
||||
return newEvent;
|
||||
}
|
||||
|
||||
@@ -137,6 +161,7 @@ export async function editEvent(eventData) {
|
||||
}
|
||||
const newEvent = await DataProvider.updateEventById(eventId, eventData);
|
||||
updateTimer([eventId]);
|
||||
socketProvider.broadcastState();
|
||||
return newEvent;
|
||||
}
|
||||
|
||||
@@ -148,6 +173,7 @@ export async function editEvent(eventData) {
|
||||
export async function deleteEvent(eventId) {
|
||||
await DataProvider.deleteEvent(eventId);
|
||||
updateTimer([eventId]);
|
||||
socketProvider.broadcastState();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,6 +183,7 @@ export async function deleteEvent(eventId) {
|
||||
export async function deleteAllEvents() {
|
||||
await DataProvider.clearRundown();
|
||||
updateTimer();
|
||||
socketProvider.broadcastState();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,4 +264,5 @@ export async function applyDelay(eventId) {
|
||||
// update rundown
|
||||
await DataProvider.setRundown(rundown);
|
||||
updateTimer();
|
||||
socketProvider.broadcastState();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { socketProvider } from '../classes/socket/SocketController.js';
|
||||
|
||||
const store = {};
|
||||
|
||||
/**
|
||||
* A runtime store that broadcasts its payload
|
||||
*/
|
||||
export const runtimeState = {
|
||||
get(key) {
|
||||
return store[key];
|
||||
},
|
||||
set(key, value) {
|
||||
store[key] = value;
|
||||
socketProvider.send(key, value);
|
||||
},
|
||||
poll() {
|
||||
return store;
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user