From bea7e2bb2bfc68a1a723ec5fd0dec5c9c9653328 Mon Sep 17 00:00:00 2001 From: Carlos Valente Date: Thu, 24 Jul 2025 06:50:34 +0200 Subject: [PATCH] refactor: extract studio data to hook --- apps/client/src/AppRouter.tsx | 1 + apps/client/src/common/hooks/useSocket.ts | 13 ++++ apps/client/src/views/studio/Studio.tsx | 63 +++++++------------ apps/client/src/views/studio/StudioClock.tsx | 16 +++-- apps/client/src/views/studio/StudioTimers.tsx | 25 ++------ apps/client/src/views/studio/useStudioData.ts | 34 ++++++++++ 6 files changed, 89 insertions(+), 63 deletions(-) create mode 100644 apps/client/src/views/studio/useStudioData.ts diff --git a/apps/client/src/AppRouter.tsx b/apps/client/src/AppRouter.tsx index e8879e96a..60d53f9e3 100644 --- a/apps/client/src/AppRouter.tsx +++ b/apps/client/src/AppRouter.tsx @@ -69,6 +69,7 @@ export default function AppRouter() { path='studio' element={ + } diff --git a/apps/client/src/common/hooks/useSocket.ts b/apps/client/src/common/hooks/useSocket.ts index 698949387..0eb2b843b 100644 --- a/apps/client/src/common/hooks/useSocket.ts +++ b/apps/client/src/common/hooks/useSocket.ts @@ -256,3 +256,16 @@ export const useBackstageSocket = createSelector((state: RuntimeStore) => ({ selectedEventId: state.eventNow?.id ?? null, time: state.timer, })); + +export const useStudioClockSocket = createSelector((state: RuntimeStore) => ({ + clock: state.clock, + playback: state.timer.playback, +})); + +export const useStudioTimersSocket = createSelector((state: RuntimeStore) => ({ + eventNext: state.eventNext, + eventNow: state.eventNow, + message: state.message, + time: state.timer, + runtime: state.runtime, +})); diff --git a/apps/client/src/views/studio/Studio.tsx b/apps/client/src/views/studio/Studio.tsx index 9463ad8da..b6f4c5e7c 100644 --- a/apps/client/src/views/studio/Studio.tsx +++ b/apps/client/src/views/studio/Studio.tsx @@ -1,45 +1,38 @@ import { useMemo } from 'react'; -import { MessageState, OntimeEvent, OntimeView, ProjectData, Runtime, Settings, ViewSettings } from 'ontime-types'; +import { OntimeView } from 'ontime-types'; +import EmptyPage from '../../common/components/state/EmptyPage'; import ViewLogo from '../../common/components/view-logo/ViewLogo'; import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor'; import { useWindowTitle } from '../../common/hooks/useWindowTitle'; -import type { ViewExtendedTimer } from '../../common/models/TimeManager.type'; import { cx } from '../../common/utils/styleUtils'; import { getDefaultFormat } from '../../common/utils/time'; +import Loader from '../common/loader/Loader'; import { getStudioOptions, useStudioOptions } from './studio.options'; import StudioClock from './StudioClock'; import StudioTimers from './StudioTimers'; +import { StudioData, useStudioData } from './useStudioData'; import './Studio.scss'; -interface StudioProps { - eventNow: OntimeEvent | null; - eventNext: OntimeEvent | null; - general: ProjectData; - isMirrored: boolean; - message: MessageState; - time: ViewExtendedTimer; - runtime: Runtime; - onAir: boolean; - settings: Settings | undefined; - viewSettings: ViewSettings; +export default function StudioLoader() { + const { data, status } = useStudioData(); + + useWindowTitle('Studio Clock'); + + if (status === 'pending') { + return ; + } + + if (status === 'error') { + return ; + } + + return ; } -export default function Studio({ - eventNow, - eventNext, - general, - isMirrored, - message, - time, - runtime, - onAir, - settings, - viewSettings, -}: StudioProps) { - useWindowTitle('Studio Clock'); +function Studio({ projectData, isMirrored, settings, viewSettings }: StudioData) { const { hideCards } = useStudioOptions(); // gather option data @@ -51,23 +44,13 @@ export default function Studio({
- {general?.logo && } -
{general.title}
+ {projectData?.logo && } +
{projectData.title}
- - {!hideCards && ( - - )} + + {!hideCards && }
); diff --git a/apps/client/src/views/studio/StudioClock.tsx b/apps/client/src/views/studio/StudioClock.tsx index 78e6bd50b..b80142c51 100644 --- a/apps/client/src/views/studio/StudioClock.tsx +++ b/apps/client/src/views/studio/StudioClock.tsx @@ -1,4 +1,7 @@ +import { Playback } from 'ontime-types'; + import { useIsMobileDevice } from '../../common/hooks/useIsMobileDevice'; +import { useStudioClockSocket } from '../../common/hooks/useSocket'; import { cx } from '../../common/utils/styleUtils'; import { formatTime } from '../../common/utils/time'; import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime'; @@ -11,13 +14,13 @@ const activeIndicators = [...Array(12).keys()]; const secondsIndicators = [...Array(60).keys()]; interface StudioClockProps { - onAir: boolean; - clock: number; hideCards: boolean; } -export default function StudioClock({ onAir, clock, hideCards }: StudioClockProps) { +export default function StudioClock({ hideCards }: StudioClockProps) { const isMobile = useIsMobileDevice(); + const { clock, playback } = useStudioClockSocket(); + const onAir = playback !== Playback.Stop; // if we are on mobile and have to show the cards if (isMobile && !hideCards) { @@ -55,7 +58,12 @@ export default function StudioClock({ onAir, clock, hideCards }: StudioClockProp ); } -function StudioClockMobile({ clock, onAir }: Omit) { +interface StudioClockMobileProps { + clock: number; + onAir: boolean; +} + +function StudioClockMobile({ clock, onAir }: StudioClockMobileProps) { const displayClock = formatTime(clock); return ( diff --git a/apps/client/src/views/studio/StudioTimers.tsx b/apps/client/src/views/studio/StudioTimers.tsx index 6cdd4866a..9c2e37102 100644 --- a/apps/client/src/views/studio/StudioTimers.tsx +++ b/apps/client/src/views/studio/StudioTimers.tsx @@ -1,7 +1,7 @@ -import { OntimeEvent, Playback, Runtime, TimerPhase, TimerState, ViewSettings } from 'ontime-types'; +import { Playback, TimerPhase, ViewSettings } from 'ontime-types'; import { millisToString } from 'ontime-utils'; -import { useAuxTimersTime } from '../../common/hooks/useSocket'; +import { useAuxTimersTime, useStudioTimersSocket } from '../../common/hooks/useSocket'; import { getOffsetState } from '../../common/utils/offset'; import { cx } from '../../common/utils/styleUtils'; import { useTranslation } from '../../translation/TranslationProvider'; @@ -12,31 +12,18 @@ import { getFormattedEventData, getFormattedScheduleTimes } from './studioTimers import './StudioTimers.scss'; interface StudioTimersProps { - runtime: Runtime; - time: TimerState; - eventNow: OntimeEvent | null; - eventNext: OntimeEvent | null; - timerMessage: string; - secondaryMessage: string; viewSettings: ViewSettings; } -export default function StudioTimers({ - runtime, - time, - eventNow, - eventNext, - timerMessage, - secondaryMessage, - viewSettings, -}: StudioTimersProps) { +export default function StudioTimers({ viewSettings }: StudioTimersProps) { const { getLocalizedString } = useTranslation(); + const { eventNow, eventNext, message, time, runtime } = useStudioTimersSocket(); const schedule = getFormattedScheduleTimes(runtime); const event = getFormattedEventData(eventNow, time); const eventNextTitle = eventNext?.title || '-'; - const formattedTimerMessage = timerMessage || '-'; - const formattedSecondaryMessage = secondaryMessage || '-'; + const formattedTimerMessage = (message.timer.visible && message.timer.text) || '-'; + const formattedSecondaryMessage = message.secondary || '-'; // gather presentation styles const timerColour = getTimerColour( diff --git a/apps/client/src/views/studio/useStudioData.ts b/apps/client/src/views/studio/useStudioData.ts new file mode 100644 index 000000000..4065ef8ff --- /dev/null +++ b/apps/client/src/views/studio/useStudioData.ts @@ -0,0 +1,34 @@ +import { ProjectData, Settings, ViewSettings } from 'ontime-types'; + +import useProjectData from '../../common/hooks-query/useProjectData'; +import useSettings from '../../common/hooks-query/useSettings'; +import useViewSettings from '../../common/hooks-query/useViewSettings'; +import { useViewOptionsStore } from '../../common/stores/viewOptions'; +import { aggregateQueryStatus, ViewData } from '../utils/viewLoader.utils'; + +export interface StudioData { + projectData: ProjectData; + isMirrored: boolean; + settings: Settings; + viewSettings: ViewSettings; +} + +export function useStudioData(): ViewData { + // persisted app state + const isMirrored = useViewOptionsStore((state) => state.mirror); + + // HTTP API data + const { data: projectData, status: projectDataStatus } = useProjectData(); + const { data: viewSettings, status: viewSettingsStatus } = useViewSettings(); + const { data: settings, status: settingsStatus } = useSettings(); + + return { + data: { + projectData, + isMirrored, + settings, + viewSettings, + }, + status: aggregateQueryStatus([projectDataStatus, viewSettingsStatus, settingsStatus]), + }; +}