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 && (