mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-07 07:19:16 +00:00
refactor: extract backstage data to hook
This commit is contained in:
committed by
Carlos Valente
parent
f23adfa45c
commit
ad7f658f15
@@ -60,6 +60,7 @@ export default function AppRouter() {
|
|||||||
path='backstage'
|
path='backstage'
|
||||||
element={
|
element={
|
||||||
<ViewLoader>
|
<ViewLoader>
|
||||||
|
<ViewNavigationMenu isLockable />
|
||||||
<Backstage />
|
<Backstage />
|
||||||
</ViewLoader>
|
</ViewLoader>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,3 +248,11 @@ export const useCountdownSocket = createSelector((state: RuntimeStore) => ({
|
|||||||
current: state.timer.current,
|
current: state.timer.current,
|
||||||
clock: state.clock,
|
clock: state.clock,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const useBackstageSocket = createSelector((state: RuntimeStore) => ({
|
||||||
|
eventNext: state.eventNext,
|
||||||
|
eventNow: state.eventNow,
|
||||||
|
runtime: state.runtime,
|
||||||
|
selectedEventId: state.eventNow?.id ?? null,
|
||||||
|
time: state.timer,
|
||||||
|
}));
|
||||||
|
|||||||
@@ -1,59 +1,53 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import QRCode from 'react-qr-code';
|
import QRCode from 'react-qr-code';
|
||||||
import { useViewportSize } from '@mantine/hooks';
|
import { useViewportSize } from '@mantine/hooks';
|
||||||
import { CustomFields, OntimeEvent, OntimeView, ProjectData, Runtime, Settings } from 'ontime-types';
|
import { OntimeView, ProjectData } from 'ontime-types';
|
||||||
import { millisToString, removeLeadingZero } from 'ontime-utils';
|
import { millisToString, removeLeadingZero } from 'ontime-utils';
|
||||||
|
|
||||||
import ProgressBar from '../../common/components/progress-bar/ProgressBar';
|
import ProgressBar from '../../common/components/progress-bar/ProgressBar';
|
||||||
import Empty from '../../common/components/state/Empty';
|
import Empty from '../../common/components/state/Empty';
|
||||||
|
import EmptyPage from '../../common/components/state/EmptyPage';
|
||||||
import TitleCard from '../../common/components/title-card/TitleCard';
|
import TitleCard from '../../common/components/title-card/TitleCard';
|
||||||
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 { useBackstageSocket, useClock } from '../../common/hooks/useSocket';
|
||||||
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
||||||
import { ViewExtendedTimer } from '../../common/models/TimeManager.type';
|
|
||||||
import { cx, timerPlaceholderMin } from '../../common/utils/styleUtils';
|
import { cx, timerPlaceholderMin } from '../../common/utils/styleUtils';
|
||||||
import { formatTime, getDefaultFormat } from '../../common/utils/time';
|
import { formatTime, getDefaultFormat } from '../../common/utils/time';
|
||||||
import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime';
|
import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime';
|
||||||
import { useTranslation } from '../../translation/TranslationProvider';
|
import { useTranslation } from '../../translation/TranslationProvider';
|
||||||
|
import Loader from '../common/loader/Loader';
|
||||||
import ScheduleExport from '../common/schedule/ScheduleExport';
|
import ScheduleExport from '../common/schedule/ScheduleExport';
|
||||||
|
|
||||||
import { getBackstageOptions, useBackstageOptions } from './backstage.options';
|
import { getBackstageOptions, useBackstageOptions } from './backstage.options';
|
||||||
import { getCardData, getIsPendingStart, getShowProgressBar, isOvertime } from './backstage.utils';
|
import { getCardData, getIsPendingStart, getShowProgressBar, isOvertime } from './backstage.utils';
|
||||||
|
import { BackstageData, useBackstageData } from './useBackstageData';
|
||||||
|
|
||||||
import './Backstage.scss';
|
import './Backstage.scss';
|
||||||
|
|
||||||
interface BackstageProps {
|
export default function BackstageLoader() {
|
||||||
events: OntimeEvent[];
|
const { data, status } = useBackstageData();
|
||||||
customFields: CustomFields;
|
|
||||||
eventNext: OntimeEvent | null;
|
|
||||||
eventNow: OntimeEvent | null;
|
|
||||||
general: ProjectData;
|
|
||||||
isMirrored: boolean;
|
|
||||||
time: ViewExtendedTimer;
|
|
||||||
runtime: Runtime;
|
|
||||||
selectedId: string | null;
|
|
||||||
settings: Settings | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Backstage({
|
|
||||||
events,
|
|
||||||
customFields,
|
|
||||||
eventNext,
|
|
||||||
eventNow,
|
|
||||||
general,
|
|
||||||
time,
|
|
||||||
isMirrored,
|
|
||||||
runtime,
|
|
||||||
selectedId,
|
|
||||||
settings,
|
|
||||||
}: BackstageProps) {
|
|
||||||
const { getLocalizedString } = useTranslation();
|
|
||||||
const { secondarySource, extraInfo } = useBackstageOptions();
|
|
||||||
const [blinkClass, setBlinkClass] = useState(false);
|
|
||||||
const { height: screenHeight } = useViewportSize();
|
|
||||||
|
|
||||||
useWindowTitle('Backstage');
|
useWindowTitle('Backstage');
|
||||||
|
|
||||||
|
if (status === 'pending') {
|
||||||
|
return <Loader />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'error') {
|
||||||
|
return <EmptyPage text='There was an error fetching data, please refresh the page.' />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Backstage {...data} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Backstage({ events, customFields, projectData, isMirrored, settings }: BackstageData) {
|
||||||
|
const { getLocalizedString } = useTranslation();
|
||||||
|
const { secondarySource, extraInfo } = useBackstageOptions();
|
||||||
|
const { eventNext, eventNow, runtime, selectedEventId, time } = useBackstageSocket();
|
||||||
|
const [blinkClass, setBlinkClass] = useState(false);
|
||||||
|
const { height: screenHeight } = useViewportSize();
|
||||||
|
|
||||||
// blink on change
|
// blink on change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setBlinkClass(false);
|
setBlinkClass(false);
|
||||||
@@ -63,7 +57,7 @@ export default function Backstage({
|
|||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
return () => clearTimeout(timer);
|
return () => clearTimeout(timer);
|
||||||
}, [selectedId]);
|
}, [selectedEventId]);
|
||||||
|
|
||||||
// gather card data
|
// gather card data
|
||||||
const hasEvents = events.length > 0;
|
const hasEvents = events.length > 0;
|
||||||
@@ -76,7 +70,6 @@ export default function Backstage({
|
|||||||
);
|
);
|
||||||
|
|
||||||
// gather timer data
|
// gather timer data
|
||||||
const clock = formatTime(time.clock);
|
|
||||||
const isPendingStart = getIsPendingStart(time.playback, time.phase);
|
const isPendingStart = getIsPendingStart(time.playback, time.phase);
|
||||||
const startedAt = isPendingStart ? formatTime(time.secondaryTimer) : formatTime(time.startedAt);
|
const startedAt = isPendingStart ? formatTime(time.secondaryTimer) : formatTime(time.startedAt);
|
||||||
|
|
||||||
@@ -104,20 +97,17 @@ export default function Backstage({
|
|||||||
// gather option data
|
// gather option data
|
||||||
const defaultFormat = getDefaultFormat(settings?.timeFormat);
|
const defaultFormat = getDefaultFormat(settings?.timeFormat);
|
||||||
const backstageOptions = useMemo(
|
const backstageOptions = useMemo(
|
||||||
() => getBackstageOptions(defaultFormat, customFields, general),
|
() => getBackstageOptions(defaultFormat, customFields, projectData),
|
||||||
[defaultFormat, customFields, general],
|
[defaultFormat, customFields, projectData],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`backstage ${isMirrored ? 'mirror' : ''}`} data-testid='backstage-view'>
|
<div className={`backstage ${isMirrored ? 'mirror' : ''}`} data-testid='backstage-view'>
|
||||||
<ViewParamsEditor target={OntimeView.Backstage} viewOptions={backstageOptions} />
|
<ViewParamsEditor target={OntimeView.Backstage} viewOptions={backstageOptions} />
|
||||||
<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 className='clock-container'>
|
<BackstageClock />
|
||||||
<div className='label'>{getLocalizedString('common.time_now')}</div>
|
|
||||||
<SuperscriptTime time={clock} className='time' />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showProgress && <ProgressBar className='progress-container' current={time.current} duration={time.duration} />}
|
{showProgress && <ProgressBar className='progress-container' current={time.current} duration={time.duration} />}
|
||||||
@@ -177,13 +167,13 @@ export default function Backstage({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showSchedule && <ScheduleExport selectedId={selectedId} />}
|
{showSchedule && <ScheduleExport selectedId={selectedEventId} />}
|
||||||
|
|
||||||
<div className={cx(['info', !showSchedule && 'info--stretch'])}>
|
<div className={cx(['info', !showSchedule && 'info--stretch'])}>
|
||||||
{extraInfo && <ExtraInfo projectData={general} size={qrSize} source={extraInfo} />}
|
{extraInfo && <ExtraInfo projectData={projectData} size={qrSize} source={extraInfo} />}
|
||||||
<div className='info-card'>
|
<div className='info-card'>
|
||||||
{general.url && <QRCode value={general.url} size={qrSize} level='L' className='info-card__qr' />}
|
{projectData.url && <QRCode value={projectData.url} size={qrSize} level='L' className='info-card__qr' />}
|
||||||
{general.info && <div className='info-card__message'>{general.info}</div>}
|
{projectData.info && <div className='info-card__message'>{projectData.info}</div>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -222,3 +212,18 @@ function ExtraInfo({ projectData, size, source }: ExtraInfoProps) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function BackstageClock() {
|
||||||
|
const { getLocalizedString } = useTranslation();
|
||||||
|
const { clock } = useClock();
|
||||||
|
|
||||||
|
// gather timer data
|
||||||
|
const formattedClock = formatTime(clock);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='clock-container'>
|
||||||
|
<div className='label'>{getLocalizedString('common.time_now')}</div>
|
||||||
|
<SuperscriptTime time={formattedClock} className='time' />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { CustomFields, OntimeEntry, ProjectData, Settings } from 'ontime-types';
|
||||||
|
|
||||||
|
import useCustomFields from '../../common/hooks-query/useCustomFields';
|
||||||
|
import useProjectData from '../../common/hooks-query/useProjectData';
|
||||||
|
import { useFlatRundown } from '../../common/hooks-query/useRundown';
|
||||||
|
import useSettings from '../../common/hooks-query/useSettings';
|
||||||
|
import { useViewOptionsStore } from '../../common/stores/viewOptions';
|
||||||
|
import { aggregateQueryStatus, ViewData } from '../utils/viewLoader.utils';
|
||||||
|
|
||||||
|
export interface BackstageData {
|
||||||
|
events: OntimeEntry[];
|
||||||
|
customFields: CustomFields;
|
||||||
|
projectData: ProjectData;
|
||||||
|
isMirrored: boolean;
|
||||||
|
settings: Settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useBackstageData(): ViewData<BackstageData> {
|
||||||
|
// persisted app state
|
||||||
|
const isMirrored = useViewOptionsStore((state) => state.mirror);
|
||||||
|
|
||||||
|
// HTTP API data
|
||||||
|
const { data: rundownData, status: rundownStatus } = useFlatRundown();
|
||||||
|
const { data: projectData, status: projectDataStatus } = useProjectData();
|
||||||
|
const { data: settings, status: settingsStatus } = useSettings();
|
||||||
|
const { data: customFields, status: customFieldsStatus } = useCustomFields();
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
events: rundownData,
|
||||||
|
customFields,
|
||||||
|
projectData,
|
||||||
|
isMirrored,
|
||||||
|
settings,
|
||||||
|
},
|
||||||
|
status: aggregateQueryStatus([rundownStatus, projectDataStatus, settingsStatus, customFieldsStatus]),
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user