From 3626b5b35794d2dbc1fc1612c5fb9c6d1af4599c Mon Sep 17 00:00:00 2001 From: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Date: Tue, 22 Nov 2022 22:53:52 +0100 Subject: [PATCH] V2 event block (#261) * feat: implement progress bar on running event * refactor: use event action hook * refactor: align backend data --- client/src/common/hooks/useSocket.ts | 5 +- .../event-block/EventBlock.module.scss | 146 ++++++++---------- .../rundown/event-block/EventBlock.tsx | 83 +++++----- .../EventBlockProgressBar.module.scss | 29 ++++ .../composite/EventBlockProgressBar.tsx | 34 ++++ client/src/theme/_main.scss | 5 +- server/src/classes/timer/EventTimer.js | 2 +- server/src/controllers/OscController.js | 6 +- 8 files changed, 181 insertions(+), 129 deletions(-) create mode 100644 client/src/features/rundown/event-block/composite/EventBlockProgressBar.module.scss create mode 100644 client/src/features/rundown/event-block/composite/EventBlockProgressBar.tsx diff --git a/client/src/common/hooks/useSocket.ts b/client/src/common/hooks/useSocket.ts index 363a7e098..d2444be92 100644 --- a/client/src/common/hooks/useSocket.ts +++ b/client/src/common/hooks/useSocket.ts @@ -131,8 +131,9 @@ export const setEventPlayback = { const emptyTimer = { clock: 0, - running: 0, - isNegative: false, + current: 0, + secondaryTimer: null, + duration: null, startedAt: null, expectedFinish: null, }; diff --git a/client/src/features/rundown/event-block/EventBlock.module.scss b/client/src/features/rundown/event-block/EventBlock.module.scss index dfa3ebbed..4aa9bb8e2 100644 --- a/client/src/features/rundown/event-block/EventBlock.module.scss +++ b/client/src/features/rundown/event-block/EventBlock.module.scss @@ -1,18 +1,74 @@ @use '../../../theme/main' as *; @use '../blockMixins' as *; +.statusElements { + grid-area: estatus; + display: grid; + grid-template-areas: + "notes status" + "progb progb"; + gap: 2px; + grid-template-rows: auto 4px; + + .eventNote { + grid-area: notes; + display: block; + font-size: 15px; + color: $notes-color; + line-height: 15px; + } + + .eventStatus { + grid-area: status; + + display: flex; + justify-content: flex-end; + gap: $element-spacing; + + .statusIcon { + width: 24px; + height: 24px; + border-radius: 12px; + padding: 4px; + color: $text-gray-disabled; + } + + .statusIcon.statusNext { + border: 1px solid transparent; + + &.enabled { + color: $text-white; + background-color: $ontime-accent; + } + } + + .statusIcon.statusDelay { + &.enabled { + color: $text-white; + background-color: $ontime-delay; + } + } + + .statusIcon.statusPublic { + &.enabled { + color: $text-white; + background-color: $ontime-roll; + } + } + } +} + .eventBlock { // layout display: grid; grid-template-areas: "binder pb-actions times actions" "binder pb-actions title title" - "binder pb-actions notes status" - "binder progb progb progb"; + "binder pb-actions estatus estatus"; grid-template-columns: $binder-width auto 1fr auto; - grid-template-rows: 36px 36px 36px $element-spacing; + grid-template-rows: 36px 36px 36px; align-items: center; - margin:4px 2px; + margin: 4px 2px; padding-right: $clearance; // style - general @@ -23,7 +79,7 @@ background-color: $bg-container-l2; border: $border-l3; - // variant + // variant &.selected { background-color: $bg-container-l3; } @@ -112,14 +168,6 @@ } } - .eventNote { - grid-area: notes; - display: block; - font-size: 14px; - color: $notes-color; - line-height: 15px; - } - .eventActions { grid-area: actions; @@ -128,44 +176,6 @@ justify-content: flex-end; } - .eventStatus { - grid-area: status; - - display: flex; - justify-content: flex-end; - gap: $element-spacing; - - .statusIcon { - width: 24px; - height: 24px; - border-radius: 12px; - padding: 4px; - color: $text-gray-disabled; - } - - .statusIcon.statusNext { - border: 1px solid transparent; - - &.enabled { - color: $text-white; - background-color: $ontime-accent; - } - } - - .statusIcon.statusDelay { - &.enabled { - color: $text-white; - background-color: $ontime-delay; - } - } - - .statusIcon.statusPublic { - &.enabled { - color: $text-white; - background-color: $ontime-roll; - } - } - } .eventOptions { margin: $element-spacing 16px $element-spacing 0; @@ -176,36 +186,12 @@ grid-area: progb; border-radius: 1px; background-color: $bg-container-l1; - margin-bottom: 4px; - margin-left: 4px; + opacity: 1; // layout height: 100%; - - .progressBar { - // layout - height: 100%; - width: 0; - border-radius: 1px 0 0 1px; - - // animations - transition: 1s linear; - transition-property: width; - - &.start { - background-color: $ontime-accent; - } - - &.pause { - background-color: $ontime-paused; - } - - &.roll { - background-color: $ontime-roll; - } - - &.overtime { - background-color: red; - } - } } + +.progressBg.hidden { + opacity: 0; +} \ No newline at end of file diff --git a/client/src/features/rundown/event-block/EventBlock.tsx b/client/src/features/rundown/event-block/EventBlock.tsx index e6dc3700d..26b05e0a8 100644 --- a/client/src/features/rundown/event-block/EventBlock.tsx +++ b/client/src/features/rundown/event-block/EventBlock.tsx @@ -17,12 +17,14 @@ import TooltipActionBtn from 'common/components/buttons/TooltipActionBtn'; import { getAccessibleColour } from 'common/utils/styleUtils'; import { useAtom } from 'jotai'; +import { useEventAction } from '../../../common/hooks/useEventAction'; import { setEventPlayback } from '../../../common/hooks/useSocket'; import { Playstate } from '../../../common/models/OntimeTypes'; import { tooltipDelayMid } from '../../../ontimeConfig'; import { EventItemActions } from '../RundownEntry'; import EventBlockActionMenu from './composite/EventBlockActionMenu'; +import EventBlockProgressBar from './composite/EventBlockProgressBar'; import EventBlockTimers from './composite/EventBlockTimers'; import style from './EventBlock.module.scss'; @@ -80,6 +82,7 @@ export default function EventBlock(props: EventBlockProps) { } = props; const [openId, setOpenId] = useAtom(editorEventId); + const { updateEvent } = useEventAction(); const [blockTitle, setBlockTitle] = useState(title || ''); const binderColours = colour && getAccessibleColour(colour); @@ -99,14 +102,11 @@ export default function EventBlock(props: EventBlockProps) { const cleanVal = text.trim(); setBlockTitle(cleanVal); - // Todo: no need for action handler - actionHandler('update', { field: 'title', value: cleanVal }); + updateEvent({ id: eventId, title: cleanVal }); }, - [actionHandler, title], + [updateEvent, title], ); - // Todo: data should come from socket - const progress = `${Math.random() * 100}%`; const eventIsPlaying = selected && playback === 'start'; const playBtnStyles = { _hover: {} }; if (!skip && eventIsPlaying) { @@ -136,12 +136,6 @@ export default function EventBlock(props: EventBlockProps) { {eventIndex} -
-
-
- {note} +
+ {note} +
+ +
+
+ + + + + + + + + + + + + + + +
+
-
- - - - - - - - - - - - - - - -
)} diff --git a/client/src/features/rundown/event-block/composite/EventBlockProgressBar.module.scss b/client/src/features/rundown/event-block/composite/EventBlockProgressBar.module.scss new file mode 100644 index 000000000..bf3bd48d5 --- /dev/null +++ b/client/src/features/rundown/event-block/composite/EventBlockProgressBar.module.scss @@ -0,0 +1,29 @@ +@use '../../../../theme/main' as *; + +.progressBar { + // layout + height: 100%; + width: 0; + border-radius: 1px 0 0 1px; + + // animations + transition: 1s linear; + transition-property: width; + + &.start { + background-color: $ontime-accent; + } + + &.pause { + background-color: $ontime-paused; + } + + &.roll { + background-color: $ontime-roll; + } + + &.overtime { + background-color: $ontime-pink-variant; + } +} + diff --git a/client/src/features/rundown/event-block/composite/EventBlockProgressBar.tsx b/client/src/features/rundown/event-block/composite/EventBlockProgressBar.tsx new file mode 100644 index 000000000..efba8ef8c --- /dev/null +++ b/client/src/features/rundown/event-block/composite/EventBlockProgressBar.tsx @@ -0,0 +1,34 @@ +import { useTimer } from '../../../../common/hooks/useSocket'; +import { Playstate } from '../../../../common/models/OntimeTypes'; +import { clamp } from '../../../../common/utils/math'; + +import style from './EventBlockProgressBar.module.scss'; + +interface EventBlockProgressBarProps { + playback?: Playstate; +} + +export default function EventBlockProgressBar(props: EventBlockProgressBarProps) { + const { playback } = props; + const { data: timer } = useTimer(); + const now = Math.floor(Math.max((timer?.current ?? 1) / 1000, 0)); + const complete = (timer?.duration ?? 1) / 1000; + const elapsed = clamp(100 - (now * 100) / complete, 0, 100); + const progress = `${elapsed}%`; + + if (timer?.current != null && timer?.current < 0) { + return ( +
+ ); + } + + return ( +
+ ); +} diff --git a/client/src/theme/_main.scss b/client/src/theme/_main.scss index 5b4ed0ded..58ecdf1a7 100644 --- a/client/src/theme/_main.scss +++ b/client/src/theme/_main.scss @@ -1,3 +1,6 @@ +$transition-time-action: 0.1s; +$transition-time-feedback: 0.3s; + //////////////////////////////////// general app colours $bg-black-gradient: #202020; @@ -42,7 +45,7 @@ $ontime-red: #E4281E; // outdent in level2 - $bg-gray-950 //////////////////////////////////// editor -$notes-color: #9fd8ff; +$notes-color: #f6f6f6; $text-white: #fffffa; $text-gray-disabled: #505050; diff --git a/server/src/classes/timer/EventTimer.js b/server/src/classes/timer/EventTimer.js index abae5368c..406d078ab 100644 --- a/server/src/classes/timer/EventTimer.js +++ b/server/src/classes/timer/EventTimer.js @@ -119,8 +119,8 @@ export class EventTimer extends Timer { current: this.current, secondaryTimer: this.secondaryTimer, duration: this.duration, - expectedFinish: this._getExpectedFinish(), startedAt: this._startedAt, + expectedFinish: this._getExpectedFinish(), }; this.socket.send('ontime-timer', featureData); } diff --git a/server/src/controllers/OscController.js b/server/src/controllers/OscController.js index 6321bd33c..fb3088638 100644 --- a/server/src/controllers/OscController.js +++ b/server/src/controllers/OscController.js @@ -1,5 +1,5 @@ import { Server } from 'node-osc'; -import { PlaybackService } from '../services/playbackService'; +import { PlaybackService } from '../services/playbackService.js'; import { messageManager } from '../classes/message-manager/MessageManager.js'; import { socketProvider } from '../classes/socket/SocketController.js'; import { ADDRESS_MESSAGE_CONTROL } from '../classes/socket/socketConfig.js'; @@ -22,7 +22,7 @@ export const initiateOSC = (config) => { oscServer.on('error', console.error); - oscServer.on('message', function (msg) { + oscServer.on('message', function(msg) { // message should look like /ontime/{path} {args} where // ontime: fixed message for app // path: command to be called @@ -125,7 +125,7 @@ export const initiateOSC = (config) => { if (isNaN(eventIndex) || eventIndex <= 0) { socketProvider.error( 'RX', - `OSC IN: event index not recognised or out of range ${eventIndex}`, + `OSC IN: event index not recognised or out of range ${eventIndex}` ); } else { PlaybackService.loadByIndex(eventIndex - 1);