diff --git a/apps/client/src/features/control/playback/playback-buttons/PlaybackButtons.tsx b/apps/client/src/features/control/playback/playback-buttons/PlaybackButtons.tsx index 045afd63ea..1f6d49bf72 100644 --- a/apps/client/src/features/control/playback/playback-buttons/PlaybackButtons.tsx +++ b/apps/client/src/features/control/playback/playback-buttons/PlaybackButtons.tsx @@ -5,8 +5,9 @@ import { IoPlaySkipBack } from '@react-icons/all-files/io5/IoPlaySkipBack'; import { IoPlaySkipForward } from '@react-icons/all-files/io5/IoPlaySkipForward'; import { IoReload } from '@react-icons/all-files/io5/IoReload'; import { IoStop } from '@react-icons/all-files/io5/IoStop'; -import { IoTime} from '@react-icons/all-files/io5/IoTime'; +import { IoTime } from '@react-icons/all-files/io5/IoTime'; import { Playback } from 'ontime-types'; +import { validatePlayback } from 'ontime-utils'; import { setPlayback } from '../../../../common/hooks/useSocket'; import { tooltipDelayMid } from '../../../../ontimeConfig'; @@ -34,7 +35,13 @@ export default function PlaybackButtons(props: PlaybackButtonsProps) { const noEvents = numEvents === 0; const disableGo = isRolling || noEvents || (isLast && !isArmed); - const disablePrev = isRolling || noEvents || isFirst; + const disablePrev = noEvents || isFirst; + + const playbackCan = validatePlayback(playback); + const disableStart = !playbackCan.start; + const disablePause = !playbackCan.pause; + const disableRoll = !playbackCan.roll || noEvents; + const disableStop = !playbackCan.stop; const goModeText = selectedEventIndex === null || isArmed ? 'Start' : 'Next'; const goModeAction = () => { @@ -51,21 +58,11 @@ export default function PlaybackButtons(props: PlaybackButtonsProps) { {goModeText}
- + - +
@@ -82,21 +79,16 @@ export default function PlaybackButtons(props: PlaybackButtonsProps) {
- + - + - + diff --git a/apps/client/src/theme/_v2Styles.scss b/apps/client/src/theme/_v2Styles.scss index 74b0c77715..0f64a0c5b9 100644 --- a/apps/client/src/theme/_v2Styles.scss +++ b/apps/client/src/theme/_v2Styles.scss @@ -24,7 +24,7 @@ $ontime-delay-text: #E69056; $ontime-paused: #c05621; $ontime-stop: #E4281E; $playback-negative: $red-500; -$active-indicator: #899948; +$active-indicator: #8bb33d; $text-black: $gray-1350; // interface panels diff --git a/apps/server/src/services/PlaybackService.ts b/apps/server/src/services/PlaybackService.ts index 5831a23760..813906f4f7 100644 --- a/apps/server/src/services/PlaybackService.ts +++ b/apps/server/src/services/PlaybackService.ts @@ -1,4 +1,5 @@ -import { OntimeEvent, Playback } from 'ontime-types'; +import { OntimeEvent } from 'ontime-types'; +import { validatePlayback } from 'ontime-utils'; import { eventLoader, EventLoader } from '../classes/event-loader/EventLoader.js'; import { eventStore } from '../stores/EventStore.js'; @@ -133,7 +134,7 @@ export class PlaybackService { * Starts playback on selected event */ static start() { - if (eventTimer.playback === Playback.Armed || eventTimer.playback === Playback.Pause) { + if (validatePlayback(eventTimer.playback).start) { eventTimer.start(); const newState = eventTimer.playback; logger.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`); @@ -155,7 +156,7 @@ export class PlaybackService { * Pauses playback on selected event */ static pause() { - if (eventTimer.playback === Playback.Play) { + if (validatePlayback(eventTimer.playback).pause) { eventTimer.pause(); const newState = eventTimer.playback; logger.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`); @@ -166,7 +167,7 @@ export class PlaybackService { * Stops timer and unloads any events */ static stop() { - if (eventTimer.playback !== Playback.Stop) { + if (validatePlayback(eventTimer.playback).stop) { eventLoader.reset(); eventTimer.stop(); const newState = eventTimer.playback; diff --git a/apps/server/src/services/TimerService.ts b/apps/server/src/services/TimerService.ts index 96c9852834..0608dcd241 100644 --- a/apps/server/src/services/TimerService.ts +++ b/apps/server/src/services/TimerService.ts @@ -7,6 +7,13 @@ import { DAY_TO_MS } from '../utils/time.js'; import { integrationService } from './integration-service/IntegrationService.js'; import { getCurrent, getElapsed, getExpectedFinish } from './timerUtils.js'; import { clock } from './Clock.js'; +import { logger } from '../classes/Logger.js'; + +type initialLoadingData = { + startedAt?: number | null; + expectedFinish?: number | null; + current?: number | null; +}; export class TimerService { private readonly _interval: NodeJS.Timer; @@ -113,7 +120,7 @@ export class TimerService { * @param {string} timer.timerType * @param {boolean} timer.skip */ - load(timer) { + load(timer, initialData?: initialLoadingData) { if (timer.skip) { throw new Error('Refuse load of skipped event'); } @@ -129,6 +136,10 @@ export class TimerService { this.pausedTime = 0; this.pausedAt = 0; + if (typeof initialData !== 'undefined') { + this.timer = { ...this.timer, ...initialData }; + } + this._onLoad(); } @@ -146,6 +157,9 @@ export class TimerService { start() { if (!this.loadedTimerId) { + if (this.playback === Playback.Roll) { + logger.error('PLAYBACK', 'Cannot start while waiting for event'); + } return; } @@ -155,12 +169,12 @@ export class TimerService { this.timer.clock = clock.timeNow(); - // add paused time + // add paused time if it exists if (this.pausedTime) { this.timer.addedTime += this.pausedTime; this.pausedAt = null; this.pausedTime = 0; - } else { + } else if (this.timer.startedAt === null) { this.timer.startedAt = this.timer.clock; } @@ -188,10 +202,6 @@ export class TimerService { } pause() { - if (this.playback !== Playback.Play) { - return; - } - this.playback = Playback.Pause; this.timer.clock = clock.timeNow(); this.pausedAt = this.timer.clock; @@ -370,9 +380,11 @@ export class TimerService { // when we load a timer in roll, we do the same things as before // but also pre-populate some data as to the running state - this.load(currentEvent); - this.timer.startedAt = currentEvent.timeStart; - this.timer.expectedFinish = currentEvent.timeEnd; + this.load(currentEvent, { + startedAt: currentEvent.timeStart, + expectedFinish: currentEvent.timeEnd, + current: currentEvent.timeEnd - this.timer.clock, + }); } else if (nextEvent) { // account for day after const nextStart = nextEvent.timeStart < this.timer.clock ? nextEvent.timeStart + DAY_TO_MS : nextEvent.timeStart; diff --git a/packages/utils/index.ts b/packages/utils/index.ts index 323704a96a..6fceea01a6 100644 --- a/packages/utils/index.ts +++ b/packages/utils/index.ts @@ -3,3 +3,4 @@ export { formatFromMillis } from './src/date-utils/formatFromMillis.js'; export { isTimeString } from './src/date-utils/isTimeString.js'; export { millisToString } from './src/date-utils/millisToString.js'; export { generateId } from './src/generate-id/generateId.js'; +export { validatePlayback } from './src/validate-action/validatePlayback.js'; diff --git a/packages/utils/package.json b/packages/utils/package.json index f2e3b63d94..1e5bdd7cfd 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -23,6 +23,7 @@ "eslint-config-prettier": "^8.6.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-simple-import-sort": "^8.0.0", + "ontime-types": "workspace:*", "prettier": "^2.8.3", "typescript": "^4.9.4", "vitest": "^0.30.1" diff --git a/packages/utils/src/validate-action/validatePlayback.ts b/packages/utils/src/validate-action/validatePlayback.ts new file mode 100644 index 0000000000..6911c73ff8 --- /dev/null +++ b/packages/utils/src/validate-action/validatePlayback.ts @@ -0,0 +1,10 @@ +import { Playback } from 'ontime-types'; + +export function validatePlayback(currentPlayback: Playback) { + return { + start: currentPlayback !== Playback.Stop, + pause: currentPlayback === Playback.Play || currentPlayback === Playback.Roll, + roll: true, + stop: currentPlayback !== Playback.Stop, + }; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e07297b7d4..4e3da0fb66 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -267,6 +267,7 @@ importers: eslint-plugin-simple-import-sort: ^8.0.0 luxon: ^3.3.0 nanoid: ^4.0.1 + ontime-types: workspace:* prettier: ^2.8.3 typescript: ^4.9.4 vitest: ^0.30.1 @@ -281,6 +282,7 @@ importers: eslint-config-prettier: 8.6.0_eslint@8.31.0 eslint-plugin-prettier: 4.2.1_do5yx3dogbskqc4h5x5ilvlwyy eslint-plugin-simple-import-sort: 8.0.0_eslint@8.31.0 + ontime-types: link:../types prettier: 2.8.3 typescript: 4.9.4 vitest: 0.30.1