refactor: extract studio data to hook

This commit is contained in:
Carlos Valente
2025-07-24 06:50:34 +02:00
committed by Carlos Valente
parent ad7f658f15
commit bea7e2bb2b
6 changed files with 89 additions and 63 deletions
+1
View File
@@ -69,6 +69,7 @@ export default function AppRouter() {
path='studio' path='studio'
element={ element={
<ViewLoader> <ViewLoader>
<ViewNavigationMenu isLockable />
<StudioClock /> <StudioClock />
</ViewLoader> </ViewLoader>
} }
+13
View File
@@ -256,3 +256,16 @@ export const useBackstageSocket = createSelector((state: RuntimeStore) => ({
selectedEventId: state.eventNow?.id ?? null, selectedEventId: state.eventNow?.id ?? null,
time: state.timer, 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,
}));
+23 -40
View File
@@ -1,45 +1,38 @@
import { useMemo } from 'react'; 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 ViewLogo from '../../common/components/view-logo/ViewLogo';
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor'; import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
import { useWindowTitle } from '../../common/hooks/useWindowTitle'; import { useWindowTitle } from '../../common/hooks/useWindowTitle';
import type { ViewExtendedTimer } from '../../common/models/TimeManager.type';
import { cx } from '../../common/utils/styleUtils'; import { cx } from '../../common/utils/styleUtils';
import { getDefaultFormat } from '../../common/utils/time'; import { getDefaultFormat } from '../../common/utils/time';
import Loader from '../common/loader/Loader';
import { getStudioOptions, useStudioOptions } from './studio.options'; import { getStudioOptions, useStudioOptions } from './studio.options';
import StudioClock from './StudioClock'; import StudioClock from './StudioClock';
import StudioTimers from './StudioTimers'; import StudioTimers from './StudioTimers';
import { StudioData, useStudioData } from './useStudioData';
import './Studio.scss'; import './Studio.scss';
interface StudioProps { export default function StudioLoader() {
eventNow: OntimeEvent | null; const { data, status } = useStudioData();
eventNext: OntimeEvent | null;
general: ProjectData; useWindowTitle('Studio Clock');
isMirrored: boolean;
message: MessageState; if (status === 'pending') {
time: ViewExtendedTimer; return <Loader />;
runtime: Runtime; }
onAir: boolean;
settings: Settings | undefined; if (status === 'error') {
viewSettings: ViewSettings; return <EmptyPage text='There was an error fetching data, please refresh the page.' />;
}
return <Studio {...data} />;
} }
export default function Studio({ function Studio({ projectData, isMirrored, settings, viewSettings }: StudioData) {
eventNow,
eventNext,
general,
isMirrored,
message,
time,
runtime,
onAir,
settings,
viewSettings,
}: StudioProps) {
useWindowTitle('Studio Clock');
const { hideCards } = useStudioOptions(); const { hideCards } = useStudioOptions();
// gather option data // gather option data
@@ -51,23 +44,13 @@ export default function Studio({
<ViewParamsEditor target={OntimeView.StudioClock} viewOptions={studioOptions} /> <ViewParamsEditor target={OntimeView.StudioClock} viewOptions={studioOptions} />
<div className='project-header'> <div className='project-header'>
{general?.logo && <ViewLogo name={general.logo} className='logo' />} {projectData?.logo && <ViewLogo name={projectData.logo} className='logo' />}
<div className='title'>{general.title}</div> <div className='title'>{projectData.title}</div>
</div> </div>
<div className={cx(['studio-contents', hideCards && 'studio-contents--onecol'])}> <div className={cx(['studio-contents', hideCards && 'studio-contents--onecol'])}>
<StudioClock onAir={onAir} clock={time.clock} hideCards={hideCards} /> <StudioClock hideCards={hideCards} />
{!hideCards && ( {!hideCards && <StudioTimers viewSettings={viewSettings} />}
<StudioTimers
eventNow={eventNow}
eventNext={eventNext}
timerMessage={message.timer.visible ? message.timer.text : ''}
secondaryMessage={message.secondary}
runtime={runtime}
time={time}
viewSettings={viewSettings}
/>
)}
</div> </div>
</div> </div>
); );
+12 -4
View File
@@ -1,4 +1,7 @@
import { Playback } from 'ontime-types';
import { useIsMobileDevice } from '../../common/hooks/useIsMobileDevice'; import { useIsMobileDevice } from '../../common/hooks/useIsMobileDevice';
import { useStudioClockSocket } from '../../common/hooks/useSocket';
import { cx } from '../../common/utils/styleUtils'; import { cx } from '../../common/utils/styleUtils';
import { formatTime } from '../../common/utils/time'; import { formatTime } from '../../common/utils/time';
import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime'; import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime';
@@ -11,13 +14,13 @@ const activeIndicators = [...Array(12).keys()];
const secondsIndicators = [...Array(60).keys()]; const secondsIndicators = [...Array(60).keys()];
interface StudioClockProps { interface StudioClockProps {
onAir: boolean;
clock: number;
hideCards: boolean; hideCards: boolean;
} }
export default function StudioClock({ onAir, clock, hideCards }: StudioClockProps) { export default function StudioClock({ hideCards }: StudioClockProps) {
const isMobile = useIsMobileDevice(); const isMobile = useIsMobileDevice();
const { clock, playback } = useStudioClockSocket();
const onAir = playback !== Playback.Stop;
// if we are on mobile and have to show the cards // if we are on mobile and have to show the cards
if (isMobile && !hideCards) { if (isMobile && !hideCards) {
@@ -55,7 +58,12 @@ export default function StudioClock({ onAir, clock, hideCards }: StudioClockProp
); );
} }
function StudioClockMobile({ clock, onAir }: Omit<StudioClockProps, 'hideCards'>) { interface StudioClockMobileProps {
clock: number;
onAir: boolean;
}
function StudioClockMobile({ clock, onAir }: StudioClockMobileProps) {
const displayClock = formatTime(clock); const displayClock = formatTime(clock);
return ( return (
+6 -19
View File
@@ -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 { millisToString } from 'ontime-utils';
import { useAuxTimersTime } from '../../common/hooks/useSocket'; import { useAuxTimersTime, useStudioTimersSocket } from '../../common/hooks/useSocket';
import { getOffsetState } from '../../common/utils/offset'; import { getOffsetState } from '../../common/utils/offset';
import { cx } from '../../common/utils/styleUtils'; import { cx } from '../../common/utils/styleUtils';
import { useTranslation } from '../../translation/TranslationProvider'; import { useTranslation } from '../../translation/TranslationProvider';
@@ -12,31 +12,18 @@ import { getFormattedEventData, getFormattedScheduleTimes } from './studioTimers
import './StudioTimers.scss'; import './StudioTimers.scss';
interface StudioTimersProps { interface StudioTimersProps {
runtime: Runtime;
time: TimerState;
eventNow: OntimeEvent | null;
eventNext: OntimeEvent | null;
timerMessage: string;
secondaryMessage: string;
viewSettings: ViewSettings; viewSettings: ViewSettings;
} }
export default function StudioTimers({ export default function StudioTimers({ viewSettings }: StudioTimersProps) {
runtime,
time,
eventNow,
eventNext,
timerMessage,
secondaryMessage,
viewSettings,
}: StudioTimersProps) {
const { getLocalizedString } = useTranslation(); const { getLocalizedString } = useTranslation();
const { eventNow, eventNext, message, time, runtime } = useStudioTimersSocket();
const schedule = getFormattedScheduleTimes(runtime); const schedule = getFormattedScheduleTimes(runtime);
const event = getFormattedEventData(eventNow, time); const event = getFormattedEventData(eventNow, time);
const eventNextTitle = eventNext?.title || '-'; const eventNextTitle = eventNext?.title || '-';
const formattedTimerMessage = timerMessage || '-'; const formattedTimerMessage = (message.timer.visible && message.timer.text) || '-';
const formattedSecondaryMessage = secondaryMessage || '-'; const formattedSecondaryMessage = message.secondary || '-';
// gather presentation styles // gather presentation styles
const timerColour = getTimerColour( const timerColour = getTimerColour(
@@ -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<StudioData> {
// 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]),
};
}