feat: operator view

This commit is contained in:
cv
2023-08-29 14:19:46 +02:00
parent e667755fd6
commit c75d8502b0
3 changed files with 140 additions and 50 deletions
+23 -3
View File
@@ -9,6 +9,7 @@ import { getOperatorOptions } from '../../common/components/view-params-editor/c
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor'; import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
import useFollowComponent from '../../common/hooks/useFollowComponent'; import useFollowComponent from '../../common/hooks/useFollowComponent';
import { useOperator } from '../../common/hooks/useSocket'; import { useOperator } from '../../common/hooks/useSocket';
import useEventData from '../../common/hooks-query/useEventData';
import useRundown from '../../common/hooks-query/useRundown'; import useRundown from '../../common/hooks-query/useRundown';
import useUserFields from '../../common/hooks-query/useUserFields'; import useUserFields from '../../common/hooks-query/useUserFields';
import { isStringBoolean } from '../../common/utils/viewUtils'; import { isStringBoolean } from '../../common/utils/viewUtils';
@@ -25,6 +26,8 @@ const selectedOffset = 50;
export default function Operator() { export default function Operator() {
const { data, status } = useRundown(); const { data, status } = useRundown();
const { data: userFields, status: userFieldsStatus } = useUserFields(); const { data: userFields, status: userFieldsStatus } = useUserFields();
const { data: projectData, status: projectDataStatus } = useEventData();
const featureData = useOperator(); const featureData = useOperator();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
@@ -69,7 +72,14 @@ export default function Operator() {
} }
}; };
if (!data || status === 'loading' || !userFields || userFieldsStatus === 'loading') { if (
!data ||
status === 'loading' ||
!userFields ||
userFieldsStatus === 'loading' ||
!projectData ||
projectDataStatus === 'loading'
) {
return <Empty text='Loading...' />; return <Empty text='Loading...' />;
} }
@@ -77,12 +87,14 @@ export default function Operator() {
const subscribe = searchParams.get('subscribe') as keyof UserFields | null; const subscribe = searchParams.get('subscribe') as keyof UserFields | null;
const subscribedAlias = subscribe ? userFields[subscribe] : ''; const subscribedAlias = subscribe ? userFields[subscribe] : '';
const showSeconds = isStringBoolean(searchParams.get('showseconds')); const showSeconds = isStringBoolean(searchParams.get('showseconds'));
const lastEvent = getLastEvent(data);
const operatorOptions = getOperatorOptions(userFields); const operatorOptions = getOperatorOptions(userFields);
let isPast = Boolean(featureData.selectedEventId); let isPast = Boolean(featureData.selectedEventId);
const hidePast = isStringBoolean(searchParams.get('hidepast')); const hidePast = isStringBoolean(searchParams.get('hidepast'));
const firstEvent = getLastEvent(data);
const lastEvent = getLastEvent(data);
return ( return (
<div className={style.operatorContainer}> <div className={style.operatorContainer}>
<NavigationMenu /> <NavigationMenu />
@@ -124,7 +136,15 @@ export default function Operator() {
<div className={style.spacer} /> <div className={style.spacer} />
</div> </div>
<FollowButton isVisible={lockAutoScroll} onClickHandler={handleOffset} /> <FollowButton isVisible={lockAutoScroll} onClickHandler={handleOffset} />
<StatusBar playback={featureData.playback} lastEvent={lastEvent} selectedEventId={featureData.selectedEventId} /> <StatusBar
projectTitle={projectData.title}
playback={featureData.playback}
selectedEventId={featureData.selectedEventId}
firstStart={firstEvent?.timeStart}
firstId={firstEvent?.id}
lastEnd={lastEvent?.timeEnd}
lastId={lastEvent?.id}
/>
</div> </div>
); );
} }
@@ -1,6 +1,13 @@
@use '../../../../src/theme/ontimeColours' as *; @use '../../../../src/theme/ontimeColours' as *;
@use '../../../../src/theme/v2Styles' as *; @use '../../../../src/theme/v2Styles' as *;
@mixin column {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.statusBar { .statusBar {
position: absolute; position: absolute;
bottom: 0; bottom: 0;
@@ -9,28 +16,55 @@
z-index: 2; z-index: 2;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid $white-10; border-top: 1px solid $white-10;
box-shadow: $large-bottom-drawer-shadow; box-shadow: $large-bottom-drawer-shadow;
display: grid;
grid-template-areas:
"playback timer1B timer2B timer3B";
grid-template-columns: 1fr auto auto auto;
column-gap: 1.5rem;
align-items: center;
} }
.playbackIcon { .playbackIcon {
grid-area: playback;
font-size: 2rem; font-size: 2rem;
color: $gray-700; color: $gray-700;
} }
.column { .timeNow {
display: flex; grid-area: timer1B;
flex-direction: column; @include column;
align-items: center;
text-align: center;
} }
.clock { .elapsedTime {
display: flex; grid-area: timer2B;
gap: 1.5rem; @include column;
}
.runningTime {
grid-area: timer3B;
@include column;
}
.title {
grid-area: title;
font-size: 1.5rem;
padding-left: 0.5rem;
display: none;
}
.startTime {
grid-area: timer2A;
@include column;
display: none;
}
.endTime {
grid-area: timer3A;
@include column;
display: none;
} }
.label { .label {
@@ -44,8 +78,23 @@
// tablet // tablet
@media (min-width: $min-tablet) { @media (min-width: $min-tablet) {
.clock { .statusBar {
grid-template-areas:
"playback timer1B timer2B timer3B"
"title timer1A timer2A timer3A";
row-gap: 0.25rem;
column-gap: 2rem;
}
.title {
display: block;
}
.startTime {
display: flex;
}
.endTime {
display: flex; display: flex;
gap: 1.5rem;
} }
} }
@@ -1,33 +1,52 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { OntimeEvent, Playback } from 'ontime-types'; import { Playback } from 'ontime-types';
import { millisToString } from 'ontime-utils'; import { millisToString } from 'ontime-utils';
import PlaybackIcon from '../../../common/components/playback-icon/PlaybackIcon'; import PlaybackIcon from '../../../common/components/playback-icon/PlaybackIcon';
import useMediaQuery from '../../../common/context/useMediaQuery';
import { useTimer } from '../../../common/hooks/useSocket'; import { useTimer } from '../../../common/hooks/useSocket';
import { formatTime } from '../../../common/utils/time'; import { formatTime } from '../../../common/utils/time';
import styles from './StatusBar.module.scss'; import styles from './StatusBar.module.scss';
export default function StatusBar({ interface StatusBarProps {
playback, projectTitle: string;
lastEvent,
selectedEventId,
}: {
playback: Playback; playback: Playback;
lastEvent: OntimeEvent | null;
selectedEventId: string | null; selectedEventId: string | null;
}) { firstStart?: number;
const timer = useTimer(); firstId?: string;
const isMobile = useMediaQuery('(max-width: 450px)'); lastEnd?: number;
lastId?: string;
}
const getTimeEnd = () => { export default function StatusBar(props: StatusBarProps) {
if (lastEvent === null) { const { projectTitle, playback, selectedEventId, firstStart, firstId, lastEnd, lastId } = props;
const timer = useTimer();
const getTimeStart = () => {
if (firstStart === undefined) {
return '...'; return '...';
} }
const timeEnd = lastEvent.id === selectedEventId ? timer.expectedFinish : lastEvent.timeEnd; if (selectedEventId) {
return millisToString(timeEnd); if (firstId === selectedEventId) {
return millisToString(timer.expectedFinish);
}
}
return millisToString(firstStart);
};
const getTimeEnd = () => {
if (lastEnd === undefined) {
return '...';
}
if (selectedEventId) {
if (lastId === selectedEventId) {
return millisToString(timer.expectedFinish);
}
}
return millisToString(lastEnd);
}; };
// use user defined format // use user defined format
@@ -44,26 +63,28 @@ export default function StatusBar({
return ( return (
<div className={styles.statusBar}> <div className={styles.statusBar}>
<span className={styles.title}>{projectTitle}</span>
<div className={styles.startTime}>
<span className={styles.label}>Scheduled start</span>
<span className={styles.timer}>{getTimeStart()}</span>
</div>
<div className={styles.endTime}>
<span className={styles.label}>Scheduled end</span>
<span className={styles.timer}>{getTimeEnd()}</span>
</div>
{PlaybackIconComponent} {PlaybackIconComponent}
<div className={styles.clock}> <div className={styles.timeNow}>
<div className={styles.column}> <span className={styles.label}>Time now</span>
<span className={styles.label}>Time now</span> <span className={styles.timer}>{timeNow}</span>
<span className={styles.timer}>{timeNow}</span> </div>
</div> <div className={styles.elapsedTime}>
{!isMobile && ( <span className={styles.label}>Elapsed time</span>
<div className={styles.column}> <span className={styles.timer}>{elapsedTime}</span>
<span className={styles.label}>Last end</span> </div>
<span className={styles.timer}>{getTimeEnd()}</span> <div className={styles.runningTime}>
</div> <span className={styles.label}>Running timer</span>
)} <span className={styles.timer}>{runningTime}</span>
<div className={styles.column}>
<span className={styles.label}>Elapsed time</span>
<span className={styles.timer}>{elapsedTime}</span>
</div>
<div className={styles.column}>
<span className={styles.label}>Running timer</span>
<span className={styles.timer}>{runningTime}</span>
</div>
</div> </div>
</div> </div>
); );