From 5be60ff6c3d0113baa8e9c59c56d7208d00719c6 Mon Sep 17 00:00:00 2001
From: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
Date: Fri, 24 Nov 2023 12:04:38 +0100
Subject: [PATCH] feat: show progress in op and cuesheet (#605)
---
.husky/pre-commit | 2 +-
apps/client/package.json | 1 +
.../MultiPartProgressBar.scss | 4 +-
.../MultiPartProgressBar.tsx | 28 +++---
.../cuesheet/CuesheetWrapper.module.scss | 4 +-
.../src/features/cuesheet/CuesheetWrapper.tsx | 2 +
.../CuesheetProgress.module.scss | 3 +
.../cuesheet-progress/CuesheetProgress.tsx | 24 +++++
.../follow-button/FollowButton.module.scss | 8 +-
.../operator/status-bar/StatusBar.module.scss | 11 ++-
.../operator/status-bar/StatusBar.tsx | 85 +++--------------
.../operator/status-bar/StatusBarProgress.tsx | 30 ++++++
.../operator/status-bar/StatusBarTimers.tsx | 94 +++++++++++++++++++
.../src/features/viewers/timer/Timer.tsx | 4 +-
apps/client/src/theme/_mixins.scss | 17 ----
apps/electron/package.json | 1 +
apps/server/package.json | 1 +
packages/types/package.json | 3 +-
packages/utils/package.json | 1 +
19 files changed, 210 insertions(+), 113 deletions(-)
create mode 100644 apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.module.scss
create mode 100644 apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.tsx
create mode 100644 apps/client/src/features/operator/status-bar/StatusBarProgress.tsx
create mode 100644 apps/client/src/features/operator/status-bar/StatusBarTimers.tsx
diff --git a/.husky/pre-commit b/.husky/pre-commit
index 58993aaee..a5a29d9f7 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -1,4 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
-pnpm lint
+pnpm lint-staged
diff --git a/apps/client/package.json b/apps/client/package.json
index 653c5ee96..c89afbdf3 100644
--- a/apps/client/package.json
+++ b/apps/client/package.json
@@ -41,6 +41,7 @@
"build:electron": "cross-env NODE_ENV=local vite build",
"build:docker": "vite build",
"lint": "eslint . --quiet",
+ "lint-staged": "eslint",
"test": "vitest",
"test:pipeline": "vitest run",
"cleanup": "rm -rf .turbo && rm -rf node_modules && rm -rf build"
diff --git a/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.scss b/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.scss
index 4b3ed43cd..b9378798c 100644
--- a/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.scss
+++ b/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.scss
@@ -10,6 +10,7 @@ $progress-bar-br: 3px;
border-radius: $progress-bar-br;
background-color: var(--timer-progress-bg-override, $viewer-card-bg-color);
display: flex;
+ overflow: hidden;
&--hidden {
display: none;
@@ -31,7 +32,6 @@ $progress-bar-br: 3px;
position: absolute;
height: inherit;
right: 0;
- border-radius: $progress-bar-br;
width: 100%;
}
@@ -39,12 +39,10 @@ $progress-bar-br: 3px;
position: absolute;
height: inherit;
right: 0;
- border-radius: 0 $progress-bar-br $progress-bar-br 0;
}
.multiprogress-bar__bg-danger {
position: absolute;
height: inherit;
right: 0;
- border-radius: 0 $progress-bar-br $progress-bar-br 0;
}
\ No newline at end of file
diff --git a/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.tsx b/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.tsx
index d44725f52..151e3efad 100644
--- a/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.tsx
+++ b/apps/client/src/common/components/multi-part-progress-bar/MultiPartProgressBar.tsx
@@ -3,7 +3,7 @@ import { clamp } from '../../utils/math';
import './MultiPartProgressBar.scss';
interface MultiPartProgressBar {
- now: number;
+ now: number | null;
complete: number;
normalColor: string;
warning: number;
@@ -17,23 +17,27 @@ interface MultiPartProgressBar {
export default function MultiPartProgressBar(props: MultiPartProgressBar) {
const { now, complete, normalColor, warning, warningColor, danger, dangerColor, hidden, className = '' } = props;
- const percentComplete = 100 - clamp(100 - (Math.max(now, 0) * 100) / complete, 0, 100);
+ const percentComplete = 100 - clamp(100 - (Math.max(now ?? 0, 0) * 100) / complete, 0, 100);
const dangerWidth = clamp((danger / complete) * 100, 0, 100);
const warningWidth = clamp((warning / complete) * 100, 0, 100);
return (
-
-
-
-
+ {now !== null && (
+ <>
+
+
+
+
+ >
+ )}
);
}
diff --git a/apps/client/src/features/cuesheet/CuesheetWrapper.module.scss b/apps/client/src/features/cuesheet/CuesheetWrapper.module.scss
index cac2a120f..da28f7d67 100644
--- a/apps/client/src/features/cuesheet/CuesheetWrapper.module.scss
+++ b/apps/client/src/features/cuesheet/CuesheetWrapper.module.scss
@@ -19,6 +19,6 @@
& > * {
border: 1px solid $white-10;
- border-radius: 4px;
+ border-radius: 3px;
}
-}
\ No newline at end of file
+}
diff --git a/apps/client/src/features/cuesheet/CuesheetWrapper.tsx b/apps/client/src/features/cuesheet/CuesheetWrapper.tsx
index 19b996408..dfd0ef5d0 100644
--- a/apps/client/src/features/cuesheet/CuesheetWrapper.tsx
+++ b/apps/client/src/features/cuesheet/CuesheetWrapper.tsx
@@ -8,6 +8,7 @@ import useRundown from '../../common/hooks-query/useRundown';
import useUserFields from '../../common/hooks-query/useUserFields';
import ExportModal, { ExportType } from '../modals/export-modal/ExportModal';
+import CuesheetProgress from './cuesheet-progress/CuesheetProgress';
import CuesheetTableHeader from './cuesheet-table-header/CuesheetTableHeader';
import Cuesheet from './Cuesheet';
import { makeCuesheetColumns } from './cuesheetCols';
@@ -140,6 +141,7 @@ export default function CuesheetWrapper() {
return (
+
diff --git a/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.module.scss b/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.module.scss
new file mode 100644
index 000000000..b82de38a8
--- /dev/null
+++ b/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.module.scss
@@ -0,0 +1,3 @@
+.progressOverride {
+ height: 1rem;
+}
diff --git a/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.tsx b/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.tsx
new file mode 100644
index 000000000..0790d6aba
--- /dev/null
+++ b/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.tsx
@@ -0,0 +1,24 @@
+import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar';
+import { useTimer } from '../../../common/hooks/useSocket';
+import useViewSettings from '../../../common/hooks-query/useViewSettings';
+
+import styles from "./CuesheetProgress.module.scss"
+
+export default function CuesheetProgress() {
+ const { data } = useViewSettings();
+ const timer = useTimer();
+ const totalTime = (timer.duration ?? 0) + (timer.addedTime ?? 0);
+
+ return (
+
+ );
+}
diff --git a/apps/client/src/features/operator/follow-button/FollowButton.module.scss b/apps/client/src/features/operator/follow-button/FollowButton.module.scss
index af4a9db25..fc4b9f9cc 100644
--- a/apps/client/src/features/operator/follow-button/FollowButton.module.scss
+++ b/apps/client/src/features/operator/follow-button/FollowButton.module.scss
@@ -2,9 +2,11 @@
@use '../../../../src/theme/v2Styles' as *;
.followButton {
- position: relative;
- bottom: 12rem;
- margin: 0 auto;
+ position: fixed;
+ bottom: 1.5rem;
+ bottom: calc(1.5rem + env(safe-area-inset-bottom));
+ left: 50%;
+ transform: translateX(-50%);
z-index: 1;
display: flex;
diff --git a/apps/client/src/features/operator/status-bar/StatusBar.module.scss b/apps/client/src/features/operator/status-bar/StatusBar.module.scss
index 30582f2f3..47757a8c3 100644
--- a/apps/client/src/features/operator/status-bar/StatusBar.module.scss
+++ b/apps/client/src/features/operator/status-bar/StatusBar.module.scss
@@ -15,16 +15,19 @@
background-color: $gray-1350;
z-index: 2;
- padding: 0.5rem 1rem;
border-bottom: 1px solid $white-10;
box-shadow: $large-top-drawer-shadow;
+}
+.timers {
display: grid;
+ padding: 0.5rem 1rem;
grid-template-areas:
"playback timer1B timer2B timer3B";
grid-template-columns: 1fr auto auto auto;
column-gap: 1.5rem;
align-items: center;
+
}
.playbackIcon {
@@ -83,7 +86,7 @@
// tablet
@media (min-width: $min-tablet) {
- .statusBar {
+ .timers {
grid-template-areas:
"playback timer1B timer2A timer3A"
"title title timer2B timer3B";
@@ -103,3 +106,7 @@
display: flex;
}
}
+
+.progressOverride {
+ border-radius: 0;
+}
diff --git a/apps/client/src/features/operator/status-bar/StatusBar.tsx b/apps/client/src/features/operator/status-bar/StatusBar.tsx
index cf76d886f..c6504c950 100644
--- a/apps/client/src/features/operator/status-bar/StatusBar.tsx
+++ b/apps/client/src/features/operator/status-bar/StatusBar.tsx
@@ -1,11 +1,9 @@
-import { useMemo } from 'react';
import { Playback } from 'ontime-types';
-import { millisToString } from 'ontime-utils';
-import PlaybackIcon from '../../../common/components/playback-icon/PlaybackIcon';
-import { useTimer } from '../../../common/hooks/useSocket';
-import { cx } from '../../../common/utils/styleUtils';
-import { formatTime } from '../../../common/utils/time';
+import useViewSettings from '../../../common/hooks-query/useViewSettings';
+
+import StatusBarProgress from './StatusBarProgress';
+import StatusBarTimers from './StatusBarTimers';
import styles from './StatusBar.module.scss';
@@ -22,73 +20,20 @@ interface StatusBarProps {
export default function StatusBar(props: StatusBarProps) {
const { projectTitle, playback, selectedEventId, firstStart, firstId, lastEnd, lastId } = props;
- const timer = useTimer();
-
- const getTimeStart = () => {
- if (firstStart === undefined) {
- return '...';
- }
-
- 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
- const timeNow = formatTime(timer.clock, {
- showSeconds: true,
- });
-
- const runningTime = millisToString(timer.current);
- const elapsedTime = millisToString(timer.elapsed);
-
- const PlaybackIconComponent = useMemo(() => {
- const isPlaying = playback === Playback.Play || playback === Playback.Roll;
- const classes = cx([styles.playbackIcon, isPlaying ? styles.active : null]);
- return ;
- }, [playback]);
+ const { data } = useViewSettings();
return (
- {PlaybackIconComponent}
-
- Time now
- {timeNow}
-
-
- Elapsed time
- {elapsedTime}
-
-
- Running timer
- {runningTime}
-
-
-
{projectTitle}
-
- Scheduled start
- {getTimeStart()}
-
-
- Scheduled end
- {getTimeEnd()}
-
+
+ {data &&
}
);
}
diff --git a/apps/client/src/features/operator/status-bar/StatusBarProgress.tsx b/apps/client/src/features/operator/status-bar/StatusBarProgress.tsx
new file mode 100644
index 000000000..bb4f06e8f
--- /dev/null
+++ b/apps/client/src/features/operator/status-bar/StatusBarProgress.tsx
@@ -0,0 +1,30 @@
+import { ViewSettings } from 'ontime-types';
+
+import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar';
+import { useTimer } from '../../../common/hooks/useSocket';
+
+import styles from './StatusBar.module.scss';
+
+interface StatusBarProgressProps {
+ viewSettings: ViewSettings;
+}
+
+export default function StatusBarProgress(props: StatusBarProgressProps) {
+ const { viewSettings } = props;
+
+ const timer = useTimer();
+ const totalTime = (timer.duration ?? 0) + (timer.addedTime ?? 0);
+
+ return (
+
+ );
+}
diff --git a/apps/client/src/features/operator/status-bar/StatusBarTimers.tsx b/apps/client/src/features/operator/status-bar/StatusBarTimers.tsx
new file mode 100644
index 000000000..581a1ffc0
--- /dev/null
+++ b/apps/client/src/features/operator/status-bar/StatusBarTimers.tsx
@@ -0,0 +1,94 @@
+import { useMemo } from 'react';
+import { Playback } from 'ontime-types';
+import { millisToString } from 'ontime-utils';
+
+import PlaybackIcon from '../../../common/components/playback-icon/PlaybackIcon';
+import { useTimer } from '../../../common/hooks/useSocket';
+import { cx } from '../../../common/utils/styleUtils';
+import { formatTime } from '../../../common/utils/time';
+
+import styles from './StatusBar.module.scss';
+
+interface StatusBarTimersProps {
+ projectTitle: string;
+ playback: Playback;
+ selectedEventId: string | null;
+ firstStart?: number;
+ firstId?: string;
+ lastEnd?: number;
+ lastId?: string;
+}
+
+export default function StatusBarTimers(props: StatusBarTimersProps) {
+ const { projectTitle, playback, selectedEventId, firstStart, firstId, lastEnd, lastId } = props;
+
+ const timer = useTimer();
+
+ const getTimeStart = () => {
+ if (firstStart === undefined) {
+ return '...';
+ }
+
+ 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);
+ };
+
+ const PlaybackIconComponent = useMemo(() => {
+ const isPlaying = playback === Playback.Play || playback === Playback.Roll;
+ const classes = cx([styles.playbackIcon, isPlaying ? styles.active : null]);
+ return ;
+ }, [playback]);
+
+ // use user defined format
+ const timeNow = formatTime(timer.clock, {
+ showSeconds: true,
+ });
+
+ const runningTime = millisToString(timer.current);
+ const elapsedTime = millisToString(timer.elapsed);
+
+ return (
+
+ {PlaybackIconComponent}
+
+ Time now
+ {timeNow}
+
+
+ Elapsed time
+ {elapsedTime}
+
+
+ Running timer
+ {runningTime}
+
+
+
{projectTitle}
+
+ Scheduled start
+ {getTimeStart()}
+
+
+ Scheduled end
+ {getTimeEnd()}
+
+
+ );
+}
diff --git a/apps/client/src/features/viewers/timer/Timer.tsx b/apps/client/src/features/viewers/timer/Timer.tsx
index e0f1a9c6e..1b9b7a6ce 100644
--- a/apps/client/src/features/viewers/timer/Timer.tsx
+++ b/apps/client/src/features/viewers/timer/Timer.tsx
@@ -124,7 +124,7 @@ export default function Timer(props: TimerProps) {
if (showExternal) {
timerFontSize *= 0.8;
}
- const externalFontSize = timerFontSize * 0.4
+ const externalFontSize = timerFontSize * 0.4;
const timerContainerClasses = `timer-container ${showBlinking ? (showOverlay ? '' : 'blink') : ''}`;
const timerClasses = `timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''}`;
@@ -170,7 +170,7 @@ export default function Timer(props: TimerProps) {
{!userOptions.hideProgress && (