From b4ad533e6a24c048094c4679174ade96760609b8 Mon Sep 17 00:00:00 2001 From: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Date: Wed, 24 Jan 2024 00:20:10 +0100 Subject: [PATCH] refactor: cleanup store (#727) * refactor: isolate clock * refactor: isolate private * refactor: remove duplicate * chore: rename loaded > runtime * fix: handle no events loaded --- .../error-boundary/ErrorBoundary.jsx | 4 +- .../MultiPartProgressBar.tsx | 8 +- apps/client/src/common/hooks/useSocket.ts | 63 +++-- .../src/common/models/TimeManager.type.ts | 30 ++- apps/client/src/common/stores/runtime.ts | 26 +- apps/client/src/common/utils/socket.ts | 30 +-- .../cuesheet-progress/CuesheetProgress.tsx | 12 +- .../CuesheetTableHeaderTimers.tsx | 9 +- .../operator/operator-event/OperatorEvent.tsx | 4 +- .../operator/status-bar/StatusBarProgress.tsx | 13 +- .../operator/status-bar/StatusBarTimers.tsx | 5 +- .../src/features/viewers/ViewWrapper.tsx | 21 +- .../features/viewers/backstage/Backstage.tsx | 4 +- .../src/features/viewers/clock/Clock.tsx | 4 +- .../features/viewers/common/viewerUtils.ts | 4 +- .../features/viewers/countdown/Countdown.tsx | 4 +- .../viewers/countdown/countdown.helpers.ts | 4 +- .../viewers/minimal-timer/MinimalTimer.tsx | 4 +- .../src/features/viewers/public/Public.tsx | 4 +- .../features/viewers/studio/StudioClock.tsx | 4 +- .../src/features/viewers/timer/Timer.tsx | 4 +- apps/server/src/app.ts | 23 +- .../src/classes/event-loader/EventLoader.ts | 4 +- apps/server/src/services/TimerService.ts | 10 +- .../src/services/__tests__/timerUtils.test.ts | 228 +++++++++++------- .../runtime-service/RuntimeService.ts | 44 ++-- apps/server/src/services/timerUtils.ts | 20 +- apps/server/src/state.ts | 162 +++++-------- .../src/definitions/runtime/Runtime.type.ts | 8 +- .../definitions/runtime/RuntimeStore.type.ts | 11 +- .../definitions/runtime/TimerState.type.ts | 24 +- 31 files changed, 411 insertions(+), 384 deletions(-) diff --git a/apps/client/src/common/components/error-boundary/ErrorBoundary.jsx b/apps/client/src/common/components/error-boundary/ErrorBoundary.jsx index dcd522a08..b3268fcda 100644 --- a/apps/client/src/common/components/error-boundary/ErrorBoundary.jsx +++ b/apps/client/src/common/components/error-boundary/ErrorBoundary.jsx @@ -3,7 +3,7 @@ import React from 'react'; // skipcq: JS-C1003 - sentry does not expose itself as an ES Module. import * as Sentry from '@sentry/react'; -import { runtime } from '@/common/stores/runtime'; +import { runtimeStore } from '@/common/stores/runtime'; import { hasConnected, reconnectAttempts, shouldReconnect } from '@/common/utils/socket'; import style from './ErrorBoundary.module.scss'; @@ -29,7 +29,7 @@ class ErrorBoundary extends React.Component { Sentry.withScope((scope) => { scope.setExtras('error', error); - scope.setExtras('store', runtime.getState()); + scope.setExtras('store', runtimeStore.getState()); scope.setExtras('hasSocket', { hasConnected, shouldReconnect, reconnectAttempts }); const eventId = Sentry.captureException(error); this.setState({ eventId, info }); diff --git a/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.tsx b/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.tsx index 90268367b..67940f6a7 100644 --- a/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.tsx +++ b/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.tsx @@ -1,14 +1,16 @@ +import { MaybeNumber } from 'ontime-types'; + import { clamp } from '../../utils/math'; import './MultiPartProgressBar.scss'; interface MultiPartProgressBar { - now: number | null; + now: MaybeNumber; complete: number; normalColor: string; - warning?: number | null; + warning?: MaybeNumber; warningColor: string; - danger?: number | null; + danger?: MaybeNumber; dangerColor: string; hidden?: boolean; className?: string; diff --git a/apps/client/src/common/hooks/useSocket.ts b/apps/client/src/common/hooks/useSocket.ts index 72b0c62ba..f6d68ddb8 100644 --- a/apps/client/src/common/hooks/useSocket.ts +++ b/apps/client/src/common/hooks/useSocket.ts @@ -5,9 +5,9 @@ import { socketSendJson } from '../utils/socket'; export const useRundownEditor = () => { const featureSelector = (state: RuntimeStore) => ({ - playback: state.playback, - selectedEventId: state.loaded.selectedEventId, - nextEventId: state.loaded.nextEventId, + playback: state.timer.playback, + selectedEventId: state.eventNow?.id ?? null, + nextEventId: state.eventNext?.id ?? null, }); return useRuntimeStore(featureSelector); @@ -15,8 +15,8 @@ export const useRundownEditor = () => { export const useOperator = () => { const featureSelector = (state: RuntimeStore) => ({ - playback: state.playback, - selectedEventId: state.loaded.selectedEventId, + playback: state.timer.playback, + selectedEventId: state.eventNow?.id ?? null, }); return useRuntimeStore(featureSelector); @@ -50,9 +50,9 @@ export const setMessage = { export const usePlaybackControl = () => { const featureSelector = (state: RuntimeStore) => ({ - playback: state.playback, - selectedEventIndex: state.loaded.selectedEventIndex, - numEvents: state.loaded.numEvents, + playback: state.timer.playback, + selectedEventIndex: state.runtime.selectedEventIndex, + numEvents: state.runtime.numEvents, }); return useRuntimeStore(featureSelector); @@ -80,12 +80,24 @@ export const setPlayback = { }, }; +export const useInfoPanel = () => { + const featureSelector = (state: RuntimeStore) => ({ + eventNow: state.eventNow, + eventNext: state.eventNext, + playback: state.timer.playback, + selectedEventIndex: state.runtime.selectedEventIndex, + numEvents: state.runtime.numEvents, + }); + + return useRuntimeStore(featureSelector); +}; + export const useCuesheet = () => { const featureSelector = (state: RuntimeStore) => ({ - playback: state.playback, - selectedEventId: state.loaded.selectedEventId, - selectedEventIndex: state.loaded.selectedEventIndex, - numEvents: state.loaded.numEvents, + playback: state.timer.playback, + selectedEventId: state.eventNow?.id ?? null, + selectedEventIndex: state.runtime.selectedEventIndex, + numEvents: state.runtime.numEvents, titleNow: state.eventNow?.title || '', }); @@ -107,14 +119,33 @@ export const useTimer = () => { return useRuntimeStore(featureSelector); }; +export const useClock = () => { + const featureSelector = (state: RuntimeStore) => ({ + clock: state.clock, + }); + return useRuntimeStore(featureSelector); +}; + +/** Used by the progress bar components */ +export const useProgressData = () => { + const featureSelector = (state: RuntimeStore) => ({ + addedTime: state.timer.addedTime, + current: state.timer.current, + duration: state.timer.duration, + timeWarning: state.eventNow?.timeWarning ?? null, + timeDanger: state.eventNow?.timeDanger ?? null, + }); + return useRuntimeStore(featureSelector); +}; + export const setClientName = (newName: string) => socketSendJson('set-client-name', newName); export const useRuntimeOverview = () => { const featureSelector = (state: RuntimeStore) => ({ - playback: state.playback, - clock: state.timer.clock, - numEvents: state.loaded.numEvents, - selectedEventIndex: state.loaded.selectedEventIndex, + playback: state.timer.playback, + clock: state.clock, + selectedEventIndex: state.runtime.selectedEventIndex, + numEvents: state.runtime.numEvents, }); return useRuntimeStore(featureSelector); diff --git a/apps/client/src/common/models/TimeManager.type.ts b/apps/client/src/common/models/TimeManager.type.ts index 61b53f101..be3f478d9 100644 --- a/apps/client/src/common/models/TimeManager.type.ts +++ b/apps/client/src/common/models/TimeManager.type.ts @@ -1,21 +1,19 @@ -import { Playback, TimerType } from 'ontime-types'; +import { MaybeNumber, Playback, TimerType } from 'ontime-types'; -export type TimeManagerType = { - clock: number; - current: null | number; - elapsed: null | number; - duration: null | number; - timerBehaviour?: string; - timerType: TimerType; - expectedFinish: null | number; +// first set extends TimerState +export type ViewExtendedTimer = { addedTime: number; - startedAt: null | number; - finishedAt: null | number; - secondaryTimer: null | number; - - finished: boolean; + current: MaybeNumber; + duration: MaybeNumber; + elapsed: MaybeNumber; + expectedFinish: MaybeNumber; + finishedAt: MaybeNumber; playback: Playback; + secondaryTimer: MaybeNumber; + startedAt: MaybeNumber; - timeWarning: number | null; - timeDanger: number | null; + clock: number; + timeDanger: MaybeNumber; + timeWarning: MaybeNumber; + timerType: TimerType; }; diff --git a/apps/client/src/common/stores/runtime.ts b/apps/client/src/common/stores/runtime.ts index 024ed0463..41294bf94 100644 --- a/apps/client/src/common/stores/runtime.ts +++ b/apps/client/src/common/stores/runtime.ts @@ -3,22 +3,19 @@ import { Playback, RuntimeStore } from 'ontime-types'; import { createWithEqualityFn, useStoreWithEqualityFn } from 'zustand/traditional'; export const runtimeStorePlaceholder: RuntimeStore = { + clock: 0, timer: { - clock: 0, + addedTime: 0, current: null, + duration: null, elapsed: null, expectedFinish: null, - addedTime: 0, - startedAt: null, finishedAt: null, + playback: Playback.Stop, secondaryTimer: null, - duration: null, - timerType: null, - endAction: null, - timeWarning: null, - timeDanger: null, + startedAt: null, }, - playback: Playback.Stop, + onAir: false, message: { timer: { text: '', @@ -39,14 +36,9 @@ export const runtimeStorePlaceholder: RuntimeStore = { visible: false, }, }, - onAir: false, - loaded: { + runtime: { numEvents: 0, selectedEventIndex: null, - selectedEventId: null, - selectedPublicEventId: null, - nextEventId: null, - nextPublicEventId: null, }, eventNow: null, eventNext: null, @@ -56,7 +48,7 @@ export const runtimeStorePlaceholder: RuntimeStore = { const deepCompare = (a: T, b: T) => isEqual(a, b); -export const runtime = createWithEqualityFn( +export const runtimeStore = createWithEqualityFn( () => ({ ...runtimeStorePlaceholder, }), @@ -64,4 +56,4 @@ export const runtime = createWithEqualityFn( ); export const useRuntimeStore = (selector: (state: RuntimeStore) => T) => - useStoreWithEqualityFn(runtime, selector, deepCompare); + useStoreWithEqualityFn(runtimeStore, selector, deepCompare); diff --git a/apps/client/src/common/utils/socket.ts b/apps/client/src/common/utils/socket.ts index b500f60ef..b32018021 100644 --- a/apps/client/src/common/utils/socket.ts +++ b/apps/client/src/common/utils/socket.ts @@ -4,7 +4,7 @@ import { isProduction, RUNTIME, websocketUrl } from '../api/apiConstants'; import { ontimeQueryClient } from '../queryClient'; import { socketClientName } from '../stores/connectionName'; import { addLog } from '../stores/logger'; -import { runtime } from '../stores/runtime'; +import { runtimeStore } from '../stores/runtime'; export let websocket: WebSocket | null = null; let reconnectTimeout: NodeJS.Timeout | null = null; @@ -63,40 +63,40 @@ export const connectSocket = (preferredClientName?: string) => { break; } case 'ontime': { - runtime.setState(payload as RuntimeStore); + runtimeStore.setState(payload as RuntimeStore); if (!isProduction) { ontimeQueryClient.setQueryData(RUNTIME, data.payload); } break; } case 'ontime-playback': { - const state = runtime.getState(); - state.playback = payload; - runtime.setState(state); + const state = runtimeStore.getState(); + state.timer.playback = payload; + runtimeStore.setState(state); break; } case 'ontime-timer': { - const state = runtime.getState(); + const state = runtimeStore.getState(); state.timer = payload; - runtime.setState(state); + runtimeStore.setState(state); break; } - case 'ontime-loaded': { - const state = runtime.getState(); - state.loaded = payload; - runtime.setState(state); + case 'ontime-runtime': { + const state = runtimeStore.getState(); + state.runtime = payload; + runtimeStore.setState(state); break; } case 'ontime-message': { - const state = runtime.getState(); + const state = runtimeStore.getState(); state.message = payload; - runtime.setState(state); + runtimeStore.setState(state); break; } case 'ontime-onAir': { - const state = runtime.getState(); + const state = runtimeStore.getState(); state.onAir = payload; - runtime.setState(state); + runtimeStore.setState(state); break; } } diff --git a/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.tsx b/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.tsx index cf6440b6a..34d5120f5 100644 --- a/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.tsx +++ b/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.tsx @@ -1,22 +1,22 @@ import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar'; -import { useTimer } from '../../../common/hooks/useSocket'; +import { useProgressData } from '../../../common/hooks/useSocket'; import useViewSettings from '../../../common/hooks-query/useViewSettings'; import styles from './CuesheetProgress.module.scss'; export default function CuesheetProgress() { const { data } = useViewSettings(); - const timer = useTimer(); - const totalTime = (timer.duration ?? 0) + (timer.addedTime ?? 0); + const { addedTime, current, duration, timeWarning, timeDanger } = useProgressData(); + const totalTime = (duration ?? 0) + (addedTime ?? 0); return ( diff --git a/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeaderTimers.tsx b/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeaderTimers.tsx index 14182589a..147e15a47 100644 --- a/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeaderTimers.tsx +++ b/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeaderTimers.tsx @@ -1,21 +1,22 @@ -import { useTimer } from '../../../common/hooks/useSocket'; +import { useClock, useTimer } from '../../../common/hooks/useSocket'; import ClockTime from '../../viewers/common/clock-time/ClockTime'; import RunningTime from '../../viewers/common/running-time/RunningTime'; import style from './CuesheetTableHeader.module.scss'; export default function CuesheetTableHeaderTimers() { - const timer = useTimer(); + const { current } = useTimer(); + const { clock } = useClock(); return ( <>
Running Timer
- +
Time Now
- +
); diff --git a/apps/client/src/features/operator/operator-event/OperatorEvent.tsx b/apps/client/src/features/operator/operator-event/OperatorEvent.tsx index cdb499967..1df37e39a 100644 --- a/apps/client/src/features/operator/operator-event/OperatorEvent.tsx +++ b/apps/client/src/features/operator/operator-event/OperatorEvent.tsx @@ -30,8 +30,8 @@ interface OperatorEventProps { // extract this to contain re-renders function RollingTime() { - const timer = useTimer(); - return ; + const { current } = useTimer(); + return ; } function OperatorEvent(props: OperatorEventProps) { diff --git a/apps/client/src/features/operator/status-bar/StatusBarProgress.tsx b/apps/client/src/features/operator/status-bar/StatusBarProgress.tsx index 065dc956e..190239994 100644 --- a/apps/client/src/features/operator/status-bar/StatusBarProgress.tsx +++ b/apps/client/src/features/operator/status-bar/StatusBarProgress.tsx @@ -1,7 +1,7 @@ import { ViewSettings } from 'ontime-types'; import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar'; -import { useTimer } from '../../../common/hooks/useSocket'; +import { useProgressData } from '../../../common/hooks/useSocket'; import styles from './StatusBar.module.scss'; @@ -11,18 +11,17 @@ interface StatusBarProgressProps { export default function StatusBarProgress(props: StatusBarProgressProps) { const { viewSettings } = props; - - const timer = useTimer(); - const totalTime = (timer.duration ?? 0) + (timer.addedTime ?? 0); + const { addedTime, current, duration, timeWarning, timeDanger } = useProgressData(); + const totalTime = (duration ?? 0) + (addedTime ?? 0); return ( diff --git a/apps/client/src/features/operator/status-bar/StatusBarTimers.tsx b/apps/client/src/features/operator/status-bar/StatusBarTimers.tsx index e67208ea6..607228a8e 100644 --- a/apps/client/src/features/operator/status-bar/StatusBarTimers.tsx +++ b/apps/client/src/features/operator/status-bar/StatusBarTimers.tsx @@ -2,7 +2,7 @@ import { useMemo } from 'react'; import { MaybeNumber, Playback } from 'ontime-types'; import PlaybackIcon from '../../../common/components/playback-icon/PlaybackIcon'; -import { useTimer } from '../../../common/hooks/useSocket'; +import { useClock, useTimer } from '../../../common/hooks/useSocket'; import { cx } from '../../../common/utils/styleUtils'; import ClockTime from '../../viewers/common/clock-time/ClockTime'; import RunningTime from '../../viewers/common/running-time/RunningTime'; @@ -23,6 +23,7 @@ export default function StatusBarTimers(props: StatusBarTimersProps) { const { projectTitle, playback, selectedEventId, firstStart, firstId, lastEnd, lastId } = props; const timer = useTimer(); + const { clock } = useClock(); const getTimeStart = (): MaybeNumber => { if (firstStart === undefined) { @@ -61,7 +62,7 @@ export default function StatusBarTimers(props: StatusBarTimersProps) { {PlaybackIconComponent}
Time now - +
Elapsed time diff --git a/apps/client/src/features/viewers/ViewWrapper.tsx b/apps/client/src/features/viewers/ViewWrapper.tsx index be4659ed8..2a638d7ab 100644 --- a/apps/client/src/features/viewers/ViewWrapper.tsx +++ b/apps/client/src/features/viewers/ViewWrapper.tsx @@ -1,5 +1,5 @@ import { ComponentType, useMemo } from 'react'; -import { TimeManagerType } from 'common/models/TimeManager.type'; +import { ViewExtendedTimer } from 'common/models/TimeManager.type'; import { Message, OntimeEvent, ProjectData, Settings, SupportedEvent, TimerMessage, ViewSettings } from 'ontime-types'; import { useStore } from 'zustand'; @@ -7,7 +7,7 @@ import useProjectData from '../../common/hooks-query/useProjectData'; import useRundown from '../../common/hooks-query/useRundown'; import useSettings from '../../common/hooks-query/useSettings'; import useViewSettings from '../../common/hooks-query/useViewSettings'; -import { runtime } from '../../common/stores/runtime'; +import { runtimeStore } from '../../common/stores/runtime'; import { useViewOptionsStore } from '../../common/stores/viewOptions'; type WithDataProps = { @@ -20,7 +20,7 @@ type WithDataProps = { publicEventNow: OntimeEvent | null; eventNext: OntimeEvent | null; publicEventNext: OntimeEvent | null; - time: TimeManagerType; + time: ViewExtendedTimer; events: OntimeEvent[]; backstageEvents: OntimeEvent[]; selectedId: string | null; @@ -55,11 +55,11 @@ const withData =

(Component: ComponentType

) => { }, [rundownData]); // websocket data - const { timer, message, playback, onAir, eventNext, publicEventNext, publicEventNow, eventNow, loaded } = - useStore(runtime); - const publicSelectedId = loaded.selectedPublicEventId; - const selectedId = loaded.selectedEventId; - const nextId = loaded.nextEventId; + const { clock, timer, message, onAir, eventNext, publicEventNext, publicEventNow, eventNow } = + useStore(runtimeStore); + const publicSelectedId = publicEventNow?.id ?? null; + const selectedId = eventNow?.id ?? null; + const nextId = eventNext?.id ?? null; /******************************************/ /*** + TimeManagerType ***/ @@ -69,7 +69,10 @@ const withData =

(Component: ComponentType

) => { const TimeManagerType = { ...timer, - playback, + clock, + timerType: eventNow?.timerType ?? null, + timeWarning: eventNow?.timeWarning ?? null, + timeDanger: eventNow?.timeWarning ?? null, }; // prevent render until we get all the data we need diff --git a/apps/client/src/features/viewers/backstage/Backstage.tsx b/apps/client/src/features/viewers/backstage/Backstage.tsx index 6035b9425..6e8bffb36 100644 --- a/apps/client/src/features/viewers/backstage/Backstage.tsx +++ b/apps/client/src/features/viewers/backstage/Backstage.tsx @@ -14,7 +14,7 @@ import TitleCard from '../../../common/components/title-card/TitleCard'; import { getBackstageOptions } from '../../../common/components/view-params-editor/constants'; import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; -import { TimeManagerType } from '../../../common/models/TimeManager.type'; +import { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; import { formatTime, getDefaultFormat } from '../../../common/utils/time'; import { useTranslation } from '../../../translation/TranslationProvider'; import { titleVariants } from '../common/animation'; @@ -27,7 +27,7 @@ interface BackstageProps { publ: Message; eventNow: OntimeEvent | null; eventNext: OntimeEvent | null; - time: TimeManagerType; + time: ViewExtendedTimer; backstageEvents: OntimeEvent[]; selectedId: string | null; general: ProjectData; diff --git a/apps/client/src/features/viewers/clock/Clock.tsx b/apps/client/src/features/viewers/clock/Clock.tsx index e86afa8ba..88d3d7fa7 100644 --- a/apps/client/src/features/viewers/clock/Clock.tsx +++ b/apps/client/src/features/viewers/clock/Clock.tsx @@ -7,7 +7,7 @@ import NavigationMenu from '../../../common/components/navigation-menu/Navigatio import { getClockOptions } from '../../../common/components/view-params-editor/constants'; import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; -import { TimeManagerType } from '../../../common/models/TimeManager.type'; +import { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; import { OverridableOptions } from '../../../common/models/View.types'; import { formatTime, getDefaultFormat } from '../../../common/utils/time'; import SuperscriptTime from '../common/superscript-time/SuperscriptTime'; @@ -16,7 +16,7 @@ import './Clock.scss'; interface ClockProps { isMirrored: boolean; - time: TimeManagerType; + time: ViewExtendedTimer; viewSettings: ViewSettings; settings: Settings | undefined; } diff --git a/apps/client/src/features/viewers/common/viewerUtils.ts b/apps/client/src/features/viewers/common/viewerUtils.ts index 91ab3a687..c83b6fbcd 100644 --- a/apps/client/src/features/viewers/common/viewerUtils.ts +++ b/apps/client/src/features/viewers/common/viewerUtils.ts @@ -1,8 +1,8 @@ import { TimerType } from 'ontime-types'; -import type { TimeManagerType } from '../../../common/models/TimeManager.type'; +import type { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; -type TimerTypeParams = Pick; +type TimerTypeParams = Pick; export function getTimerByType(timerObject?: TimerTypeParams): number | null { if (!timerObject) { diff --git a/apps/client/src/features/viewers/countdown/Countdown.tsx b/apps/client/src/features/viewers/countdown/Countdown.tsx index 3be41574a..8a36603c1 100644 --- a/apps/client/src/features/viewers/countdown/Countdown.tsx +++ b/apps/client/src/features/viewers/countdown/Countdown.tsx @@ -8,7 +8,7 @@ import NavigationMenu from '../../../common/components/navigation-menu/Navigatio import { getCountdownOptions } from '../../../common/components/view-params-editor/constants'; import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; -import { TimeManagerType } from '../../../common/models/TimeManager.type'; +import { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; import { formatTime, getDefaultFormat } from '../../../common/utils/time'; import { useTranslation } from '../../../translation/TranslationProvider'; import SuperscriptTime from '../common/superscript-time/SuperscriptTime'; @@ -21,7 +21,7 @@ import './Countdown.scss'; interface CountdownProps { isMirrored: boolean; backstageEvents: OntimeEvent[]; - time: TimeManagerType; + time: ViewExtendedTimer; selectedId: string | null; viewSettings: ViewSettings; settings: Settings | undefined; diff --git a/apps/client/src/features/viewers/countdown/countdown.helpers.ts b/apps/client/src/features/viewers/countdown/countdown.helpers.ts index 981feceff..19e20c9f9 100644 --- a/apps/client/src/features/viewers/countdown/countdown.helpers.ts +++ b/apps/client/src/features/viewers/countdown/countdown.helpers.ts @@ -1,6 +1,6 @@ import { OntimeEvent, Playback } from 'ontime-types'; -import { TimeManagerType } from '../../../common/models/TimeManager.type'; +import { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; export enum TimerMessage { toStart = 'to_start', @@ -19,7 +19,7 @@ export const sanitiseTitle = (title: string | null) => (title ? title : '{no tit * Returns a parsed timer and relevant status message */ export const fetchTimerData = ( - time: TimeManagerType, + time: ViewExtendedTimer, follow: OntimeEvent, selectedId: string | null, ): { message: TimerMessage; timer: number } => { diff --git a/apps/client/src/features/viewers/minimal-timer/MinimalTimer.tsx b/apps/client/src/features/viewers/minimal-timer/MinimalTimer.tsx index 9ba2e2bb7..009f8ad7c 100644 --- a/apps/client/src/features/viewers/minimal-timer/MinimalTimer.tsx +++ b/apps/client/src/features/viewers/minimal-timer/MinimalTimer.tsx @@ -8,7 +8,7 @@ import NavigationMenu from '../../../common/components/navigation-menu/Navigatio import { MINIMAL_TIMER_OPTIONS } from '../../../common/components/view-params-editor/constants'; import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; -import { TimeManagerType } from '../../../common/models/TimeManager.type'; +import { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; import { OverridableOptions } from '../../../common/models/View.types'; import { isStringBoolean } from '../../../common/utils/viewUtils'; import { useTranslation } from '../../../translation/TranslationProvider'; @@ -19,7 +19,7 @@ import './MinimalTimer.scss'; interface MinimalTimerProps { isMirrored: boolean; pres: TimerMessage; - time: TimeManagerType; + time: ViewExtendedTimer; viewSettings: ViewSettings; } diff --git a/apps/client/src/features/viewers/public/Public.tsx b/apps/client/src/features/viewers/public/Public.tsx index 011fb70f8..29930a80a 100644 --- a/apps/client/src/features/viewers/public/Public.tsx +++ b/apps/client/src/features/viewers/public/Public.tsx @@ -12,7 +12,7 @@ import TitleCard from '../../../common/components/title-card/TitleCard'; import { getPublicOptions } from '../../../common/components/view-params-editor/constants'; import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; -import { TimeManagerType } from '../../../common/models/TimeManager.type'; +import { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; import { formatTime, getDefaultFormat } from '../../../common/utils/time'; import { useTranslation } from '../../../translation/TranslationProvider'; import { titleVariants } from '../common/animation'; @@ -25,7 +25,7 @@ interface BackstageProps { publ: Message; publicEventNow: OntimeEvent | null; publicEventNext: OntimeEvent | null; - time: TimeManagerType; + time: ViewExtendedTimer; events: OntimeEvent[]; publicSelectedId: string | null; general: ProjectData; diff --git a/apps/client/src/features/viewers/studio/StudioClock.tsx b/apps/client/src/features/viewers/studio/StudioClock.tsx index 4c6161db8..34e8c8c8e 100644 --- a/apps/client/src/features/viewers/studio/StudioClock.tsx +++ b/apps/client/src/features/viewers/studio/StudioClock.tsx @@ -10,7 +10,7 @@ import { getStudioClockOptions } from '../../../common/components/view-params-ed import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; import useFitText from '../../../common/hooks/useFitText'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; -import { TimeManagerType } from '../../../common/models/TimeManager.type'; +import { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; import { formatTime, getDefaultFormat } from '../../../common/utils/time'; import { isStringBoolean } from '../../../common/utils/viewUtils'; import SuperscriptTime from '../common/superscript-time/SuperscriptTime'; @@ -22,7 +22,7 @@ import './StudioClock.scss'; interface StudioClockProps { isMirrored: boolean; eventNext: OntimeEvent | null; - time: TimeManagerType; + time: ViewExtendedTimer; backstageEvents: OntimeRundown; selectedId: string | null; nextId: string | null; diff --git a/apps/client/src/features/viewers/timer/Timer.tsx b/apps/client/src/features/viewers/timer/Timer.tsx index 31b16b191..12c058944 100644 --- a/apps/client/src/features/viewers/timer/Timer.tsx +++ b/apps/client/src/features/viewers/timer/Timer.tsx @@ -11,7 +11,7 @@ import TitleCard from '../../../common/components/title-card/TitleCard'; import { getTimerOptions } from '../../../common/components/view-params-editor/constants'; import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; -import { TimeManagerType } from '../../../common/models/TimeManager.type'; +import { ViewExtendedTimer } from '../../../common/models/TimeManager.type'; import { formatTime, getDefaultFormat } from '../../../common/utils/time'; import { isStringBoolean } from '../../../common/utils/viewUtils'; import { useTranslation } from '../../../translation/TranslationProvider'; @@ -42,7 +42,7 @@ interface TimerProps { external: Message; eventNow: OntimeEvent | null; eventNext: OntimeEvent | null; - time: TimeManagerType; + time: ViewExtendedTimer; viewSettings: ViewSettings; settings: Settings | undefined; } diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts index fbdafb977..3a0b42316 100644 --- a/apps/server/src/app.ts +++ b/apps/server/src/app.ts @@ -157,28 +157,13 @@ export const startServer = async () => { /** * Module initialises the services and provides initial payload for the store - * Currently registered objects in store - * - * - Message Service timerMessage - * - Message Service publicMessage - * - Message Service lowerMessage - * - Message Service externalMessage - * - * - Runtime Service onAir (derived from playback) - * - Runtime Service timer - * - Runtime Service playback - * - Runtime Service loaded // TODO: rename to runtime ?? - * - Runtime Service eventNow - * - Runtime Service publicEventNow - * - Runtime Service eventNext - * - Runtime Service publicEventNext */ eventStore.init({ - message: messageService, - onAir: state.playback !== Playback.Stop, + clock: state.clock, timer: state.timer, - playback: state.playback, - loaded: state.runtime, + onAir: state.timer.playback !== Playback.Stop, + message: messageService.getState(), + runtime: state.runtime, eventNow: state.eventNow, publicEventNow: state.publicEventNow, eventNext: state.eventNext, diff --git a/apps/server/src/classes/event-loader/EventLoader.ts b/apps/server/src/classes/event-loader/EventLoader.ts index 3a1e15b44..1f1108504 100644 --- a/apps/server/src/classes/event-loader/EventLoader.ts +++ b/apps/server/src/classes/event-loader/EventLoader.ts @@ -66,7 +66,7 @@ export class EventLoader { * finds the previous event * @return {object | undefined} */ - static findPrevious(currentEventId: string | null): OntimeEvent | null { + static findPrevious(currentEventId?: string): OntimeEvent | null { const timedEvents = EventLoader.getPlayableEvents(); if (!timedEvents || !timedEvents.length) { return null; @@ -87,7 +87,7 @@ export class EventLoader { * finds the next event * @return {object | undefined} */ - static findNext(currentEventId: string | null): OntimeEvent | null { + static findNext(currentEventId?: string): OntimeEvent | null { const timedEvents = EventLoader.getPlayableEvents(); if (!timedEvents || !timedEvents.length) { return null; diff --git a/apps/server/src/services/TimerService.ts b/apps/server/src/services/TimerService.ts index fd542a33a..2584cc8bc 100644 --- a/apps/server/src/services/TimerService.ts +++ b/apps/server/src/services/TimerService.ts @@ -22,11 +22,11 @@ export class TimerService { } start() { - if (!state.runtime.selectedEventId) { + if (!state.eventNow) { return; } - if (state.playback === Playback.Play) { + if (state.timer.playback === Playback.Play) { return; } @@ -36,14 +36,14 @@ export class TimerService { } pause() { - if (state.playback !== Playback.Play) { + if (state.timer.playback !== Playback.Play) { return; } stateMutations.timer.pause(); } stop() { - if (state.playback === Playback.Stop) { + if (state.timer.playback === Playback.Stop) { return; } stateMutations.timer.stop(); @@ -54,7 +54,7 @@ export class TimerService { * @param {number} amount */ addTime(amount: number) { - if (state.runtime.selectedEventId === null) { + if (state.eventNow === null) { return; } stateMutations.timer.addTime(amount); diff --git a/apps/server/src/services/__tests__/timerUtils.test.ts b/apps/server/src/services/__tests__/timerUtils.test.ts index b68578103..5731e8d99 100644 --- a/apps/server/src/services/__tests__/timerUtils.test.ts +++ b/apps/server/src/services/__tests__/timerUtils.test.ts @@ -16,14 +16,16 @@ describe('getExpectedFinish()', () => { const state = { eventNow: { timeEnd: 10, + timerType: TimerType.CountDown, }, timer: { addedTime: 0, duration: 10, finishedAt: null, - pausedAt: null, startedAt: null, - timerType: TimerType.CountDown, + }, + _timer: { + pausedAt: null, }, } as TState; const calculatedFinish = getExpectedFinish(state); @@ -33,14 +35,16 @@ describe('getExpectedFinish()', () => { const state = { eventNow: { timeEnd: 20, + timerType: TimerType.CountDown, }, timer: { addedTime: 0, duration: 10, finishedAt: 20, // <---- finished at - pausedAt: null, startedAt: 10, - timerType: TimerType.CountDown, + }, + _timer: { + pausedAt: null, }, } as TState; const calculatedFinish = getExpectedFinish(state); @@ -50,14 +54,16 @@ describe('getExpectedFinish()', () => { const state = { eventNow: { timeEnd: 11, + timerType: TimerType.CountDown, }, timer: { addedTime: 0, duration: 10, finishedAt: null, - pausedAt: null, startedAt: 1, - timerType: TimerType.CountDown, + }, + _timer: { + pausedAt: null, }, } as TState; const calculatedFinish = getExpectedFinish(state); @@ -67,14 +73,16 @@ describe('getExpectedFinish()', () => { const state = { eventNow: { timeEnd: 11, + timerType: TimerType.CountDown, }, timer: { addedTime: 20, duration: 10, finishedAt: null, - pausedAt: null, startedAt: 1, - timerType: TimerType.CountDown, + }, + _timer: { + pausedAt: null, }, } as TState; @@ -85,14 +93,16 @@ describe('getExpectedFinish()', () => { const state = { eventNow: { timeEnd: 11, + timerType: TimerType.CountDown, }, timer: { addedTime: -10, duration: 10, finishedAt: null, - pausedAt: null, startedAt: 1, - timerType: TimerType.CountDown, + }, + _timer: { + pausedAt: null, }, } as TState; @@ -103,14 +113,16 @@ describe('getExpectedFinish()', () => { const state = { eventNow: { timeEnd: 11, + timerType: TimerType.CountDown, }, timer: { addedTime: -100, duration: 10, finishedAt: null, - pausedAt: null, startedAt: 1, - timerType: TimerType.CountDown, + }, + _timer: { + pausedAt: null, }, } as TState; @@ -121,14 +133,16 @@ describe('getExpectedFinish()', () => { const state = { eventNow: { timeEnd: 0, + timerType: TimerType.CountDown, }, timer: { addedTime: 0, duration: 0, finishedAt: null, - pausedAt: null, startedAt: 1, - timerType: TimerType.CountDown, + }, + _timer: { + pausedAt: null, }, } as TState; @@ -139,14 +153,16 @@ describe('getExpectedFinish()', () => { const state = { eventNow: { timeEnd: 10, + timerType: TimerType.CountDown, }, timer: { addedTime: 0, duration: dayInMs, finishedAt: null, - pausedAt: null, startedAt: 10, - timerType: TimerType.CountDown, + }, + _timer: { + pausedAt: null, }, } as TState; @@ -158,14 +174,16 @@ describe('getExpectedFinish()', () => { const state = { eventNow: { timeEnd: 30, + timerType: TimerType.TimeToEnd, }, timer: { addedTime: 10, duration: dayInMs, finishedAt: null, - pausedAt: null, startedAt: 10, - timerType: TimerType.TimeToEnd, + }, + _timer: { + pausedAt: null, }, } as TState; @@ -176,13 +194,15 @@ describe('getExpectedFinish()', () => { const state = { eventNow: { timeEnd: 600000, // 00:10:00 + timerType: TimerType.TimeToEnd, }, timer: { addedTime: 0, finishedAt: null, - pausedAt: null, startedAt: 79200000, // 22:00:00 - timerType: TimerType.TimeToEnd, + }, + _timer: { + pausedAt: null, }, } as TState; @@ -198,15 +218,17 @@ describe('getCurrent()', () => { const state = { eventNow: { timeEnd: 30, + timerType: TimerType.CountDown, }, + clock: 0, timer: { addedTime: 10, - clock: 0, duration: 111, // <-- we take the duration value startedAt: null, finishedAt: null, + }, + _timer: { pausedAt: null, - timerType: TimerType.CountDown, }, } as TState; @@ -217,14 +239,17 @@ describe('getCurrent()', () => { const state = { eventNow: { timeEnd: 10, + timerType: TimerType.CountDown, }, + clock: 1, timer: { addedTime: 0, - clock: 1, duration: 10, startedAt: 0, finishedAt: null, - timerType: TimerType.CountDown, + }, + _timer: { + pausedAt: null, }, } as TState; @@ -235,15 +260,17 @@ describe('getCurrent()', () => { const state = { eventNow: { timeEnd: 10, + timerType: TimerType.CountDown, }, + clock: 1, timer: { addedTime: 10, - clock: 1, duration: 10, startedAt: 0, finishedAt: null, + }, + _timer: { pausedAt: null, - timerType: TimerType.CountDown, }, } as TState; @@ -254,15 +281,17 @@ describe('getCurrent()', () => { const state = { eventNow: { timeEnd: 20, + timerType: TimerType.CountDown, }, + clock: 10, timer: { addedTime: 0, - clock: 10, duration: dayInMs + 10, startedAt: 10, finishedAt: null, + }, + _timer: { pausedAt: null, - timerType: TimerType.CountDown, }, } as TState; @@ -273,15 +302,17 @@ describe('getCurrent()', () => { const state = { eventNow: { timeEnd: 20, + timerType: TimerType.CountDown, }, + clock: 5, timer: { addedTime: 0, - clock: 5, duration: dayInMs + 10, startedAt: 10, finishedAt: null, + }, + _timer: { pausedAt: null, - timerType: TimerType.CountDown, }, } as TState; @@ -292,15 +323,17 @@ describe('getCurrent()', () => { const state = { eventNow: { timeEnd: 20, + timerType: TimerType.CountDown, }, + clock: 5, timer: { addedTime: 20, - clock: 5, duration: dayInMs + 10, startedAt: 10, finishedAt: null, + }, + _timer: { pausedAt: null, - timerType: TimerType.CountDown, }, } as TState; @@ -312,15 +345,17 @@ describe('getCurrent()', () => { const state = { eventNow: { timeEnd: 100, + timerType: TimerType.TimeToEnd, }, + clock: 30, timer: { addedTime: 0, - clock: 30, duration: 100, startedAt: 10, finishedAt: null, + }, + _timer: { pausedAt: null, - timerType: TimerType.TimeToEnd, }, } as TState; @@ -331,15 +366,17 @@ describe('getCurrent()', () => { const state = { eventNow: { timeEnd: 100, + timerType: TimerType.TimeToEnd, }, + clock: 30, timer: { addedTime: 7, - clock: 30, duration: 100, startedAt: 10, finishedAt: null, + }, + _timer: { pausedAt: null, - timerType: TimerType.TimeToEnd, }, } as TState; @@ -350,15 +387,17 @@ describe('getCurrent()', () => { const state = { eventNow: { timeEnd: 600000, // 00:10:00 + timerType: TimerType.TimeToEnd, }, + clock: 79500000, // 22:05:00 timer: { addedTime: 0, - clock: 79500000, // 22:05:00 duration: Infinity, // not relevant, startedAt: 79200000, // 22:00:00 finishedAt: null, + }, + _timer: { pausedAt: null, - timerType: TimerType.TimeToEnd, }, } as TState; @@ -369,15 +408,17 @@ describe('getCurrent()', () => { const state = { eventNow: { timeEnd: 600000, // 00:10:00 + timerType: TimerType.TimeToEnd, }, + clock: 79500000, // 22:05:00 timer: { addedTime: 0, - clock: 79500000, // 22:05:00 duration: Infinity, // not relevant, startedAt: 79200000, // 22:00:00 finishedAt: null, + }, + _timer: { pausedAt: null, - timerType: TimerType.TimeToEnd, }, } as TState; @@ -393,15 +434,17 @@ describe('getExpectedFinish() and getCurrentTime() combined', () => { const state = { eventNow: { timeEnd: 10, + timerType: TimerType.CountDown, }, + clock: 0, timer: { addedTime: 0, - clock: 0, duration, startedAt: 0, - pausedAt: null, finishedAt: null, - timerType: TimerType.CountDown, + }, + _timer: { + pausedAt: null, }, } as TState; @@ -419,15 +462,17 @@ describe('getExpectedFinish() and getCurrentTime() combined', () => { const state = { eventNow: { timeEnd: 10, + timerType: TimerType.CountDown, }, + clock: 5, timer: { addedTime: 3, - clock: 5, duration, startedAt: 0, - pausedAt: null, finishedAt: null, - timerType: TimerType.CountDown, + }, + _timer: { + pausedAt: null, }, } as TState; @@ -449,8 +494,8 @@ describe('skippedOutOfEvent()', () => { const expectedFinish = startedAt + duration; const previousTime = expectedFinish - testSkipLimit / 2; const state = { + clock: previousTime, timer: { - clock: previousTime, expectedFinish, startedAt, }, @@ -459,7 +504,7 @@ describe('skippedOutOfEvent()', () => { expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); // @ts-expect-error -- cheating in tests - state.timer.clock += testSkipLimit; + state.clock += testSkipLimit; expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); }); @@ -470,8 +515,8 @@ describe('skippedOutOfEvent()', () => { const previousTime = startedAt + testSkipLimit / 2; const state = { + clock: previousTime, timer: { - clock: previousTime, expectedFinish, startedAt, }, @@ -480,7 +525,7 @@ describe('skippedOutOfEvent()', () => { expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); // @ts-expect-error -- cheating in tests - state.timer.clock += testSkipLimit; + state.clock += testSkipLimit; expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); }); @@ -490,8 +535,8 @@ describe('skippedOutOfEvent()', () => { const previousTime = dayInMs - 1; const state = { + clock: previousTime, timer: { - clock: previousTime, expectedFinish, startedAt, }, @@ -500,7 +545,7 @@ describe('skippedOutOfEvent()', () => { expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); // @ts-expect-error -- cheating in tests - state.timer.clock = testSkipLimit - 2; + state.clock = testSkipLimit - 2; expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); }); @@ -510,8 +555,8 @@ describe('skippedOutOfEvent()', () => { const previousTime = startedAt + 1; const state = { + clock: previousTime, timer: { - clock: previousTime, expectedFinish, startedAt, }, @@ -520,7 +565,7 @@ describe('skippedOutOfEvent()', () => { expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); // @ts-expect-error -- cheating in tests - state.timer.clock -= testSkipLimit; + state.clock -= testSkipLimit; expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); }); @@ -531,8 +576,8 @@ describe('skippedOutOfEvent()', () => { const previousTime = expectedFinish - testSkipLimit / 2; const state = { + clock: previousTime, timer: { - clock: previousTime, expectedFinish, startedAt, }, @@ -541,7 +586,7 @@ describe('skippedOutOfEvent()', () => { expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); // @ts-expect-error -- cheating in tests - state.timer.clock += testSkipLimit + 1; + state.clock += testSkipLimit + 1; expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(true); }); @@ -552,8 +597,8 @@ describe('skippedOutOfEvent()', () => { const previousTime = startedAt + testSkipLimit / 2; const state = { + clock: previousTime, timer: { - clock: previousTime, expectedFinish, startedAt, }, @@ -562,7 +607,7 @@ describe('skippedOutOfEvent()', () => { expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); // @ts-expect-error -- cheating in tests - state.timer.clock -= testSkipLimit + 1; + state.clock -= testSkipLimit + 1; expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(true); }); @@ -572,8 +617,8 @@ describe('skippedOutOfEvent()', () => { const previousTime = dayInMs - 3; const state = { + clock: previousTime, timer: { - clock: previousTime, expectedFinish, startedAt, }, @@ -582,7 +627,7 @@ describe('skippedOutOfEvent()', () => { expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); // @ts-expect-error -- cheating in tests - state.timer.clock = testSkipLimit - 2; + state.clock = testSkipLimit - 2; expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(true); }); @@ -592,8 +637,8 @@ describe('skippedOutOfEvent()', () => { const previousTime = startedAt + 1; const state = { + clock: previousTime, timer: { - clock: previousTime, expectedFinish, startedAt, }, @@ -602,7 +647,7 @@ describe('skippedOutOfEvent()', () => { expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(false); // @ts-expect-error -- cheating in tests - state.timer.clock -= testSkipLimit + 1; + state.clock -= testSkipLimit + 1; expect(skippedOutOfEvent(state, previousTime, testSkipLimit)).toBe(true); }); }); @@ -1124,14 +1169,17 @@ test('normaliseEndTime()', () => { describe('updateRoll()', () => { it('it updates running events correctly', () => { const timers = { - runtime: { - selectedEventId: '1', + eventNow: { + id: '1', }, + clock: 11, timer: { current: 10, expectedFinish: 15, - clock: 11, secondaryTimer: null, + startedAt: 1, + }, + _timer: { secondaryTarget: null, }, } as TState; @@ -1149,7 +1197,7 @@ describe('updateRoll()', () => { // @ts-expect-error -- cheating for tests timers.timer.expectedFinish = 1000; // @ts-expect-error -- cheating for tests - timers.timer.clock = 600; + timers.clock = 600; expected.updatedTimer = 1000 - 600; expect(updateRoll(timers)).toStrictEqual(expected); @@ -1157,14 +1205,14 @@ describe('updateRoll()', () => { it('it updates secondary timer', () => { const timers = { - runtime: { - selectedEventId: null, - }, + eventNow: null, + clock: 11, timer: { current: null, expectedFinish: null, - clock: 11, secondaryTimer: 1, + }, + _timer: { secondaryTarget: 15, }, } as TState; @@ -1181,15 +1229,17 @@ describe('updateRoll()', () => { it('flags an event end', () => { const timers = { - runtime: { - selectedEventId: '1', + eventNow: { + id: '1', }, + clock: 12, timer: { startedAt: 0, current: 10, expectedFinish: 11, - clock: 12, secondaryTimer: null, + }, + _timer: { secondaryTarget: null, }, } as TState; @@ -1206,15 +1256,15 @@ describe('updateRoll()', () => { it('secondary events do not trigger event ends', () => { const timers = { - runtime: { - selectedEventId: null, - }, + eventNow: null, + clock: 16, timer: { startedAt: null, current: null, expectedFinish: null, - clock: 16, secondaryTimer: 1, + }, + _timer: { secondaryTarget: 15, }, } as TState; @@ -1231,14 +1281,14 @@ describe('updateRoll()', () => { it('when a secondary timer is finished, it prompts for new event load', () => { const timers = { - runtime: { - selectedEventId: null, - }, + eventNow: null, + clock: 15, timer: { current: null, expectedFinish: null, - clock: 15, secondaryTimer: 0, + }, + _timer: { secondaryTarget: 15, }, } as TState; @@ -1255,15 +1305,17 @@ describe('updateRoll()', () => { it('counts over midnight', () => { const timers = { - runtime: { - selectedEventId: '1', + eventNow: { + id: '1', }, + clock: dayInMs - 10, timer: { current: 25, expectedFinish: 10, startedAt: 1000, - clock: dayInMs - 10, secondaryTimer: null, + }, + _timer: { secondaryTarget: null, }, } as TState; @@ -1280,15 +1332,17 @@ describe('updateRoll()', () => { it('rolls over midnight', () => { const timers = { - runtime: { - selectedEventId: '1', + eventNow: { + id: '1', }, + clock: 10, timer: { current: dayInMs, expectedFinish: 10, startedAt: 1000, - clock: 10, secondaryTimer: null, + }, + _timer: { secondaryTarget: null, }, } as TState; diff --git a/apps/server/src/services/runtime-service/RuntimeService.ts b/apps/server/src/services/runtime-service/RuntimeService.ts index 1dedb4cb4..b56a0bed1 100644 --- a/apps/server/src/services/runtime-service/RuntimeService.ts +++ b/apps/server/src/services/runtime-service/RuntimeService.ts @@ -36,10 +36,10 @@ class RuntimeService { * Checks if a list of IDs is in the current selection */ private affectsLoaded(affectedIds: string[]): boolean { - const now = state.runtime.selectedEventId; - const nowPublic = state.runtime.selectedPublicEventId; - const next = state.runtime.nextEventId; - const nextPublic = state.runtime.nextPublicEventId; + const now = state.eventNow?.id; + const nowPublic = state.publicEventNow?.id; + const next = state.eventNext?.id; + const nextPublic = state.publicEventNext?.id; return ( affectedIds.includes(now) || affectedIds.includes(nowPublic) || @@ -50,8 +50,8 @@ class RuntimeService { private isNewNext() { const timedEvents = EventLoader.getPlayableEvents(); - const now = state.runtime.selectedEventId; - const next = state.runtime.nextEventId; + const now = state.eventNow?.id; + const next = state.eventNext?.id; // check whether the index of now and next are consecutive const indexNow = timedEvents.findIndex((event) => event.id === now); @@ -61,8 +61,8 @@ class RuntimeService { return true; } // iterate through timed events and see if there are public events between nowPublic and nextPublic - const nowPublic = state.runtime.selectedPublicEventId; - const nextPublic = state.runtime.nextPublicEventId; + const nowPublic = state.publicEventNow?.id; + const nextPublic = state.publicEventNext?.id; let foundNew = false; let isAfter = false; @@ -93,7 +93,7 @@ class RuntimeService { * check whether underlying data of runtime has changed */ update(affectedIds?: string[]) { - const hasLoadedElements = state.runtime.selectedEventId && state.runtime.nextEventId; + const hasLoadedElements = state.eventNow && state.eventNext; if (!hasLoadedElements) { return; } @@ -107,11 +107,11 @@ class RuntimeService { let isNext = false; if (safeOption || eventInMemory) { - if (state.playback === Playback.Roll) { + if (state.timer.playback === Playback.Roll) { this.roll(); } // load stuff again, but keep running if our events still exist - const eventNow = EventLoader.getEventWithId(state.runtime.selectedEventId); + const eventNow = EventLoader.getEventWithId(state.eventNow.id); if (eventNow) { stateMutations.reload(eventNow); } @@ -139,7 +139,7 @@ class RuntimeService { const timedEvents = EventLoader.getPlayableEvents(); stateMutations.load(event, timedEvents); - const success = event.id === state.runtime.selectedEventId; + const success = event.id === state.eventNow?.id; if (success) { logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`); @@ -227,7 +227,7 @@ class RuntimeService { * @return {boolean} success - whether an event was loaded */ loadPrevious(): boolean { - const previousEvent = EventLoader.findPrevious(state.runtime.selectedEventId); + const previousEvent = EventLoader.findPrevious(state.eventNow?.id); if (previousEvent) { const success = this.loadEvent(previousEvent); return success; @@ -240,7 +240,7 @@ class RuntimeService { * @return {boolean} success */ loadNext(): boolean { - const nextEvent = EventLoader.findNext(state.runtime.selectedEventId); + const nextEvent = EventLoader.findNext(state.eventNow?.id); if (nextEvent) { const success = this.loadEvent(nextEvent); return success; @@ -254,10 +254,10 @@ class RuntimeService { * Starts playback on selected event */ start() { - const canStart = validatePlayback(state.playback).start; + const canStart = validatePlayback(state.timer.playback).start; if (canStart) { this.eventTimer.start(); - logger.info(LogOrigin.Playback, `Play Mode ${state.playback.toUpperCase()}`); + logger.info(LogOrigin.Playback, `Play Mode ${state.timer.playback.toUpperCase()}`); } } @@ -275,9 +275,9 @@ class RuntimeService { * Pauses playback on selected event */ pause() { - if (validatePlayback(state.playback).pause) { + if (validatePlayback(state.timer.playback).pause) { this.eventTimer.pause(); - const newState = state.playback; + const newState = state.timer.playback; logger.info(LogOrigin.Playback, `Play Mode ${newState.toUpperCase()}`); } } @@ -286,9 +286,9 @@ class RuntimeService { * Stops timer and unloads any events */ stop() { - if (validatePlayback(state.playback).stop) { + if (validatePlayback(state.timer.playback).stop) { this.eventTimer.stop(); - const newState = state.playback; + const newState = state.timer.playback; logger.info(LogOrigin.Playback, `Play Mode ${newState.toUpperCase()}`); } } @@ -297,7 +297,7 @@ class RuntimeService { * Reloads current event */ reload() { - if (state.runtime.selectedEventId) { + if (state.eventNow) { stateMutations.reload(); } } @@ -313,7 +313,7 @@ class RuntimeService { logger.warning(LogOrigin.Server, `Roll: ${error}`); } - const newState = state.playback; + const newState = state.timer.playback; logger.info(LogOrigin.Playback, `Play Mode ${newState.toUpperCase()}`); } diff --git a/apps/server/src/services/timerUtils.ts b/apps/server/src/services/timerUtils.ts index 66bc1e29b..2c9c1b5f0 100644 --- a/apps/server/src/services/timerUtils.ts +++ b/apps/server/src/services/timerUtils.ts @@ -14,8 +14,11 @@ export const normaliseEndTime = (start: number, end: number) => (end < start ? e * @returns {number | null} new current time or null if nothing is running */ export function getExpectedFinish(state: TState): MaybeNumber { - const { startedAt, clock, finishedAt, duration, addedTime, timerType, pausedAt } = state.timer; + const { startedAt, finishedAt, duration, addedTime } = state.timer; + const { timerType } = state.eventNow; + const { pausedAt } = state._timer; const { timeEnd } = state.eventNow; + const { clock } = state; if (startedAt === null) { return null; @@ -47,8 +50,11 @@ export function getExpectedFinish(state: TState): MaybeNumber { * @returns {number} current time for timer */ export function getCurrent(state: TState): number { - const { startedAt, duration, addedTime, clock, timerType, pausedAt } = state.timer; + const { startedAt, duration, addedTime } = state.timer; + const { timerType } = state.eventNow; + const { pausedAt } = state._timer; const { timeEnd } = state.eventNow; + const { clock } = state; if (timerType === TimerType.TimeToEnd) { const isNextDay = startedAt > timeEnd; @@ -77,7 +83,9 @@ export function getCurrent(state: TState): number { * @returns {boolean} */ export function skippedOutOfEvent(state: TState, previousTime: number, skipLimit: number): boolean { - const { clock, startedAt, expectedFinish } = state.timer; + const { startedAt, expectedFinish } = state.timer; + const { clock } = state; + const hasPassedMidnight = previousTime > dayInMs - skipLimit && clock < skipLimit; const adjustedClock = hasPassedMidnight ? clock + dayInMs : clock; @@ -224,8 +232,10 @@ export const getRollTimers = (rundown: OntimeEvent[], timeNow: number) => { * @returns object with selection variables */ export const updateRoll = (state: TState) => { - const { selectedEventId } = state.runtime; - const { current, expectedFinish, startedAt, clock, secondaryTimer, secondaryTarget } = state.timer; + const { current, expectedFinish, startedAt, secondaryTimer } = state.timer; + const { secondaryTarget } = state._timer; + const { clock } = state; + const selectedEventId = state.eventNow?.id ?? null; // timers let updatedTimer = current; diff --git a/apps/server/src/state.ts b/apps/server/src/state.ts index 439ba03d5..2c59a5fb9 100644 --- a/apps/server/src/state.ts +++ b/apps/server/src/state.ts @@ -25,38 +25,31 @@ const timeSkipLimit = 1000; const initialRuntime: Runtime = { selectedEventIndex: null, - selectedEventId: null, // TODO: remove - selectedPublicEventId: null, // TODO: remove - nextEventId: null, // TODO: remove - nextPublicEventId: null, // TODO: remove numEvents: 0, }; const initialTimer: TimerState = { - clock: clock.timeNow(), + addedTime: 0, current: null, + duration: null, elapsed: null, expectedFinish: null, // TODO: expected finish could account for midnight, we cleanup in the clients - addedTime: 0, - startedAt: null, finishedAt: null, + playback: Playback.Stop, secondaryTimer: null, - duration: null, - timerType: null, // TODO: remove - endAction: null, // TODO: remove - timeWarning: null, // TODO: remove - timeDanger: null, // TODO: remove + startedAt: null, }; export type TState = DeepReadonly<{ + clock: number; // realtime clock eventNow: OntimeEvent | null; publicEventNow: OntimeEvent | null; eventNext: OntimeEvent | null; publicEventNext: OntimeEvent | null; runtime: Runtime; - playback: Playback; // TODO: merge into timer? - // TODO: these are private state and should not be emitted - timer: TimerState & { + timer: TimerState; + // private properties of the timer calculations + _timer: { pausedAt: MaybeNumber; finishedNow: boolean; lastUpdate: MaybeNumber; @@ -65,14 +58,14 @@ export type TState = DeepReadonly<{ }>; export const state: TState = { + clock: clock.timeNow(), eventNow: null, publicEventNow: null, eventNext: null, publicEventNext: null, runtime: initialRuntime, - playback: Playback.Stop, // TODO: merge into timer? - timer: { - ...initialTimer, + timer: { ...initialTimer }, + _timer: { pausedAt: null, lastUpdate: null, secondaryTarget: null, @@ -90,22 +83,16 @@ export const stateMutations = { const eventIndex = rundown.findIndex((eventInMemory) => eventInMemory.id === event.id); state.runtime.selectedEventIndex = eventIndex; - state.runtime.selectedEventId = event.id; state.runtime.numEvents = rundown.length; this.loadNow(event, rundown); this.loadNext(rundown); - state.timer.clock = clock.timeNow(); - state.playback = Playback.Armed; + state.clock = clock.timeNow(); + state.timer.playback = Playback.Armed; state.timer.duration = calculateDuration(event.timeStart, event.timeEnd); state.timer.current = getCurrent(state); - state.timer.timerType = event.timerType; - state.timer.endAction = event.endAction; - state.timer.timeWarning = event.timeWarning; - state.timer.timeDanger = event.timeDanger; - if (initialData) { stateMutations.timer.patch(initialData); } @@ -118,11 +105,9 @@ export const stateMutations = { // check if current is also public if (event.isPublic) { state.publicEventNow = event; - state.runtime.selectedPublicEventId = event.id; } else { // assume there is no public event state.publicEventNow = null; - state.runtime.selectedPublicEventId = null; // if there is nothing before, return if (!state.runtime.selectedEventIndex) { @@ -133,7 +118,6 @@ export const stateMutations = { for (let i = state.runtime.selectedEventIndex; i >= 0; i--) { if (playableEvents[i].isPublic) { state.publicEventNow = playableEvents[i]; - state.runtime.selectedPublicEventId = playableEvents[i].id; break; } } @@ -145,8 +129,6 @@ export const stateMutations = { // assume there are no next events state.eventNext = null; state.publicEventNext = null; - state.runtime.nextEventId = null; - state.runtime.nextPublicEventId = null; if (state.runtime.selectedEventIndex === null) { return; @@ -162,14 +144,12 @@ export const stateMutations = { // if we have not set private if (!nextProduction) { state.eventNext = playableEvents[i]; - state.runtime.nextEventId = playableEvents[i].id; nextProduction = true; } // if event is public if (playableEvents[i].isPublic) { state.publicEventNext = playableEvents[i]; - state.runtime.nextPublicEventId = playableEvents[i].id; nextPublic = true; } @@ -180,14 +160,8 @@ export const stateMutations = { }); }, resume(restorePoint: RestorePoint, event: OntimeEvent, rundown: OntimeEvent[]) { - mutate((state) => { - const { playback, ...patch } = restorePoint; - - // TODO: this.load gets typed as any? - stateMutations.load(event, rundown, patch); - - // TODO: send as part of the patch when playback is merged to timer - state.playback = playback; + mutate((_state) => { + stateMutations.load(event, rundown, restorePoint); }); }, /** @@ -202,21 +176,17 @@ export const stateMutations = { // update data which is duplicate between eventNow and timer objects state.timer.duration = calculateDuration(state.eventNow.timeStart, state.eventNow.timeEnd); state.timer.expectedFinish = getExpectedFinish(state); - state.timer.timerType = state.eventNow.timerType; - state.timer.endAction = state.eventNow.endAction; return; } - state.playback = Playback.Armed; + state.timer.playback = Playback.Armed; state.timer.duration = calculateDuration(state.eventNow.timeStart, state.eventNow.timeEnd); state.timer.current = state.timer.duration; state.timer.elapsed = null; - state.timer.timerType = state.eventNow.timerType; - state.timer.endAction = state.eventNow.endAction; state.timer.startedAt = null; state.timer.finishedAt = null; - state.timer.pausedAt = null; + state._timer.pausedAt = null; state.timer.addedTime = 0; state.timer.expectedFinish = getExpectedFinish(state); @@ -242,11 +212,10 @@ export const stateMutations = { // TODO: could we avoid having this dependency? state.runtime.numEvents = EventLoader.getPlayableEvents().length; - state.playback = Playback.Stop; - - state.timer = { - ...initialTimer, - clock: clock.timeNow(), + state.timer.playback = Playback.Stop; + state.clock = clock.timeNow(); + state.timer = { ...initialTimer }; + state._timer = { pausedAt: null, lastUpdate: null, secondaryTarget: null, @@ -267,22 +236,22 @@ export const stateMutations = { start() { mutate( (state) => { - state.timer.clock = clock.timeNow(); + state.clock = clock.timeNow(); state.timer.secondaryTimer = null; - state.timer.secondaryTarget = null; + state._timer.secondaryTarget = null; // add paused time if it exists - if (state.timer.pausedAt) { - const timeToAdd = state.timer.clock - state.timer.pausedAt; + if (state._timer.pausedAt) { + const timeToAdd = state.clock - state._timer.pausedAt; state.timer.addedTime += timeToAdd; - state.timer.pausedAt = null; + state._timer.pausedAt = null; } if (state.timer.startedAt === null) { - state.timer.startedAt = state.timer.clock; + state.timer.startedAt = state.clock; } - state.playback = Playback.Play; + state.timer.playback = Playback.Play; state.timer.expectedFinish = getExpectedFinish(state); }, { @@ -307,13 +276,13 @@ export const stateMutations = { pause() { mutate( (state) => { - if (state.playback !== Playback.Play) { + if (state.timer.playback !== Playback.Play) { return false; } - state.playback = Playback.Pause; - state.timer.clock = clock.timeNow(); - state.timer.pausedAt = state.timer.clock; + state.timer.playback = Playback.Pause; + state.clock = clock.timeNow(); + state._timer.pausedAt = state.clock; return true; }, { @@ -327,24 +296,18 @@ export const stateMutations = { }, resume(event: OntimeEvent, restorePoint: RestorePoint) { mutate((state) => { - state.timer.clock = clock.timeNow(); + state.clock = clock.timeNow(); - // TODO: the duplication of timer data would not be necessary - // once event loader is merged here - state.runtime.selectedEventId = event.id; state.timer.startedAt = restorePoint.startedAt; state.timer.duration = calculateDuration(event.timeStart, event.timeEnd); state.timer.current = state.timer.duration; - state.timer.timerType = event.timerType; - state.timer.endAction = event.endAction; - - state.playback = restorePoint.playback; - state.timer.pausedAt = restorePoint.pausedAt; + state.timer.playback = restorePoint.playback; + state._timer.pausedAt = restorePoint.pausedAt; state.timer.addedTime = restorePoint.addedTime; // check if event finished meanwhile - if (state.timer.timerType === TimerType.TimeToEnd) { + if (event.timerType === TimerType.TimeToEnd) { state.timer.current = getCurrent(state); } }); @@ -388,10 +351,6 @@ export const stateMutations = { state.timer.secondaryTimer = updatedSecondaryTimer; state.timer.elapsed = state.timer.duration - state.timer.current; - if (isFinished) { - state.runtime.selectedEventId = null; - } - return { doRoll: doRollLoad, isFinished }; } @@ -399,8 +358,8 @@ export const stateMutations = { let isFinished = false; state.timer.current = getCurrent(state); - if (state.playback === Playback.Play && state.timer.finishedNow) { - state.timer.finishedAt = state.timer.clock; + if (state.timer.playback === Playback.Play && state._timer.finishedNow) { + state.timer.finishedAt = state.clock; isFinished = true; } else { state.timer.expectedFinish = getExpectedFinish(state); @@ -419,16 +378,16 @@ export const stateMutations = { let _isFinished = false; let _shouldNotify = false; - const previousTime = state.timer.clock; - state.timer.clock = clock.timeNow(); - const hasSkippedBack = previousTime > state.timer.clock; + const previousTime = state.clock; + state.clock = clock.timeNow(); + const hasSkippedBack = previousTime > state.clock; if (hasSkippedBack) { _force = true; } // we call integrations if we update timers - if (state.playback === Playback.Roll) { + if (state.timer.playback === Playback.Roll) { const result = roll(); _shouldNotify = true; _doRoll = result.doRoll; @@ -442,9 +401,9 @@ export const stateMutations = { // we only update the store at the updateInterval // side effects such as onFinish will still be triggered in the update functions - const isTimeToUpdate = state.timer.clock > state.timer.lastUpdate + updateInterval; + const isTimeToUpdate = state.clock > state._timer.lastUpdate + updateInterval; if (_force || isTimeToUpdate) { - state.timer.lastUpdate = state.timer.clock; + state._timer.lastUpdate = state.clock; // TODO: can we simplify the didUpdate and shouldNotify _didUpdate = true; } @@ -471,13 +430,13 @@ export const stateMutations = { integrationService.dispatch(TimerLifeCycle.onFinish); // handle end action if there was a timer playing - if (newState.playback === Playback.Play) { - if (newState.timer.endAction === EndAction.Stop) { + if (newState.timer.playback === Playback.Play) { + if (newState.eventNow.endAction === EndAction.Stop) { runtimeService.stop(); - } else if (newState.timer.endAction === EndAction.LoadNext) { + } else if (newState.eventNow.endAction === EndAction.LoadNext) { // we need to delay here to put this action in the queue stack. otherwise it won't be executed properly setTimeout(runtimeService.loadNext, 0); - } else if (newState.timer.endAction === EndAction.PlayNext) { + } else if (newState.eventNow.endAction === EndAction.PlayNext) { runtimeService.startNext(); } } @@ -490,12 +449,12 @@ export const stateMutations = { mutate((state) => { stateMutations.timer.clear(); - const { nextEvent, currentEvent } = getRollTimers(rundown, state.timer.clock); + const { nextEvent, currentEvent } = getRollTimers(rundown, state.clock); if (currentEvent) { // there is something running, load state.timer.secondaryTimer = null; - state.timer.secondaryTarget = null; + state._timer.secondaryTarget = null; // account for event that finishes the day after const endTime = @@ -506,17 +465,16 @@ export const stateMutations = { stateMutations.load(currentEvent, rundown, { startedAt: currentEvent.timeStart, expectedFinish: currentEvent.timeEnd, - current: endTime - state.timer.clock, + current: endTime - state.clock, }); } else if (nextEvent) { // account for day after - const nextStart = - nextEvent.timeStart < state.timer.clock ? nextEvent.timeStart + dayInMs : nextEvent.timeStart; + const nextStart = nextEvent.timeStart < state.clock ? nextEvent.timeStart + dayInMs : nextEvent.timeStart; // nothing now, but something coming up - state.timer.secondaryTimer = nextStart - state.timer.clock; - state.timer.secondaryTarget = nextStart; + state.timer.secondaryTimer = nextStart - state.clock; + state._timer.secondaryTarget = nextStart; } - state.playback = Playback.Roll; + state.timer.playback = Playback.Roll; }); }, }, @@ -559,22 +517,22 @@ export function mutate( // set eventStore any time a mutation happens // this means we are pushing this data to the client every 32ms eventStore.batchSet({ + clock: newState.clock, eventNow: newState.eventNow, publicEventNow: newState.publicEventNow, eventNext: newState.eventNext, publicEventNext: newState.publicEventNext, - loaded: newState.runtime, // TODO: rename to runtime - playback: newState.playback, + runtime: newState.runtime, timer: newState.timer, }); // we write to restore service if the underlying data changes restoreService.save({ - playback: state.playback, - selectedEventId: state.runtime.selectedEventId, + playback: state.timer.playback, + selectedEventId: state.eventNow?.id ?? null, startedAt: state.timer.startedAt, addedTime: state.timer.addedTime, - pausedAt: state.timer.pausedAt, + pausedAt: state._timer.pausedAt, }); return result; diff --git a/packages/types/src/definitions/runtime/Runtime.type.ts b/packages/types/src/definitions/runtime/Runtime.type.ts index b688df240..996a0d96e 100644 --- a/packages/types/src/definitions/runtime/Runtime.type.ts +++ b/packages/types/src/definitions/runtime/Runtime.type.ts @@ -1,8 +1,6 @@ +import { MaybeNumber } from '../../utils/utils.type.js'; + export type Runtime = { numEvents: number; - selectedEventIndex: number | null; - selectedEventId: string | null; - selectedPublicEventId: string | null; - nextEventId: string | null; - nextPublicEventId: string | null; + selectedEventIndex: MaybeNumber; }; diff --git a/packages/types/src/definitions/runtime/RuntimeStore.type.ts b/packages/types/src/definitions/runtime/RuntimeStore.type.ts index 8756ddd8f..9e6c21ec4 100644 --- a/packages/types/src/definitions/runtime/RuntimeStore.type.ts +++ b/packages/types/src/definitions/runtime/RuntimeStore.type.ts @@ -1,20 +1,19 @@ -import { Playback } from './Playback.type.js'; import { MessageState } from './MessageControl.type.js'; import { TimerState } from './TimerState.type.js'; import { Runtime } from './Runtime.type.js'; import { OntimeEvent } from '../core/OntimeEvent.type.js'; export type RuntimeStore = { - // timer service + // timer data + clock: number; timer: TimerState; - playback: Playback; + onAir: boolean; // messages service message: MessageState; - onAir: boolean; - // event loader - loaded: Runtime; + // rundown data + runtime: Runtime; eventNow: OntimeEvent | null; publicEventNow: OntimeEvent | null; eventNext: OntimeEvent | null; diff --git a/packages/types/src/definitions/runtime/TimerState.type.ts b/packages/types/src/definitions/runtime/TimerState.type.ts index a8f92a86e..b8195349d 100644 --- a/packages/types/src/definitions/runtime/TimerState.type.ts +++ b/packages/types/src/definitions/runtime/TimerState.type.ts @@ -1,18 +1,14 @@ -import { TimerType } from '../TimerType.type.js'; -import { EndAction } from '../EndAction.type.js'; +import { MaybeNumber } from '../../index.js'; +import { Playback } from './Playback.type.js'; export type TimerState = { - clock: number; // realtime clock - current: number | null; // running countdown - elapsed: number | null; // elapsed time in current timer - expectedFinish: number | null; addedTime: number; // time added by user, can be negative - startedAt: number | null; - finishedAt: number | null; // only if timer has already finished - secondaryTimer: number | null; // used for roll mode - duration: number | null; - timerType: TimerType | null; - endAction: EndAction | null; - timeWarning: number | null; - timeDanger: number | null; + current: MaybeNumber; // running countdown + duration: MaybeNumber; // normalised duration of current event + elapsed: MaybeNumber; // elapsed time in current timer + expectedFinish: MaybeNumber; // time we expect timer to finish + finishedAt: MaybeNumber; // only if timer has already finished + playback: Playback; + secondaryTimer: MaybeNumber; // used for roll mode + startedAt: MaybeNumber; // only if timer has already started };