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]),
+ };
+}