mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-20 14:39:06 +00:00
refactor: pass events instead of derived data to titles (#493)
* refactor: pass events instead of derived data to titles
This commit is contained in:
@@ -79,7 +79,8 @@ export const setPlayback = {
|
|||||||
|
|
||||||
export const useInfoPanel = () => {
|
export const useInfoPanel = () => {
|
||||||
const featureSelector = (state: RuntimeStore) => ({
|
const featureSelector = (state: RuntimeStore) => ({
|
||||||
titles: state.titles,
|
eventNow: state.eventNow,
|
||||||
|
eventNext: state.eventNext,
|
||||||
playback: state.playback,
|
playback: state.playback,
|
||||||
selectedEventIndex: state.loaded.selectedEventIndex,
|
selectedEventIndex: state.loaded.selectedEventIndex,
|
||||||
numEvents: state.loaded.numEvents,
|
numEvents: state.loaded.numEvents,
|
||||||
@@ -94,7 +95,7 @@ export const useCuesheet = () => {
|
|||||||
selectedEventId: state.loaded.selectedEventId,
|
selectedEventId: state.loaded.selectedEventId,
|
||||||
selectedEventIndex: state.loaded.selectedEventIndex,
|
selectedEventIndex: state.loaded.selectedEventIndex,
|
||||||
numEvents: state.loaded.numEvents,
|
numEvents: state.loaded.numEvents,
|
||||||
titleNow: state.titles.titleNow,
|
titleNow: state.eventNow?.title || '',
|
||||||
});
|
});
|
||||||
|
|
||||||
return useRuntimeStore(featureSelector, deepCompare);
|
return useRuntimeStore(featureSelector, deepCompare);
|
||||||
|
|||||||
@@ -42,26 +42,10 @@ export const runtimeStorePlaceholder = {
|
|||||||
nextEventId: null,
|
nextEventId: null,
|
||||||
nextPublicEventId: null,
|
nextPublicEventId: null,
|
||||||
},
|
},
|
||||||
titles: {
|
eventNow: null,
|
||||||
titleNow: null,
|
eventNext: null,
|
||||||
subtitleNow: null,
|
publicEventNow: null,
|
||||||
presenterNow: null,
|
publicEventNext: null,
|
||||||
noteNow: null,
|
|
||||||
titleNext: null,
|
|
||||||
subtitleNext: null,
|
|
||||||
presenterNext: null,
|
|
||||||
noteNext: null,
|
|
||||||
},
|
|
||||||
titlesPublic: {
|
|
||||||
titleNow: null,
|
|
||||||
subtitleNow: null,
|
|
||||||
presenterNow: null,
|
|
||||||
noteNow: null,
|
|
||||||
titleNext: null,
|
|
||||||
subtitleNext: null,
|
|
||||||
presenterNext: null,
|
|
||||||
noteNext: null,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const runtime = createStore<RuntimeStore>(() => ({
|
export const runtime = createStore<RuntimeStore>(() => ({
|
||||||
|
|||||||
@@ -87,18 +87,6 @@ export const connectSocket = (preferredClientName?: string) => {
|
|||||||
runtime.setState(state);
|
runtime.setState(state);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'ontime-titles': {
|
|
||||||
const state = runtime.getState();
|
|
||||||
state.titles = payload;
|
|
||||||
runtime.setState(state);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'ontime-titlesPublic': {
|
|
||||||
const state = runtime.getState();
|
|
||||||
state.titlesPublic = payload;
|
|
||||||
runtime.setState(state);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'ontime-timerMessage': {
|
case 'ontime-timerMessage': {
|
||||||
const state = runtime.getState();
|
const state = runtime.getState();
|
||||||
state.timerMessage = payload;
|
state.timerMessage = payload;
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ export default function Info() {
|
|||||||
const showNif = useEditorSettings((state) => state.eventSettings.showNif);
|
const showNif = useEditorSettings((state) => state.eventSettings.showNif);
|
||||||
|
|
||||||
const titlesNow = {
|
const titlesNow = {
|
||||||
title: data.titles.titleNow || '',
|
title: data.eventNow?.title || '',
|
||||||
subtitle: data.titles.subtitleNow || '',
|
subtitle: data.eventNow?.subtitle || '',
|
||||||
presenter: data.titles.presenterNow || '',
|
presenter: data.eventNow?.presenter || '',
|
||||||
note: data.titles.noteNow || '',
|
note: data.eventNow?.note || '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const titlesNext = {
|
const titlesNext = {
|
||||||
title: data.titles.titleNext || '',
|
title: data.eventNext?.title || '',
|
||||||
subtitle: data.titles.subtitleNext || '',
|
subtitle: data.eventNext?.subtitle || '',
|
||||||
presenter: data.titles.presenterNext || '',
|
presenter: data.eventNext?.presenter || '',
|
||||||
note: data.titles.noteNext || '',
|
note: data.eventNext?.note || '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const selected = !data.numEvents
|
const selected = !data.numEvents
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable react/display-name */
|
/* eslint-disable react/display-name */
|
||||||
import { ComponentType, useMemo } from 'react';
|
import { ComponentType, useMemo } from 'react';
|
||||||
import { TitleBlock } from 'ontime-types';
|
|
||||||
import { useStore } from 'zustand';
|
import { useStore } from 'zustand';
|
||||||
|
|
||||||
import useEventData from '../../common/hooks-query/useEventData';
|
import useEventData from '../../common/hooks-query/useEventData';
|
||||||
@@ -9,8 +8,6 @@ import useViewSettings from '../../common/hooks-query/useViewSettings';
|
|||||||
import { runtime } from '../../common/stores/runtime';
|
import { runtime } from '../../common/stores/runtime';
|
||||||
import { useViewOptionsStore } from '../../common/stores/viewOptions';
|
import { useViewOptionsStore } from '../../common/stores/viewOptions';
|
||||||
|
|
||||||
export type TitleManager = TitleBlock & { showNow: boolean; showNext: boolean };
|
|
||||||
|
|
||||||
const withData = <P extends object>(Component: ComponentType<P>) => {
|
const withData = <P extends object>(Component: ComponentType<P>) => {
|
||||||
return (props: Partial<P>) => {
|
return (props: Partial<P>) => {
|
||||||
// persisted app state
|
// persisted app state
|
||||||
@@ -30,44 +27,22 @@ const withData = <P extends object>(Component: ComponentType<P>) => {
|
|||||||
|
|
||||||
// websocket data
|
// websocket data
|
||||||
const data = useStore(runtime);
|
const data = useStore(runtime);
|
||||||
const { timer, titles, titlesPublic, publicMessage, timerMessage, lowerMessage, playback, onAir } = data;
|
const {
|
||||||
const publicSelectedId = data.loaded.selectedPublicEventId;
|
timer,
|
||||||
const selectedId = data.loaded.selectedEventId;
|
publicMessage,
|
||||||
const nextId = data.loaded.nextEventId;
|
timerMessage,
|
||||||
|
lowerMessage,
|
||||||
/********************************************/
|
playback,
|
||||||
/*** + titleManager ***/
|
onAir,
|
||||||
/*** WRAP INFORMATION RELATED TO TITLES ***/
|
eventNext,
|
||||||
/*** ---------------------------------- ***/
|
publicEventNext,
|
||||||
/********************************************/
|
publicEventNow,
|
||||||
// is there a now field?
|
eventNow,
|
||||||
let showNow = true;
|
loaded,
|
||||||
if (!titles.titleNow && !titles.subtitleNow && !titles.presenterNow) showNow = false;
|
} = data;
|
||||||
|
const publicSelectedId = loaded.selectedPublicEventId;
|
||||||
// is there a next field?
|
const selectedId = loaded.selectedEventId;
|
||||||
let showNext = true;
|
const nextId = loaded.nextEventId;
|
||||||
if (!titles.titleNext && !titles.subtitleNext && !titles.presenterNext) showNext = false;
|
|
||||||
|
|
||||||
const titleManager: TitleManager = { ...titles, showNow: showNow, showNext: showNext };
|
|
||||||
|
|
||||||
/********************************************/
|
|
||||||
/*** + publicTitleManager ***/
|
|
||||||
/*** WRAP INFORMATION RELATED TO TITLES ***/
|
|
||||||
/*** ---------------------------------- ***/
|
|
||||||
/********************************************/
|
|
||||||
// is there a now field?
|
|
||||||
let showPublicNow = true;
|
|
||||||
if (!titlesPublic.titleNow && !titlesPublic.subtitleNow && !titlesPublic.presenterNow) showPublicNow = false;
|
|
||||||
|
|
||||||
// is there a next field?
|
|
||||||
let showPublicNext = true;
|
|
||||||
if (!titlesPublic.titleNext && !titlesPublic.subtitleNext && !titlesPublic.presenterNext) showPublicNext = false;
|
|
||||||
|
|
||||||
const publicTitleManager: TitleManager = {
|
|
||||||
...titlesPublic,
|
|
||||||
showNow: showPublicNow,
|
|
||||||
showNext: showPublicNext,
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************/
|
/******************************************/
|
||||||
/*** + TimeManagerType ***/
|
/*** + TimeManagerType ***/
|
||||||
@@ -75,9 +50,6 @@ const withData = <P extends object>(Component: ComponentType<P>) => {
|
|||||||
/*** -------------------------------- ***/
|
/*** -------------------------------- ***/
|
||||||
/******************************************/
|
/******************************************/
|
||||||
|
|
||||||
// inject info:
|
|
||||||
// is timer finished
|
|
||||||
// get clock string
|
|
||||||
const TimeManagerType = {
|
const TimeManagerType = {
|
||||||
...timer,
|
...timer,
|
||||||
playback,
|
playback,
|
||||||
@@ -95,8 +67,10 @@ const withData = <P extends object>(Component: ComponentType<P>) => {
|
|||||||
pres={timerMessage}
|
pres={timerMessage}
|
||||||
publ={publicMessage}
|
publ={publicMessage}
|
||||||
lower={lowerMessage}
|
lower={lowerMessage}
|
||||||
title={titleManager}
|
eventNow={eventNow}
|
||||||
publicTitle={publicTitleManager}
|
publicEventNow={publicEventNow}
|
||||||
|
eventNext={eventNext}
|
||||||
|
publicEventNext={publicEventNext}
|
||||||
time={TimeManagerType}
|
time={TimeManagerType}
|
||||||
events={publicEvents}
|
events={publicEvents}
|
||||||
backstageEvents={rundownData}
|
backstageEvents={rundownData}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
|||||||
import { formatTime } from '../../../common/utils/time';
|
import { formatTime } from '../../../common/utils/time';
|
||||||
import { useTranslation } from '../../../translation/TranslationProvider';
|
import { useTranslation } from '../../../translation/TranslationProvider';
|
||||||
import { titleVariants } from '../common/animation';
|
import { titleVariants } from '../common/animation';
|
||||||
import { TitleManager } from '../ViewWrapper';
|
|
||||||
|
|
||||||
import './Backstage.scss';
|
import './Backstage.scss';
|
||||||
|
|
||||||
@@ -30,7 +29,8 @@ const formatOptions = {
|
|||||||
interface BackstageProps {
|
interface BackstageProps {
|
||||||
isMirrored: boolean;
|
isMirrored: boolean;
|
||||||
publ: Message;
|
publ: Message;
|
||||||
title: TitleManager;
|
eventNow: OntimeEvent | null;
|
||||||
|
eventNext: OntimeEvent | null;
|
||||||
time: TimeManagerType;
|
time: TimeManagerType;
|
||||||
backstageEvents: OntimeEvent[];
|
backstageEvents: OntimeEvent[];
|
||||||
selectedId: string | null;
|
selectedId: string | null;
|
||||||
@@ -39,7 +39,7 @@ interface BackstageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Backstage(props: BackstageProps) {
|
export default function Backstage(props: BackstageProps) {
|
||||||
const { isMirrored, publ, title, time, backstageEvents, selectedId, general, viewSettings } = props;
|
const { isMirrored, publ, eventNow, eventNext, time, backstageEvents, selectedId, general, viewSettings } = props;
|
||||||
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
||||||
const { getLocalizedString } = useTranslation();
|
const { getLocalizedString } = useTranslation();
|
||||||
const [blinkClass, setBlinkClass] = useState(false);
|
const [blinkClass, setBlinkClass] = useState(false);
|
||||||
@@ -110,7 +110,7 @@ export default function Backstage(props: BackstageProps) {
|
|||||||
|
|
||||||
<div className='now-container'>
|
<div className='now-container'>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{title.showNow && (
|
{eventNow && (
|
||||||
<motion.div
|
<motion.div
|
||||||
className={`event now ${blinkClass ? 'blink' : ''}`}
|
className={`event now ${blinkClass ? 'blink' : ''}`}
|
||||||
key='now'
|
key='now'
|
||||||
@@ -121,9 +121,9 @@ export default function Backstage(props: BackstageProps) {
|
|||||||
>
|
>
|
||||||
<TitleCard
|
<TitleCard
|
||||||
label='now'
|
label='now'
|
||||||
title={title.titleNow}
|
title={eventNow.title}
|
||||||
subtitle={title.subtitleNow}
|
subtitle={eventNow.subtitle}
|
||||||
presenter={title.presenterNow}
|
presenter={eventNow.presenter}
|
||||||
/>
|
/>
|
||||||
<div className='timer-group'>
|
<div className='timer-group'>
|
||||||
<div className='aux-timers'>
|
<div className='aux-timers'>
|
||||||
@@ -144,7 +144,7 @@ export default function Backstage(props: BackstageProps) {
|
|||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{title.showNext && (
|
{eventNext && (
|
||||||
<motion.div
|
<motion.div
|
||||||
className='event next'
|
className='event next'
|
||||||
key='next'
|
key='next'
|
||||||
@@ -155,9 +155,9 @@ export default function Backstage(props: BackstageProps) {
|
|||||||
>
|
>
|
||||||
<TitleCard
|
<TitleCard
|
||||||
label='next'
|
label='next'
|
||||||
title={title.titleNext}
|
title={eventNext.title}
|
||||||
subtitle={title.subtitleNext}
|
subtitle={eventNext.subtitle}
|
||||||
presenter={title.presenterNext}
|
presenter={eventNext.presenter}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { Message } from 'ontime-types';
|
|||||||
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
|
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
|
||||||
import { LOWER_THIRDS_OPTIONS } from '../../../common/components/view-params-editor/constants';
|
import { LOWER_THIRDS_OPTIONS } from '../../../common/components/view-params-editor/constants';
|
||||||
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
|
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
|
||||||
import { TitleManager } from '../ViewWrapper';
|
|
||||||
|
|
||||||
import { LowerOptions } from './LowerWrapper';
|
import { LowerOptions } from './LowerWrapper';
|
||||||
|
|
||||||
@@ -13,12 +12,14 @@ import './LowerLines.scss';
|
|||||||
|
|
||||||
interface LowerLinesProps {
|
interface LowerLinesProps {
|
||||||
lower: Message;
|
lower: Message;
|
||||||
title: TitleManager;
|
heading: string;
|
||||||
|
subheading: string;
|
||||||
options: LowerOptions;
|
options: LowerOptions;
|
||||||
|
doShow: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function LowerLines(props: LowerLinesProps) {
|
export default function LowerLines(props: LowerLinesProps) {
|
||||||
const { lower, title, options } = props;
|
const { lower, heading, subheading, options, doShow } = props;
|
||||||
const [showLower, setShowLower] = useState(true);
|
const [showLower, setShowLower] = useState(true);
|
||||||
|
|
||||||
// Unmount if fadeOut
|
// Unmount if fadeOut
|
||||||
@@ -36,8 +37,8 @@ export default function LowerLines(props: LowerLinesProps) {
|
|||||||
}, [options.fadeOut, options.transitionIn]);
|
}, [options.fadeOut, options.transitionIn]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setShowLower(title.showNow);
|
setShowLower(doShow);
|
||||||
}, [title.showNow]);
|
}, [doShow]);
|
||||||
|
|
||||||
// Format messages
|
// Format messages
|
||||||
const showLowerMessage = lower.text !== '' && lower.visible;
|
const showLowerMessage = lower.text !== '' && lower.visible;
|
||||||
@@ -146,14 +147,14 @@ export default function LowerLines(props: LowerLinesProps) {
|
|||||||
>
|
>
|
||||||
<motion.div className='title-container' variants={titleContainerVariants}>
|
<motion.div className='title-container' variants={titleContainerVariants}>
|
||||||
<motion.div className='title' variants={titleVariants}>
|
<motion.div className='title' variants={titleVariants}>
|
||||||
{title.titleNow}
|
{heading}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<div className='title-decor' />
|
<div className='title-decor' />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<motion.div className='subtitle-container' variants={subtitleContainerVariants}>
|
<motion.div className='subtitle-container' variants={subtitleContainerVariants}>
|
||||||
<div className='sub-decor' />
|
<div className='sub-decor' />
|
||||||
<motion.div className='subtitle' variants={subtitleVariants}>
|
<motion.div className='subtitle' variants={subtitleVariants}>
|
||||||
{title.presenterNow}
|
{subheading}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { memo, useEffect, useState } from 'react';
|
import { memo, useEffect, useState } from 'react';
|
||||||
import isEqual from 'react-fast-compare';
|
import isEqual from 'react-fast-compare';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { Message, ViewSettings } from 'ontime-types';
|
import { Message, OntimeEvent, ViewSettings } from 'ontime-types';
|
||||||
|
|
||||||
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
||||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||||
import { TitleManager } from '../ViewWrapper';
|
|
||||||
|
|
||||||
import LowerLines from './LowerLines';
|
import LowerLines from './LowerLines';
|
||||||
|
|
||||||
@@ -17,33 +16,26 @@ export type LowerOptions = {
|
|||||||
keyColour?: string;
|
keyColour?: string;
|
||||||
fadeOut: number;
|
fadeOut: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface LowerProps {
|
interface LowerProps {
|
||||||
title: TitleManager;
|
eventNow: OntimeEvent | null;
|
||||||
lower: Message;
|
lower: Message;
|
||||||
viewSettings: ViewSettings;
|
viewSettings: ViewSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent triggering animation without a content change
|
// prevent triggering animation without a content change
|
||||||
const areEqual = (prevProps: LowerProps, nextProps: LowerProps) => {
|
const areEqual = (prevProps: LowerProps, nextProps: LowerProps) => {
|
||||||
return isEqual(prevProps.title, nextProps.title) && isEqual(prevProps.lower, nextProps.lower);
|
return isEqual(prevProps.eventNow?.title, nextProps.eventNow?.title) && isEqual(prevProps.lower, nextProps.lower);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Lower = (props: LowerProps) => {
|
const Lower = (props: LowerProps) => {
|
||||||
const { title, lower, viewSettings } = props;
|
const { eventNow, lower, viewSettings } = props;
|
||||||
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const [titles, setTitles] = useState<TitleManager>({
|
|
||||||
titleNow: '',
|
const [heading, setHeading] = useState('');
|
||||||
titleNext: '',
|
const [subheading, setSubheading] = useState('');
|
||||||
subtitleNow: '',
|
const [showLower, setShowLower] = useState(false);
|
||||||
subtitleNext: '',
|
|
||||||
presenterNow: '',
|
|
||||||
presenterNext: '',
|
|
||||||
noteNow: '',
|
|
||||||
noteNext: '',
|
|
||||||
showNow: false,
|
|
||||||
showNext: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set window title
|
// Set window title
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -54,28 +46,32 @@ const Lower = (props: LowerProps) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// clear titles if necessary
|
// clear titles if necessary
|
||||||
// will trigger an animation out in the component
|
// will trigger an animation out in the component
|
||||||
let timeout: NodeJS.Timeout | null = null;
|
let timeout: NodeJS.Timeout;
|
||||||
if (
|
|
||||||
title?.titleNow !== titles?.titleNow ||
|
|
||||||
title?.subtitleNow !== titles?.subtitleNow ||
|
|
||||||
title?.presenterNow !== titles?.presenterNow
|
|
||||||
) {
|
|
||||||
setTitles((t) => ({ ...t, showNow: false }));
|
|
||||||
|
|
||||||
const transitionTime = 2000;
|
const haveTitlesChanged = eventNow?.title !== heading || eventNow?.presenter !== subheading;
|
||||||
|
const areTitlesEmpty = !eventNow?.title && !eventNow?.presenter;
|
||||||
|
|
||||||
|
// we have new titles
|
||||||
|
if (haveTitlesChanged && !areTitlesEmpty) {
|
||||||
|
// show lower
|
||||||
|
setHeading(eventNow?.title ?? '');
|
||||||
|
setSubheading(eventNow?.presenter ?? '');
|
||||||
|
setShowLower(true);
|
||||||
|
|
||||||
|
// schedule transition out
|
||||||
|
const transitionTime = 5000;
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
setTitles(title);
|
setShowLower(false);
|
||||||
}, transitionTime);
|
}, transitionTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (timeout != null) {
|
if (timeout) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line -- we do this to keep animations
|
// eslint-disable-next-line -- we do this to keep animations
|
||||||
}, [title.titleNow, title.subtitleNow, title.presenterNow]);
|
}, [eventNow?.title, eventNow?.presenter]);
|
||||||
|
|
||||||
// defer rendering until we load stylesheets
|
// defer rendering until we load stylesheets
|
||||||
if (!shouldRender) {
|
if (!shouldRender) {
|
||||||
@@ -135,7 +131,7 @@ const Lower = (props: LowerProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <LowerLines lower={lower} title={titles} options={options} />;
|
return <LowerLines lower={lower} heading={heading} subheading={subheading} options={options} doShow={showLower} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(Lower, areEqual);
|
export default memo(Lower, areEqual);
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
|||||||
import { formatTime } from '../../../common/utils/time';
|
import { formatTime } from '../../../common/utils/time';
|
||||||
import { useTranslation } from '../../../translation/TranslationProvider';
|
import { useTranslation } from '../../../translation/TranslationProvider';
|
||||||
import { titleVariants } from '../common/animation';
|
import { titleVariants } from '../common/animation';
|
||||||
import { TitleManager } from '../ViewWrapper';
|
|
||||||
|
|
||||||
import './Public.scss';
|
import './Public.scss';
|
||||||
|
|
||||||
@@ -28,7 +27,8 @@ const formatOptions = {
|
|||||||
interface BackstageProps {
|
interface BackstageProps {
|
||||||
isMirrored: boolean;
|
isMirrored: boolean;
|
||||||
publ: Message;
|
publ: Message;
|
||||||
publicTitle: TitleManager;
|
publicEventNow: OntimeEvent | null;
|
||||||
|
publicEventNext: OntimeEvent | null;
|
||||||
time: TimeManagerType;
|
time: TimeManagerType;
|
||||||
events: OntimeEvent[];
|
events: OntimeEvent[];
|
||||||
publicSelectedId: string | null;
|
publicSelectedId: string | null;
|
||||||
@@ -37,7 +37,8 @@ interface BackstageProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Public(props: BackstageProps) {
|
export default function Public(props: BackstageProps) {
|
||||||
const { isMirrored, publ, publicTitle, time, events, publicSelectedId, general, viewSettings } = props;
|
const { isMirrored, publ, publicEventNow, publicEventNext, time, events, publicSelectedId, general, viewSettings } =
|
||||||
|
props;
|
||||||
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
||||||
const { getLocalizedString } = useTranslation();
|
const { getLocalizedString } = useTranslation();
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ export default function Public(props: BackstageProps) {
|
|||||||
|
|
||||||
<div className='now-container'>
|
<div className='now-container'>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{publicTitle.showNow && (
|
{publicEventNow && (
|
||||||
<motion.div
|
<motion.div
|
||||||
className='event now'
|
className='event now'
|
||||||
key='now'
|
key='now'
|
||||||
@@ -79,16 +80,16 @@ export default function Public(props: BackstageProps) {
|
|||||||
>
|
>
|
||||||
<TitleCard
|
<TitleCard
|
||||||
label='now'
|
label='now'
|
||||||
title={publicTitle.titleNow}
|
title={publicEventNow.title}
|
||||||
subtitle={publicTitle.subtitleNow}
|
subtitle={publicEventNow.subtitle}
|
||||||
presenter={publicTitle.presenterNow}
|
presenter={publicEventNow.presenter}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{publicTitle.showNext && (
|
{publicEventNext && (
|
||||||
<motion.div
|
<motion.div
|
||||||
className='event next'
|
className='event next'
|
||||||
key='next'
|
key='next'
|
||||||
@@ -99,9 +100,9 @@ export default function Public(props: BackstageProps) {
|
|||||||
>
|
>
|
||||||
<TitleCard
|
<TitleCard
|
||||||
label='next'
|
label='next'
|
||||||
title={publicTitle.titleNext}
|
title={publicEventNext.title}
|
||||||
subtitle={publicTitle.subtitleNext}
|
subtitle={publicEventNext.subtitle}
|
||||||
presenter={publicTitle.presenterNext}
|
presenter={publicEventNext.presenter}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet
|
|||||||
import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||||
import { secondsInMillis } from '../../../common/utils/dateConfig';
|
import { secondsInMillis } from '../../../common/utils/dateConfig';
|
||||||
import { formatTime } from '../../../common/utils/time';
|
import { formatTime } from '../../../common/utils/time';
|
||||||
import { TitleManager } from '../ViewWrapper';
|
|
||||||
|
|
||||||
import { type ScheduleEvent, formatEventList, trimRundown } from './studioClock.utils';
|
import { type ScheduleEvent, formatEventList, trimRundown } from './studioClock.utils';
|
||||||
|
|
||||||
@@ -26,7 +25,7 @@ const formatOptions = {
|
|||||||
|
|
||||||
interface StudioClockProps {
|
interface StudioClockProps {
|
||||||
isMirrored: boolean;
|
isMirrored: boolean;
|
||||||
title: TitleManager;
|
eventNext: OntimeEvent | null;
|
||||||
time: TimeManagerType;
|
time: TimeManagerType;
|
||||||
backstageEvents: OntimeRundown;
|
backstageEvents: OntimeRundown;
|
||||||
selectedId: string | null;
|
selectedId: string | null;
|
||||||
@@ -36,7 +35,7 @@ interface StudioClockProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function StudioClock(props: StudioClockProps) {
|
export default function StudioClock(props: StudioClockProps) {
|
||||||
const { isMirrored, title, time, backstageEvents, selectedId, nextId, onAir, viewSettings } = props;
|
const { isMirrored, eventNext, time, backstageEvents, selectedId, nextId, onAir, viewSettings } = props;
|
||||||
|
|
||||||
// deferring rendering seems to affect styling (font and useFitText)
|
// deferring rendering seems to affect styling (font and useFitText)
|
||||||
useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
||||||
@@ -87,7 +86,7 @@ export default function StudioClock(props: StudioClockProps) {
|
|||||||
className='next-title'
|
className='next-title'
|
||||||
style={{ fontSize: titleFontSize, height: '10vh', width: '100%', maxWidth: '75%' }}
|
style={{ fontSize: titleFontSize, height: '10vh', width: '100%', maxWidth: '75%' }}
|
||||||
>
|
>
|
||||||
{title.titleNext}
|
{eventNext?.title ?? ''}
|
||||||
</div>
|
</div>
|
||||||
<div className={isNegative ? 'next-countdown' : 'next-countdown next-countdown--overtime'}>
|
<div className={isNegative ? 'next-countdown' : 'next-countdown next-countdown--overtime'}>
|
||||||
{selectedId !== null && formatDisplay(time.current)}
|
{selectedId !== null && formatDisplay(time.current)}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import { EventData, Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types';
|
import { EventData, OntimeEvent, Playback, TimerMessage, TimerType, ViewSettings } from 'ontime-types';
|
||||||
|
|
||||||
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
||||||
import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar';
|
import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar';
|
||||||
@@ -13,7 +13,6 @@ import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
|||||||
import { formatTime } from '../../../common/utils/time';
|
import { formatTime } from '../../../common/utils/time';
|
||||||
import { useTranslation } from '../../../translation/TranslationProvider';
|
import { useTranslation } from '../../../translation/TranslationProvider';
|
||||||
import { formatTimerDisplay, getTimerByType } from '../common/viewerUtils';
|
import { formatTimerDisplay, getTimerByType } from '../common/viewerUtils';
|
||||||
import { TitleManager } from '../ViewWrapper';
|
|
||||||
|
|
||||||
import './Timer.scss';
|
import './Timer.scss';
|
||||||
|
|
||||||
@@ -42,13 +41,14 @@ interface TimerProps {
|
|||||||
isMirrored: boolean;
|
isMirrored: boolean;
|
||||||
general: EventData;
|
general: EventData;
|
||||||
pres: TimerMessage;
|
pres: TimerMessage;
|
||||||
title: TitleManager;
|
eventNow: OntimeEvent | null;
|
||||||
|
eventNext: OntimeEvent | null;
|
||||||
time: TimeManagerType;
|
time: TimeManagerType;
|
||||||
viewSettings: ViewSettings;
|
viewSettings: ViewSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Timer(props: TimerProps) {
|
export default function Timer(props: TimerProps) {
|
||||||
const { isMirrored, pres, title, time, viewSettings } = props;
|
const { isMirrored, pres, eventNow, eventNext, time, viewSettings } = props;
|
||||||
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
||||||
const { getLocalizedString } = useTranslation();
|
const { getLocalizedString } = useTranslation();
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ export default function Timer(props: TimerProps) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{title.showNow && !finished && (
|
{eventNow && !finished && (
|
||||||
<motion.div
|
<motion.div
|
||||||
className='event now'
|
className='event now'
|
||||||
key='now'
|
key='now'
|
||||||
@@ -148,13 +148,13 @@ export default function Timer(props: TimerProps) {
|
|||||||
animate='visible'
|
animate='visible'
|
||||||
exit='exit'
|
exit='exit'
|
||||||
>
|
>
|
||||||
<TitleCard label='now' title={title.titleNow} subtitle={title.subtitleNow} presenter={title.presenterNow} />
|
<TitleCard label='now' title={eventNow.title} subtitle={eventNow.subtitle} presenter={eventNow.presenter} />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{title.showNext && (
|
{eventNext && (
|
||||||
<motion.div
|
<motion.div
|
||||||
className='event next'
|
className='event next'
|
||||||
key='next'
|
key='next'
|
||||||
@@ -165,9 +165,9 @@ export default function Timer(props: TimerProps) {
|
|||||||
>
|
>
|
||||||
<TitleCard
|
<TitleCard
|
||||||
label='next'
|
label='next'
|
||||||
title={title.titleNext}
|
title={eventNext.title}
|
||||||
subtitle={title.subtitleNext}
|
subtitle={eventNext.subtitle}
|
||||||
presenter={title.presenterNext}
|
presenter={eventNext.presenter}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Loaded, OntimeEvent, SupportedEvent, TitleBlock } from 'ontime-types';
|
import { Loaded, OntimeEvent, SupportedEvent } from 'ontime-types';
|
||||||
|
|
||||||
import { DataProvider } from '../data-provider/DataProvider.js';
|
import { DataProvider } from '../data-provider/DataProvider.js';
|
||||||
import { getRollTimers } from '../../services/rollUtils.js';
|
import { getRollTimers } from '../../services/rollUtils.js';
|
||||||
@@ -10,10 +10,11 @@ let instance;
|
|||||||
* Manages business logic around loading events
|
* Manages business logic around loading events
|
||||||
*/
|
*/
|
||||||
export class EventLoader {
|
export class EventLoader {
|
||||||
loadedEvent: OntimeEvent | null;
|
|
||||||
loaded: Loaded;
|
loaded: Loaded;
|
||||||
titles: TitleBlock;
|
eventNow: OntimeEvent | null;
|
||||||
titlesPublic: TitleBlock;
|
publicEventNow: OntimeEvent | null;
|
||||||
|
eventNext: OntimeEvent | null;
|
||||||
|
publicEventNext: OntimeEvent | null;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (instance) {
|
if (instance) {
|
||||||
@@ -56,7 +57,7 @@ export class EventLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns an event given its index
|
* returns an event given its index after filtering for OntimeEvents
|
||||||
* @param {number} eventIndex
|
* @param {number} eventIndex
|
||||||
* @return {OntimeEvent | undefined}
|
* @return {OntimeEvent | undefined}
|
||||||
*/
|
*/
|
||||||
@@ -65,16 +66,6 @@ export class EventLoader {
|
|||||||
return timedEvents?.[eventIndex];
|
return timedEvents?.[eventIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* returns an event given its index
|
|
||||||
* @param {number} eventIndex
|
|
||||||
* @return {object | undefined}
|
|
||||||
*/
|
|
||||||
static getPlayableAtIndex(eventIndex) {
|
|
||||||
const timedEvents = EventLoader.getPlayableEvents();
|
|
||||||
return timedEvents?.[eventIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns an event given its id
|
* returns an event given its id
|
||||||
* @param {string} eventId
|
* @param {string} eventId
|
||||||
@@ -167,16 +158,18 @@ export class EventLoader {
|
|||||||
timeNow,
|
timeNow,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.loadedEvent = currentEvent;
|
// load events
|
||||||
|
this.eventNow = currentEvent;
|
||||||
|
this.publicEventNow = currentPublicEvent;
|
||||||
|
this.eventNext = nextEvent;
|
||||||
|
this.publicEventNext = nextPublicEvent;
|
||||||
|
|
||||||
|
// loaded data summary
|
||||||
this.loaded.selectedEventIndex = nowIndex;
|
this.loaded.selectedEventIndex = nowIndex;
|
||||||
this.loaded.selectedEventId = currentEvent?.id || null;
|
this.loaded.selectedEventId = currentEvent?.id || null;
|
||||||
this.loaded.numEvents = timedEvents.length;
|
this.loaded.numEvents = timedEvents.length;
|
||||||
|
this.loaded.nextEventId = nextEvent.id;
|
||||||
// titles
|
this.loaded.nextPublicEventId = nextPublicEvent.id;
|
||||||
this._loadThisTitles(currentEvent, 'now-private');
|
|
||||||
this._loadThisTitles(currentPublicEvent, 'now-public');
|
|
||||||
this._loadThisTitles(nextEvent, 'next-private');
|
|
||||||
this._loadThisTitles(nextPublicEvent, 'next-public');
|
|
||||||
|
|
||||||
return { currentEvent, nextEvent, timeToNext };
|
return { currentEvent, nextEvent, timeToNext };
|
||||||
}
|
}
|
||||||
@@ -187,10 +180,11 @@ export class EventLoader {
|
|||||||
*/
|
*/
|
||||||
getLoaded() {
|
getLoaded() {
|
||||||
return {
|
return {
|
||||||
loadedEvent: this.loadedEvent,
|
|
||||||
loaded: this.loaded,
|
loaded: this.loaded,
|
||||||
titles: this.titles,
|
eventNow: this.eventNow,
|
||||||
titlesPublic: this.titlesPublic,
|
publicEventNow: this.publicEventNow,
|
||||||
|
eventNext: this.eventNext,
|
||||||
|
publicEventNext: this.publicEventNext,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +200,10 @@ export class EventLoader {
|
|||||||
* Resets instance state
|
* Resets instance state
|
||||||
*/
|
*/
|
||||||
reset(emit = true) {
|
reset(emit = true) {
|
||||||
this.loadedEvent = null;
|
this.eventNow = null;
|
||||||
|
this.publicEventNow = null;
|
||||||
|
this.eventNext = null;
|
||||||
|
this.publicEventNext = null;
|
||||||
this.loaded = {
|
this.loaded = {
|
||||||
selectedEventIndex: null,
|
selectedEventIndex: null,
|
||||||
selectedEventId: null,
|
selectedEventId: null,
|
||||||
@@ -215,26 +212,6 @@ export class EventLoader {
|
|||||||
nextPublicEventId: null,
|
nextPublicEventId: null,
|
||||||
numEvents: EventLoader.getPlayableEvents().length,
|
numEvents: EventLoader.getPlayableEvents().length,
|
||||||
};
|
};
|
||||||
this.titles = {
|
|
||||||
titleNow: null,
|
|
||||||
subtitleNow: null,
|
|
||||||
presenterNow: null,
|
|
||||||
noteNow: null,
|
|
||||||
titleNext: null,
|
|
||||||
subtitleNext: null,
|
|
||||||
presenterNext: null,
|
|
||||||
noteNext: null,
|
|
||||||
};
|
|
||||||
this.titlesPublic = {
|
|
||||||
titleNow: null,
|
|
||||||
subtitleNow: null,
|
|
||||||
presenterNow: null,
|
|
||||||
noteNow: null,
|
|
||||||
titleNext: null,
|
|
||||||
subtitleNext: null,
|
|
||||||
presenterNext: null,
|
|
||||||
noteNext: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
// workaround for socket not being ready in constructor
|
// workaround for socket not being ready in constructor
|
||||||
if (emit) {
|
if (emit) {
|
||||||
@@ -255,13 +232,12 @@ export class EventLoader {
|
|||||||
const playableEvents = EventLoader.getPlayableEvents();
|
const playableEvents = EventLoader.getPlayableEvents();
|
||||||
|
|
||||||
// we know some stuff now
|
// we know some stuff now
|
||||||
this.loadedEvent = event;
|
|
||||||
this.loaded.selectedEventIndex = eventIndex;
|
this.loaded.selectedEventIndex = eventIndex;
|
||||||
this.loaded.selectedEventId = event.id;
|
this.loaded.selectedEventId = event.id;
|
||||||
this.loaded.numEvents = timedEvents.length;
|
this.loaded.numEvents = timedEvents.length;
|
||||||
// this.nextEventId = playableEvents[eventIndex + 1].id;
|
this.eventNow = event;
|
||||||
this._loadTitlesNow(event, playableEvents);
|
this._loadEventNow(event, playableEvents);
|
||||||
this._loadTitlesNext(playableEvents);
|
this._loadEventNext(playableEvents);
|
||||||
|
|
||||||
this._loadEvent();
|
this._loadEvent();
|
||||||
|
|
||||||
@@ -274,29 +250,28 @@ export class EventLoader {
|
|||||||
private _loadEvent() {
|
private _loadEvent() {
|
||||||
eventStore.batchSet({
|
eventStore.batchSet({
|
||||||
loaded: this.loaded,
|
loaded: this.loaded,
|
||||||
titles: this.titles,
|
eventNow: this.eventNow,
|
||||||
titlesPublic: this.titlesPublic,
|
publicEventNow: this.publicEventNow,
|
||||||
|
eventNext: this.eventNext,
|
||||||
|
publicEventNext: this.publicEventNext,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description loads given title (now)
|
* @description loads currently running events
|
||||||
* @private
|
* @private
|
||||||
* @param {object} event
|
* @param {object} event
|
||||||
* @param {array} rundown
|
* @param {array} rundown
|
||||||
*/
|
*/
|
||||||
private _loadTitlesNow(event, rundown) {
|
private _loadEventNow(event, rundown) {
|
||||||
// private title is always current
|
this.eventNow = event;
|
||||||
|
|
||||||
// check if current is also public
|
// check if current is also public
|
||||||
if (event.isPublic) {
|
if (event.isPublic) {
|
||||||
this._loadThisTitles(event, 'now');
|
this.publicEventNow = event;
|
||||||
} else {
|
} else {
|
||||||
this._loadThisTitles(event, 'now-private');
|
|
||||||
|
|
||||||
// assume there is no public event
|
// assume there is no public event
|
||||||
this.titlesPublic.titleNow = null;
|
this.publicEventNow = null;
|
||||||
this.titlesPublic.subtitleNow = null;
|
|
||||||
this.titlesPublic.presenterNow = null;
|
|
||||||
this.loaded.selectedPublicEventId = null;
|
this.loaded.selectedPublicEventId = null;
|
||||||
|
|
||||||
// if there is nothing before, return
|
// if there is nothing before, return
|
||||||
@@ -305,7 +280,8 @@ export class EventLoader {
|
|||||||
// iterate backwards to find it
|
// iterate backwards to find it
|
||||||
for (let i = this.loaded.selectedEventIndex; i >= 0; i--) {
|
for (let i = this.loaded.selectedEventIndex; i >= 0; i--) {
|
||||||
if (rundown[i].isPublic) {
|
if (rundown[i].isPublic) {
|
||||||
this._loadThisTitles(rundown[i], 'now-public');
|
this.publicEventNow = rundown[i];
|
||||||
|
this.loaded.selectedPublicEventId = rundown[i].id;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,180 +289,44 @@ export class EventLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description look for next titles to load
|
* @description look for next events
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private _loadTitlesNext(rundown) {
|
private _loadEventNext(rundown) {
|
||||||
// maybe there is nothing to load
|
// assume there are no next events
|
||||||
if (this.loaded.selectedEventIndex === null) return;
|
this.eventNext = null;
|
||||||
|
this.publicEventNext = null;
|
||||||
// assume there is no next event
|
|
||||||
this.titles.titleNext = null;
|
|
||||||
this.titles.subtitleNext = null;
|
|
||||||
this.titles.presenterNext = null;
|
|
||||||
this.titles.noteNext = null;
|
|
||||||
this.loaded.nextEventId = null;
|
this.loaded.nextEventId = null;
|
||||||
|
|
||||||
this.titlesPublic.titleNext = null;
|
|
||||||
this.titlesPublic.subtitleNext = null;
|
|
||||||
this.titlesPublic.presenterNext = null;
|
|
||||||
this.loaded.nextPublicEventId = null;
|
this.loaded.nextPublicEventId = null;
|
||||||
|
|
||||||
|
if (this.loaded.selectedEventIndex === null) return;
|
||||||
|
|
||||||
const numEvents = rundown.length;
|
const numEvents = rundown.length;
|
||||||
|
|
||||||
if (this.loaded.selectedEventIndex < numEvents - 1) {
|
if (this.loaded.selectedEventIndex < numEvents - 1) {
|
||||||
let nextPublic = false;
|
let nextPublic = false;
|
||||||
let nextPrivate = false;
|
let nextProduction = false;
|
||||||
|
|
||||||
for (let i = this.loaded.selectedEventIndex + 1; i < numEvents; i++) {
|
for (let i = this.loaded.selectedEventIndex + 1; i < numEvents; i++) {
|
||||||
// if we have not set private
|
// if we have not set private
|
||||||
if (!nextPrivate) {
|
if (!nextProduction) {
|
||||||
this._loadThisTitles(rundown[i], 'next-private');
|
this.eventNext = rundown[i];
|
||||||
nextPrivate = true;
|
this.loaded.nextEventId = rundown[i].id;
|
||||||
|
nextProduction = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if event is public
|
// if event is public
|
||||||
if (rundown[i].isPublic) {
|
if (rundown[i].isPublic) {
|
||||||
this._loadThisTitles(rundown[i], 'next-public');
|
this.publicEventNext = rundown[i];
|
||||||
|
this.loaded.nextPublicEventId = rundown[i].id;
|
||||||
nextPublic = true;
|
nextPublic = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop if both are set
|
// Stop if both are set
|
||||||
if (nextPublic && nextPrivate) break;
|
if (nextPublic && nextProduction) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @description loads given title
|
|
||||||
* @param event
|
|
||||||
* @param type
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private _loadThisTitles(event, type) {
|
|
||||||
if (type === 'now') {
|
|
||||||
if (event === null) {
|
|
||||||
// public
|
|
||||||
this.titlesPublic.titleNow = null;
|
|
||||||
this.titlesPublic.subtitleNow = null;
|
|
||||||
this.titlesPublic.presenterNow = null;
|
|
||||||
this.titlesPublic.noteNow = null;
|
|
||||||
this.loaded.selectedPublicEventId = null;
|
|
||||||
|
|
||||||
// private
|
|
||||||
this.titles.titleNow = null;
|
|
||||||
this.titles.subtitleNow = null;
|
|
||||||
this.titles.presenterNow = null;
|
|
||||||
this.titles.noteNow = null;
|
|
||||||
this.loaded.selectedEventId = null;
|
|
||||||
} else {
|
|
||||||
// public
|
|
||||||
this.titlesPublic.titleNow = event.title;
|
|
||||||
this.titlesPublic.subtitleNow = event.subtitle;
|
|
||||||
this.titlesPublic.presenterNow = event.presenter;
|
|
||||||
this.titlesPublic.noteNow = event.note;
|
|
||||||
this.loaded.selectedPublicEventId = event.id;
|
|
||||||
|
|
||||||
// private
|
|
||||||
this.titles.titleNow = event.title;
|
|
||||||
this.titles.subtitleNow = event.subtitle;
|
|
||||||
this.titles.presenterNow = event.presenter;
|
|
||||||
this.titles.noteNow = event.note;
|
|
||||||
this.loaded.selectedEventId = event.id;
|
|
||||||
}
|
|
||||||
} else if (type === 'now-public') {
|
|
||||||
if (event === null) {
|
|
||||||
this.titlesPublic.titleNow = null;
|
|
||||||
this.titlesPublic.subtitleNow = null;
|
|
||||||
this.titlesPublic.presenterNow = null;
|
|
||||||
this.titlesPublic.noteNow = null;
|
|
||||||
this.loaded.selectedPublicEventId = null;
|
|
||||||
} else {
|
|
||||||
this.titlesPublic.titleNow = event.title;
|
|
||||||
this.titlesPublic.subtitleNow = event.subtitle;
|
|
||||||
this.titlesPublic.presenterNow = event.presenter;
|
|
||||||
this.titlesPublic.noteNow = event.note;
|
|
||||||
this.loaded.selectedPublicEventId = event.id;
|
|
||||||
}
|
|
||||||
} else if (type === 'now-private') {
|
|
||||||
if (event === null) {
|
|
||||||
this.titles.titleNow = null;
|
|
||||||
this.titles.subtitleNow = null;
|
|
||||||
this.titles.presenterNow = null;
|
|
||||||
this.titles.noteNow = null;
|
|
||||||
this.loaded.selectedEventId = null;
|
|
||||||
} else {
|
|
||||||
this.titles.titleNow = event.title;
|
|
||||||
this.titles.subtitleNow = event.subtitle;
|
|
||||||
this.titles.presenterNow = event.presenter;
|
|
||||||
this.titles.noteNow = event.note;
|
|
||||||
this.loaded.selectedEventId = event.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// next, load to both public and private
|
|
||||||
else if (type === 'next') {
|
|
||||||
if (event === null) {
|
|
||||||
// public
|
|
||||||
this.titlesPublic.titleNext = null;
|
|
||||||
this.titlesPublic.subtitleNext = null;
|
|
||||||
this.titlesPublic.presenterNext = null;
|
|
||||||
this.titlesPublic.noteNext = null;
|
|
||||||
this.loaded.nextPublicEventId = null;
|
|
||||||
|
|
||||||
// private
|
|
||||||
this.titles.titleNext = null;
|
|
||||||
this.titles.subtitleNext = null;
|
|
||||||
this.titles.presenterNext = null;
|
|
||||||
this.titles.noteNext = null;
|
|
||||||
this.loaded.nextEventId = null;
|
|
||||||
} else {
|
|
||||||
// public
|
|
||||||
this.titlesPublic.titleNext = event.title;
|
|
||||||
this.titlesPublic.subtitleNext = event.subtitle;
|
|
||||||
this.titlesPublic.presenterNext = event.presenter;
|
|
||||||
this.titlesPublic.noteNext = event.note;
|
|
||||||
this.loaded.nextPublicEventId = event.id;
|
|
||||||
|
|
||||||
// private
|
|
||||||
this.titles.titleNext = event.title;
|
|
||||||
this.titles.subtitleNext = event.subtitle;
|
|
||||||
this.titles.presenterNext = event.presenter;
|
|
||||||
this.titles.noteNext = event.note;
|
|
||||||
this.loaded.nextEventId = event.id;
|
|
||||||
}
|
|
||||||
} else if (type === 'next-public') {
|
|
||||||
if (event === null) {
|
|
||||||
this.titlesPublic.titleNext = null;
|
|
||||||
this.titlesPublic.subtitleNext = null;
|
|
||||||
this.titlesPublic.presenterNext = null;
|
|
||||||
this.titlesPublic.noteNext = null;
|
|
||||||
this.loaded.nextPublicEventId = null;
|
|
||||||
} else {
|
|
||||||
this.titlesPublic.titleNext = event.title;
|
|
||||||
this.titlesPublic.subtitleNext = event.subtitle;
|
|
||||||
this.titlesPublic.presenterNext = event.presenter;
|
|
||||||
this.titlesPublic.noteNext = event.note;
|
|
||||||
this.loaded.nextPublicEventId = event.id;
|
|
||||||
}
|
|
||||||
} else if (type === 'next-private') {
|
|
||||||
if (event === null) {
|
|
||||||
this.titles.titleNext = null;
|
|
||||||
this.titles.subtitleNext = null;
|
|
||||||
this.titles.presenterNext = null;
|
|
||||||
this.titles.noteNext = null;
|
|
||||||
this.loaded.nextEventId = null;
|
|
||||||
} else {
|
|
||||||
this.titles.titleNext = event.title;
|
|
||||||
this.titles.subtitleNext = event.subtitle;
|
|
||||||
this.titles.presenterNext = event.presenter;
|
|
||||||
this.titles.noteNext = event.note;
|
|
||||||
this.loaded.nextEventId = event.id;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error(`Unhandled title type: ${type}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const eventLoader = new EventLoader();
|
export const eventLoader = new EventLoader();
|
||||||
|
|||||||
@@ -116,8 +116,8 @@ export function updateTimer(affectedIds?: string[]) {
|
|||||||
|
|
||||||
if (safeOption) {
|
if (safeOption) {
|
||||||
eventLoader.reset();
|
eventLoader.reset();
|
||||||
const { loadedEvent } = eventLoader.loadById(runningEventId) || {};
|
const { eventNow } = eventLoader.loadById(runningEventId) || {};
|
||||||
eventTimer.hotReload(loadedEvent);
|
eventTimer.hotReload(eventNow);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,9 +133,9 @@ export function updateTimer(affectedIds?: string[]) {
|
|||||||
eventTimer.roll(currentEvent, nextEvent);
|
eventTimer.roll(currentEvent, nextEvent);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const { loadedEvent } = eventLoader.loadById(runningEventId) || {};
|
const { eventNow } = eventLoader.loadById(runningEventId) || {};
|
||||||
if (loadedEvent) {
|
if (eventNow) {
|
||||||
eventTimer.hotReload(loadedEvent);
|
eventTimer.hotReload(eventNow);
|
||||||
} else {
|
} else {
|
||||||
eventTimer.stop();
|
eventTimer.stop();
|
||||||
}
|
}
|
||||||
@@ -144,8 +144,8 @@ export function updateTimer(affectedIds?: string[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isNext) {
|
if (isNext) {
|
||||||
const { loadedEvent } = eventLoader.loadById(runningEventId) || {};
|
const { eventNow } = eventLoader.loadById(runningEventId) || {};
|
||||||
eventTimer.hotReload(loadedEvent);
|
eventTimer.hotReload(eventNow);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -56,8 +56,10 @@ export const eventStore = {
|
|||||||
* - Message Service lowerMessage
|
* - Message Service lowerMessage
|
||||||
* - Message Service onAir
|
* - Message Service onAir
|
||||||
* - Event Loader loaded
|
* - Event Loader loaded
|
||||||
* - Event Loader titles
|
* - Event Loader eventNow
|
||||||
* - Event Loader titlesPublic
|
* - Event Loader publicEventNow
|
||||||
|
* - Event Loader eventNext
|
||||||
|
* - Event Loader publicEventNext
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const getInitialPayload = () => ({
|
export const getInitialPayload = () => ({
|
||||||
@@ -68,6 +70,8 @@ export const getInitialPayload = () => ({
|
|||||||
lowerMessage: messageService.lowerMessage,
|
lowerMessage: messageService.lowerMessage,
|
||||||
onAir: messageService.onAir,
|
onAir: messageService.onAir,
|
||||||
loaded: eventLoader.loaded,
|
loaded: eventLoader.loaded,
|
||||||
titles: eventLoader.titles,
|
eventNow: eventLoader.eventNow,
|
||||||
titlesPublic: eventLoader.titlesPublic,
|
publicEventNow: eventLoader.publicEventNow,
|
||||||
|
eventNext: eventLoader.eventNext,
|
||||||
|
publicEventNext: eventLoader.publicEventNext,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Playback } from './Playback.type.js';
|
import { Playback } from './Playback.type.js';
|
||||||
import { Message, TimerMessage } from './MessageControl.type.js';
|
import { Message, TimerMessage } from './MessageControl.type.js';
|
||||||
import { TimerState } from './TimerState.type.js';
|
import { TimerState } from './TimerState.type.js';
|
||||||
import { TitleBlock } from './TitleBlock.type.js';
|
|
||||||
import { Loaded } from './Playlist.type.js';
|
import { Loaded } from './Playlist.type.js';
|
||||||
|
import { OntimeEvent } from '../core/OntimeEvent.type.js';
|
||||||
|
|
||||||
export type RuntimeStore = {
|
export type RuntimeStore = {
|
||||||
// timer service
|
// timer service
|
||||||
@@ -17,6 +17,8 @@ export type RuntimeStore = {
|
|||||||
|
|
||||||
// event loader
|
// event loader
|
||||||
loaded: Loaded;
|
loaded: Loaded;
|
||||||
titles: TitleBlock;
|
eventNow: OntimeEvent | null;
|
||||||
titlesPublic: TitleBlock;
|
publicEventNow: OntimeEvent | null;
|
||||||
|
eventNext: OntimeEvent | null;
|
||||||
|
publicEventNext: OntimeEvent | null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
export type TitleBlock = {
|
|
||||||
titleNow: string | null;
|
|
||||||
subtitleNow: string | null;
|
|
||||||
presenterNow: string | null;
|
|
||||||
noteNow: string | null;
|
|
||||||
titleNext: string | null;
|
|
||||||
subtitleNext: string | null;
|
|
||||||
presenterNext: string | null;
|
|
||||||
noteNext: string | null;
|
|
||||||
};
|
|
||||||
@@ -5,8 +5,8 @@ export type { DatabaseModel } from './definitions/DataModel.type.js';
|
|||||||
export { EndAction } from './definitions/EndAction.type.js';
|
export { EndAction } from './definitions/EndAction.type.js';
|
||||||
export {
|
export {
|
||||||
type OntimeBaseEvent,
|
type OntimeBaseEvent,
|
||||||
type OntimeBlock,
|
|
||||||
type OntimeDelay,
|
type OntimeDelay,
|
||||||
|
type OntimeBlock,
|
||||||
type OntimeEvent,
|
type OntimeEvent,
|
||||||
SupportedEvent,
|
SupportedEvent,
|
||||||
} from './definitions/core/OntimeEvent.type.js';
|
} from './definitions/core/OntimeEvent.type.js';
|
||||||
@@ -42,7 +42,6 @@ export type { Message, TimerMessage } from './definitions/runtime/MessageControl
|
|||||||
export type { Loaded } from './definitions/runtime/Playlist.type.js';
|
export type { Loaded } from './definitions/runtime/Playlist.type.js';
|
||||||
export type { RuntimeStore } from './definitions/runtime/RuntimeStore.type.js';
|
export type { RuntimeStore } from './definitions/runtime/RuntimeStore.type.js';
|
||||||
export type { TimerState } from './definitions/runtime/TimerState.type.js';
|
export type { TimerState } from './definitions/runtime/TimerState.type.js';
|
||||||
export type { TitleBlock } from './definitions/runtime/TitleBlock.type.js';
|
|
||||||
|
|
||||||
// CLIENT
|
// CLIENT
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user