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'
element={
<ViewLoader>
<ViewNavigationMenu isLockable />
<StudioClock />
</ViewLoader>
}
+13
View File
@@ -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,
}));
+23 -40
View File
@@ -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 <Loader />;
}
if (status === 'error') {
return <EmptyPage text='There was an error fetching data, please refresh the page.' />;
}
return <Studio {...data} />;
}
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({
<ViewParamsEditor target={OntimeView.StudioClock} viewOptions={studioOptions} />
<div className='project-header'>
{general?.logo && <ViewLogo name={general.logo} className='logo' />}
<div className='title'>{general.title}</div>
{projectData?.logo && <ViewLogo name={projectData.logo} className='logo' />}
<div className='title'>{projectData.title}</div>
</div>
<div className={cx(['studio-contents', hideCards && 'studio-contents--onecol'])}>
<StudioClock onAir={onAir} clock={time.clock} hideCards={hideCards} />
{!hideCards && (
<StudioTimers
eventNow={eventNow}
eventNext={eventNext}
timerMessage={message.timer.visible ? message.timer.text : ''}
secondaryMessage={message.secondary}
runtime={runtime}
time={time}
viewSettings={viewSettings}
/>
)}
<StudioClock hideCards={hideCards} />
{!hideCards && <StudioTimers viewSettings={viewSettings} />}
</div>
</div>
);
+12 -4
View File
@@ -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<StudioClockProps, 'hideCards'>) {
interface StudioClockMobileProps {
clock: number;
onAir: boolean;
}
function StudioClockMobile({ clock, onAir }: StudioClockMobileProps) {
const displayClock = formatTime(clock);
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 { 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(
@@ -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]),
};
}