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 useFollowComponent from '../../common/hooks/useFollowComponent';
import { useOperator } from '../../common/hooks/useSocket';
import useEventData from '../../common/hooks-query/useEventData';
import useRundown from '../../common/hooks-query/useRundown';
import useUserFields from '../../common/hooks-query/useUserFields';
import { isStringBoolean } from '../../common/utils/viewUtils';
@@ -25,6 +26,8 @@ const selectedOffset = 50;
export default function Operator() {
const { data, status } = useRundown();
const { data: userFields, status: userFieldsStatus } = useUserFields();
const { data: projectData, status: projectDataStatus } = useEventData();
const featureData = useOperator();
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...' />;
}
@@ -77,12 +87,14 @@ export default function Operator() {
const subscribe = searchParams.get('subscribe') as keyof UserFields | null;
const subscribedAlias = subscribe ? userFields[subscribe] : '';
const showSeconds = isStringBoolean(searchParams.get('showseconds'));
const lastEvent = getLastEvent(data);
const operatorOptions = getOperatorOptions(userFields);
let isPast = Boolean(featureData.selectedEventId);
const hidePast = isStringBoolean(searchParams.get('hidepast'));
const firstEvent = getLastEvent(data);
const lastEvent = getLastEvent(data);
return (
<div className={style.operatorContainer}>
<NavigationMenu />
@@ -124,7 +136,15 @@ export default function Operator() {
<div className={style.spacer} />
</div>
<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>
);
}
@@ -1,6 +1,13 @@
@use '../../../../src/theme/ontimeColours' as *;
@use '../../../../src/theme/v2Styles' as *;
@mixin column {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.statusBar {
position: absolute;
bottom: 0;
@@ -9,28 +16,55 @@
z-index: 2;
padding: 0.5rem 1rem;
display: flex;
justify-content: space-between;
align-items: center;
border-top: 1px solid $white-10;
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 {
grid-area: playback;
font-size: 2rem;
color: $gray-700;
}
.column {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
.timeNow {
grid-area: timer1B;
@include column;
}
.clock {
display: flex;
gap: 1.5rem;
.elapsedTime {
grid-area: timer2B;
@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 {
@@ -44,8 +78,23 @@
// 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;
gap: 1.5rem;
}
}
@@ -1,33 +1,52 @@
import { useMemo } from 'react';
import { OntimeEvent, Playback } from 'ontime-types';
import { Playback } from 'ontime-types';
import { millisToString } from 'ontime-utils';
import PlaybackIcon from '../../../common/components/playback-icon/PlaybackIcon';
import useMediaQuery from '../../../common/context/useMediaQuery';
import { useTimer } from '../../../common/hooks/useSocket';
import { formatTime } from '../../../common/utils/time';
import styles from './StatusBar.module.scss';
export default function StatusBar({
playback,
lastEvent,
selectedEventId,
}: {
interface StatusBarProps {
projectTitle: string;
playback: Playback;
lastEvent: OntimeEvent | null;
selectedEventId: string | null;
}) {
const timer = useTimer();
const isMobile = useMediaQuery('(max-width: 450px)');
firstStart?: number;
firstId?: string;
lastEnd?: number;
lastId?: string;
}
const getTimeEnd = () => {
if (lastEvent === null) {
export default function StatusBar(props: StatusBarProps) {
const { projectTitle, playback, selectedEventId, firstStart, firstId, lastEnd, lastId } = props;
const timer = useTimer();
const getTimeStart = () => {
if (firstStart === undefined) {
return '...';
}
const timeEnd = lastEvent.id === selectedEventId ? timer.expectedFinish : lastEvent.timeEnd;
return millisToString(timeEnd);
if (selectedEventId) {
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
@@ -44,26 +63,28 @@ export default function StatusBar({
return (
<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}
<div className={styles.clock}>
<div className={styles.column}>
<span className={styles.label}>Time now</span>
<span className={styles.timer}>{timeNow}</span>
</div>
{!isMobile && (
<div className={styles.column}>
<span className={styles.label}>Last end</span>
<span className={styles.timer}>{getTimeEnd()}</span>
</div>
)}
<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 className={styles.timeNow}>
<span className={styles.label}>Time now</span>
<span className={styles.timer}>{timeNow}</span>
</div>
<div className={styles.elapsedTime}>
<span className={styles.label}>Elapsed time</span>
<span className={styles.timer}>{elapsedTime}</span>
</div>
<div className={styles.runningTime}>
<span className={styles.label}>Running timer</span>
<span className={styles.timer}>{runningTime}</span>
</div>
</div>
);