diff --git a/apps/client/src/features/rundown/event-block/composite/EventBlockTimers.jsx b/apps/client/src/features/rundown/event-block/composite/EventBlockTimers.tsx
similarity index 69%
rename from apps/client/src/features/rundown/event-block/composite/EventBlockTimers.jsx
rename to apps/client/src/features/rundown/event-block/composite/EventBlockTimers.tsx
index 0d2c24f612..ff90585d69 100644
--- a/apps/client/src/features/rundown/event-block/composite/EventBlockTimers.jsx
+++ b/apps/client/src/features/rundown/event-block/composite/EventBlockTimers.tsx
@@ -1,18 +1,25 @@
-import { useCallback } from 'react';
+import { useCallback, useState } from 'react';
import { millisToString } from 'ontime-utils';
-import PropTypes from 'prop-types';
-
-import { useEmitLog } from '@/common/stores/logger';
import TimeInput from '../../../../common/components/input/time-input/TimeInput';
import { millisToMinutes } from '../../../../common/utils/dateConfig';
-import { validateEntry } from '../../../../common/utils/timesManager';
+import { TimeEntryField, validateEntry } from '../../../../common/utils/timesManager';
+import { EventItemActions } from '../../RundownEntry';
import style from '../EventBlock.module.scss';
-export default function EventBlockTimers(props) {
+interface EventBlockTimerProps {
+ timeStart: number;
+ timeEnd: number;
+ duration: number;
+ delay: number;
+ actionHandler: (action: EventItemActions, payload?: any) => void;
+ previousEnd: number;
+}
+
+export default function EventBlockTimers(props: EventBlockTimerProps) {
const { timeStart, timeEnd, duration, delay, actionHandler, previousEnd } = props;
- const { emitWarning } = useEmitLog();
+ const [warning, setWarnings] = useState({ start: '', end: '', duration: '' });
const delayTime = `${delay >= 0 ? '+' : '-'} ${millisToMinutes(Math.abs(delay))}`;
const newTime = millisToString(timeStart + delay);
@@ -24,21 +31,19 @@ export default function EventBlockTimers(props) {
* @return {boolean}
*/
const handleValidation = useCallback(
- (field, value) => {
+ (field: TimeEntryField, value: number) => {
const valid = validateEntry(field, value, timeStart, timeEnd);
- if (valid.catch) {
- emitWarning(`Time Input Warning: ${valid.catch}`);
- }
+ setWarnings((prev) => ({ ...prev, ...valid.warnings }));
return valid.value;
},
- [emitWarning, timeEnd, timeStart]
+ [timeEnd, timeStart],
);
const handleSubmit = useCallback(
- (field, value) => {
+ (field: TimeEntryField, value: number) => {
actionHandler('update', { field, value });
},
- [actionHandler]
+ [actionHandler],
);
return (
@@ -51,6 +56,7 @@ export default function EventBlockTimers(props) {
delay={delay}
placeholder='Start'
previousEnd={previousEnd}
+ warning={warning.start}
/>
{delay !== 0 && delay !== null && (
@@ -79,12 +87,3 @@ export default function EventBlockTimers(props) {
);
}
-
-EventBlockTimers.propTypes = {
- timeStart: PropTypes.number,
- timeEnd: PropTypes.number,
- duration: PropTypes.number,
- delay: PropTypes.number,
- actionHandler: PropTypes.func,
- previousEnd: PropTypes.number,
-};
diff --git a/apps/client/src/features/rundown/quick-add-block/QuickAddBlock.module.scss b/apps/client/src/features/rundown/quick-add-block/QuickAddBlock.module.scss
index 32a09c55d4..3b255cc1e9 100644
--- a/apps/client/src/features/rundown/quick-add-block/QuickAddBlock.module.scss
+++ b/apps/client/src/features/rundown/quick-add-block/QuickAddBlock.module.scss
@@ -15,6 +15,7 @@
gap: 10%;
.quickBtn {
+ font-weight: 400;
width: auto;
padding: 0 32px;
}
diff --git a/apps/client/src/features/table/TableHeader.jsx b/apps/client/src/features/table/TableHeader.jsx
index fb7371ae33..ffa4cbeef9 100644
--- a/apps/client/src/features/table/TableHeader.jsx
+++ b/apps/client/src/features/table/TableHeader.jsx
@@ -11,7 +11,7 @@ import { TableSettingsContext } from '../../common/context/TableSettingsContext'
import useFullscreen from '../../common/hooks/useFullscreen';
import { useTimer } from '../../common/hooks/useSocket';
import useEventData from '../../common/hooks-query/useEventData';
-import { formatDisplay, millisToSeconds } from '../../common/utils/dateConfig';
+import { formatDisplay } from '../../common/utils/dateConfig';
import { formatTime } from '../../common/utils/time';
import { tooltipDelayFast } from '../../ontimeConfig';
@@ -33,7 +33,7 @@ export default function TableHeader({ handleCSVExport, featureData }) {
// prepare presentation variables
const isOvertime = timer.current < 0;
- const timerNow = `${isOvertime ? '-' : ''}${formatDisplay(millisToSeconds(timer.current))}`;
+ const timerNow = `${isOvertime ? '-' : ''}${formatDisplay(timer.current)}`;
const timeNow = formatTime(timer.clock, {
showSeconds: true,
format: 'hh:mm:ss a',
diff --git a/apps/client/src/features/viewers/backstage/Backstage.tsx b/apps/client/src/features/viewers/backstage/Backstage.tsx
index 67ac843de7..b3f2a70947 100644
--- a/apps/client/src/features/viewers/backstage/Backstage.tsx
+++ b/apps/client/src/features/viewers/backstage/Backstage.tsx
@@ -12,7 +12,7 @@ import ScheduleNav from '../../../common/components/schedule/ScheduleNav';
import TitleCard from '../../../common/components/title-card/TitleCard';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
-import { formatDisplay, millisToSeconds } from '../../../common/utils/dateConfig';
+import { formatDisplay } from '../../../common/utils/dateConfig';
import { getEventsWithDelay } from '../../../common/utils/eventsManager';
import { formatTime } from '../../../common/utils/time';
import { useTranslation } from '../../../translation/TranslationProvider';
@@ -66,7 +66,7 @@ export default function Backstage(props: BackstageProps) {
if (time.current === null) {
stageTimer = '- - : - -';
} else {
- stageTimer = formatDisplay(Math.abs(millisToSeconds(time.current)), true);
+ stageTimer = formatDisplay(Math.abs(time.current), true);
if (isNegative) {
stageTimer = `-${stageTimer}`;
}
diff --git a/apps/client/src/features/viewers/common/viewerUtils.ts b/apps/client/src/features/viewers/common/viewerUtils.ts
index e2dd841bff..54430d253c 100644
--- a/apps/client/src/features/viewers/common/viewerUtils.ts
+++ b/apps/client/src/features/viewers/common/viewerUtils.ts
@@ -1,7 +1,7 @@
import { TimerType } from 'ontime-types';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
-import { formatDisplay, millisToSeconds } from '../../../common/utils/dateConfig';
+import { formatDisplay } from '../../../common/utils/dateConfig';
import { formatTime } from '../../../common/utils/time';
const formatOptions = {
@@ -36,7 +36,7 @@ export function formatTimerDisplay(timer?: string | number | null): string {
} else if (timer === null || typeof timer === 'undefined' || isNaN(timer)) {
display = '-- : -- : --';
} else {
- display = formatDisplay(millisToSeconds(timer), true);
+ display = formatDisplay(timer, true);
}
return display;
diff --git a/apps/client/src/features/viewers/countdown/Countdown.tsx b/apps/client/src/features/viewers/countdown/Countdown.tsx
index ceefc5a7c3..da08026a1f 100644
--- a/apps/client/src/features/viewers/countdown/Countdown.tsx
+++ b/apps/client/src/features/viewers/countdown/Countdown.tsx
@@ -6,7 +6,7 @@ import { overrideStylesURL } from '../../../common/api/apiConstants';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type';
-import { formatDisplay, millisToSeconds } from '../../../common/utils/dateConfig';
+import { formatDisplay } from '../../../common/utils/dateConfig';
import getDelayTo from '../../../common/utils/getDelayTo';
import { formatTime } from '../../../common/utils/time';
import { useTranslation } from '../../../translation/TranslationProvider';
@@ -102,8 +102,8 @@ export default function Countdown(props: CountdownProps) {
runningMessage === TimerMessage.ended
? formatTime(runningTimer, formatOptionsFinished)
: formatDisplay(
- isSelected ? millisToSeconds(runningTimer) : millisToSeconds(runningTimer + delay),
- isSelected || time.waiting,
+ isSelected ? runningTimer : runningTimer + delay,
+ isSelected || runningMessage === TimerMessage.waiting,
);
return (
diff --git a/apps/client/src/features/viewers/countdown/countdown.helpers.ts b/apps/client/src/features/viewers/countdown/countdown.helpers.ts
index 600674c716..981feceff2 100644
--- a/apps/client/src/features/viewers/countdown/countdown.helpers.ts
+++ b/apps/client/src/features/viewers/countdown/countdown.helpers.ts
@@ -21,7 +21,7 @@ export const sanitiseTitle = (title: string | null) => (title ? title : '{no tit
export const fetchTimerData = (
time: TimeManagerType,
follow: OntimeEvent,
- selectedId: string,
+ selectedId: string | null,
): { message: TimerMessage; timer: number } => {
let message;
let timer;
diff --git a/apps/client/src/features/viewers/studio/StudioClock.jsx b/apps/client/src/features/viewers/studio/StudioClock.jsx
index 78f540e2c5..456141f2d0 100644
--- a/apps/client/src/features/viewers/studio/StudioClock.jsx
+++ b/apps/client/src/features/viewers/studio/StudioClock.jsx
@@ -83,7 +83,7 @@ export default function StudioClock(props) {
{title.titleNext}
- {selectedId != null && formatDisplay(time.current)}
+ {selectedId !== null && formatDisplay(time.current)}
{activeIndicators.map((i) => (
diff --git a/apps/client/src/features/viewers/timer/Timer.tsx b/apps/client/src/features/viewers/timer/Timer.tsx
index e985564355..858eb5cc85 100644
--- a/apps/client/src/features/viewers/timer/Timer.tsx
+++ b/apps/client/src/features/viewers/timer/Timer.tsx
@@ -65,7 +65,7 @@ export default function Timer(props: TimerProps) {
const isNegative =
(time.current ?? 0) < 0 && time.timerType !== TimerType.Clock && time.timerType !== TimerType.CountUp;
- const showEndMessage = time.current < 0 && general.endMessage;
+ const showEndMessage = (time.current ?? 1) < 0 && general.endMessage;
const showProgress = time.playback !== Playback.Stop;
const showFinished = time.finished && (time.timerType !== TimerType.Clock || showEndMessage);
const showClock = time.timerType !== TimerType.Clock;
@@ -110,8 +110,8 @@ export default function Timer(props: TimerProps) {
diff --git a/apps/electron/package.json b/apps/electron/package.json
index 0acfda26e1..914b22d4a8 100644
--- a/apps/electron/package.json
+++ b/apps/electron/package.json
@@ -1,6 +1,6 @@
{
"name": "ontime",
- "version": "2.0.0-beta1",
+ "version": "2.0.0-beta2",
"author": "Carlos Valente",
"description": "Time keeping for live events",
"repository": "https://github.com/cpvalente/ontime",
diff --git a/apps/server/package.json b/apps/server/package.json
index 5107560a99..ef84e794bf 100644
--- a/apps/server/package.json
+++ b/apps/server/package.json
@@ -2,7 +2,7 @@
"name": "ontime-server",
"type": "module",
"main": "src/index.ts",
- "version": "2.0.0-beta1",
+ "version": "2.0.0-beta2",
"exports": "./src/index.js",
"dependencies": {
"@sentry/node": "^7.24.1",
diff --git a/apps/server/src/adapters/WebsocketAdapter.ts b/apps/server/src/adapters/WebsocketAdapter.ts
index a5540adfe8..ba274fe71a 100644
--- a/apps/server/src/adapters/WebsocketAdapter.ts
+++ b/apps/server/src/adapters/WebsocketAdapter.ts
@@ -112,7 +112,7 @@ export class SocketServer implements IAdapter {
}
// message is any serializable value
- send(message: unknown) {
+ sendAsJson(message: unknown) {
this.wss?.clients.forEach((client) => {
if (client !== this.wss && client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(message));
diff --git a/apps/server/src/adapters/websocketAux.ts b/apps/server/src/adapters/websocketAux.ts
index a0ed93b1d4..432e421012 100644
--- a/apps/server/src/adapters/websocketAux.ts
+++ b/apps/server/src/adapters/websocketAux.ts
@@ -5,7 +5,7 @@ import { socket } from './WebsocketAdapter.js';
* @param payload -- possible patch payload
*/
export function sendRefetch(payload: any | null = null) {
- socket.send({
+ socket.sendAsJson({
type: 'ontime-refetch',
payload,
});
diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts
index 7e3a6d69e3..355bae9868 100644
--- a/apps/server/src/app.ts
+++ b/apps/server/src/app.ts
@@ -30,6 +30,7 @@ import { integrationService } from './services/integration-service/IntegrationSe
import { logger } from './classes/Logger.js';
import { oscIntegration } from './services/integration-service/OscIntegration.js';
import { populateStyles } from './modules/loadStyles.js';
+import { eventStore, getInitialPayload } from './stores/EventStore.js';
console.log(`Starting Ontime version ${ONTIME_VERSION}`);
@@ -132,7 +133,10 @@ export const startServer = async () => {
expressServer = http.createServer(app);
socket.init(expressServer);
+
+ // provide initial payload to event store
eventLoader.init();
+ eventStore.init(getInitialPayload());
expressServer.listen(serverPort, '0.0.0.0');
diff --git a/apps/server/src/classes/Logger.ts b/apps/server/src/classes/Logger.ts
index d4318a1271..5437d23adf 100644
--- a/apps/server/src/classes/Logger.ts
+++ b/apps/server/src/classes/Logger.ts
@@ -39,7 +39,7 @@ class Logger {
}
try {
- socket.send({
+ socket.sendAsJson({
type: 'ontime-log',
payload: log,
});
diff --git a/apps/server/src/classes/data-provider/DataProvider.ts b/apps/server/src/classes/data-provider/DataProvider.ts
index bf08b57544..8e0448a78e 100644
--- a/apps/server/src/classes/data-provider/DataProvider.ts
+++ b/apps/server/src/classes/data-provider/DataProvider.ts
@@ -2,7 +2,7 @@
* Class Event Provider is a mediator for handling the local db
* and adds logic specific to ontime data
*/
-import { EventData, ViewSettings } from 'ontime-types';
+import { EventData, SupportedEvent, ViewSettings } from 'ontime-types';
import { data, db } from '../../modules/loadDb.js';
import { safeMerge } from './DataProvider.utils.js';
@@ -35,7 +35,9 @@ export class DataProvider {
const eventIndex = data.rundown.findIndex((e) => e.id === eventId);
const persistedEvent = data.rundown[eventIndex];
const newEvent = { ...persistedEvent, ...newData };
- newEvent.revision++;
+ if (newEvent.type === SupportedEvent.Event) {
+ newEvent.revision++;
+ }
data.rundown[eventIndex] = newEvent;
await this.persist();
return data.rundown[eventIndex];
diff --git a/apps/server/src/classes/event-loader/EventLoader.ts b/apps/server/src/classes/event-loader/EventLoader.ts
index 8e2602f896..9dd650da0f 100644
--- a/apps/server/src/classes/event-loader/EventLoader.ts
+++ b/apps/server/src/classes/event-loader/EventLoader.ts
@@ -24,9 +24,9 @@ export class EventLoader {
instance = this;
}
+ // we need to delay init until the store is ready
init() {
- this.reset();
- this.loadedEvent = null;
+ this.reset(false);
}
/**
diff --git a/apps/server/src/external/styles/override.css b/apps/server/src/external/styles/override.css
index 53e82502e7..5f886e2d5f 100644
--- a/apps/server/src/external/styles/override.css
+++ b/apps/server/src/external/styles/override.css
@@ -2,12 +2,14 @@
--background-color-override: #ececec;
--color-override: #101010;
--secondary-color-override: #404040;
- --accent-color-override: #FA5656;
+ --accent-color-override: #fa5656;
--label-color-override: #6c6c6c;
--timer-color-override: #202020;
- --card-background-color-override: #FFF;
+ --card-background-color-override: #fff;
--font-family-override: "Open Sans";
--font-family-bold-override: "Arial Black";
+ --timer-progress-bg-override: #fff;
+ --timer-progress-override: #202020;
}
.timer {
diff --git a/apps/server/src/models/eventsDefinition.ts b/apps/server/src/models/eventsDefinition.ts
index 5a2c46185e..bb41bbf84c 100644
--- a/apps/server/src/models/eventsDefinition.ts
+++ b/apps/server/src/models/eventsDefinition.ts
@@ -34,5 +34,6 @@ export const delay: Omit
= {
};
export const block: Omit = {
+ title: '',
type: SupportedEvent.Block,
};
diff --git a/apps/server/src/services/PlaybackService.ts b/apps/server/src/services/PlaybackService.ts
index 7e68f73abf..f2f00c70da 100644
--- a/apps/server/src/services/PlaybackService.ts
+++ b/apps/server/src/services/PlaybackService.ts
@@ -219,7 +219,9 @@ export class PlaybackService {
if (eventTimer.loadedTimerId) {
const delayInMs = delayTime * 1000 * 60;
eventTimer.delay(delayInMs);
- logger.info('PLAYBACK', `Added ${delayTime} min delay`);
+ delayInMs > 0
+ ? logger.info('PLAYBACK', `Added ${delayTime} min delay`)
+ : logger.info('PLAYBACK', `Removed ${delayTime} min delay`);
}
}
}
diff --git a/apps/server/src/services/RundownService.ts b/apps/server/src/services/RundownService.ts
index ffa3497af8..9f6d9e66bc 100644
--- a/apps/server/src/services/RundownService.ts
+++ b/apps/server/src/services/RundownService.ts
@@ -1,4 +1,4 @@
-import { OntimeBaseEvent, OntimeBlock, OntimeDelay, OntimeEvent } from 'ontime-types';
+import { OntimeBaseEvent, OntimeBlock, OntimeDelay, OntimeEvent, SupportedEvent } from 'ontime-types';
import { generateId } from 'ontime-utils';
import { DataProvider } from '../classes/data-provider/DataProvider.js';
import { block as blockDef, delay as delayDef, event as eventDef } from '../models/eventsDefinition.js';
@@ -218,15 +218,13 @@ export async function reorderEvent(eventId, from, to) {
*/
export async function applyDelay(eventId) {
const rundown = DataProvider.getRundown();
- // AUX
let delayIndex = null;
- let blockIndex = null;
let delayValue = 0;
for (const [index, e] of rundown.entries()) {
// look for delay
if (delayIndex === null) {
- if (e.id === eventId && e.type === 'delay') {
+ if (e.id === eventId && e.type === SupportedEvent.Delay) {
delayValue = e.duration;
delayIndex = index;
}
@@ -234,16 +232,14 @@ export async function applyDelay(eventId) {
// apply delay value to all items until block or end
else {
- if (e.type === 'event') {
+ if (e.type === SupportedEvent.Event) {
// update times
e.timeStart += delayValue;
e.timeEnd += delayValue;
// increment revision
e.revision += 1;
- } else if (e.type === 'block') {
- // save id and stop
- blockIndex = index;
+ } else if (e.type === SupportedEvent.Block) {
break;
}
}
@@ -256,10 +252,6 @@ export async function applyDelay(eventId) {
// delete delay
rundown.splice(delayIndex, 1);
- // delete block
- // index would have moved down since we deleted delay
- if (blockIndex) rundown.splice(blockIndex - 1, 1);
-
// update rundown
await DataProvider.setRundown(rundown);
updateTimer();
diff --git a/apps/server/src/stores/EventStore.ts b/apps/server/src/stores/EventStore.ts
index f6ba159835..c6da23fa42 100644
--- a/apps/server/src/stores/EventStore.ts
+++ b/apps/server/src/stores/EventStore.ts
@@ -1,12 +1,18 @@
import { RuntimeStore } from 'ontime-types';
import { socket } from '../adapters/WebsocketAdapter.js';
+import { eventTimer } from '../services/TimerService.js';
+import { messageService } from '../services/message-service/MessageService.js';
+import { eventLoader } from '../classes/event-loader/EventLoader.js';
-const store: Partial = {};
+let store: Partial = {};
/**
* A runtime store that broadcasts its payload
*/
export const eventStore = {
+ init(payload: RuntimeStore) {
+ store = payload;
+ },
get(key: T) {
return store[key];
},
@@ -23,9 +29,35 @@ export const eventStore = {
return store;
},
broadcast() {
- socket.send({
+ socket.sendAsJson({
type: 'ontime',
payload: store,
});
},
};
+
+/**
+ * Module initialises the services and provides initial payload for the store
+ * Currently registered objects in store
+ * - Timer Service timer
+ * - Timer Service playback
+ * - Message Service timerMessage
+ * - Message Service publicMessage
+ * - Message Service lowerMessage
+ * - Message Service onAir
+ * - Event Loader loaded
+ * - Event Loader titles
+ * - Event Loader titlesPublic
+ */
+
+export const getInitialPayload = () => ({
+ timer: eventTimer.timer,
+ playback: eventTimer.playback,
+ timerMessage: messageService.timerMessage,
+ publicMessage: messageService.publicMessage,
+ lowerMessage: messageService.lowerMessage,
+ onAir: messageService.onAir,
+ loaded: eventLoader.loaded,
+ titles: eventLoader.titles,
+ titlesPublic: eventLoader.titlesPublic,
+});
diff --git a/apps/server/src/utils/__tests__/getRandomName.test.js b/apps/server/src/utils/__tests__/getRandomName.test.js
index 70bf7e5df1..12ec6964c2 100644
--- a/apps/server/src/utils/__tests__/getRandomName.test.js
+++ b/apps/server/src/utils/__tests__/getRandomName.test.js
@@ -1,11 +1,11 @@
import getRandomName from '../getRandomName.js';
-test('generates 100 unique names', () => {
+test('generates unique names', () => {
const names = new Set();
let attempts = 1;
- while (names.size < 100) {
+ while (names.size < 10) {
names.add(getRandomName());
attempts++;
}
- expect(attempts).toBeLessThan(105);
+ expect(attempts).toBeLessThan(50);
});
diff --git a/apps/server/src/utils/parserFunctions.ts b/apps/server/src/utils/parserFunctions.ts
index 1e036cef48..6fe7172090 100644
--- a/apps/server/src/utils/parserFunctions.ts
+++ b/apps/server/src/utils/parserFunctions.ts
@@ -54,7 +54,7 @@ export const parseRundown = (data): OntimeRundown => {
id: e.id || generateId(),
});
} else if (e.type === 'block') {
- rundown.push({ ...blockDef, id: e.id || generateId() });
+ rundown.push({ ...blockDef, title: e.title, id: e.id || generateId() });
} else {
console.log('ERROR: undefined event type, skipping');
}
diff --git a/package.json b/package.json
index 7cafef114d..0cc6a7bc1b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ontime",
- "version": "2.0.0-beta1",
+ "version": "2.0.0-beta2",
"description": "Time keeping for live events",
"keywords": [
"lighdev",
diff --git a/packages/types/src/definitions/core/OntimeEvent.type.ts b/packages/types/src/definitions/core/OntimeEvent.type.ts
index 2d594d438c..8b0342b1a5 100644
--- a/packages/types/src/definitions/core/OntimeEvent.type.ts
+++ b/packages/types/src/definitions/core/OntimeEvent.type.ts
@@ -21,6 +21,7 @@ export type OntimeDelay = OntimeBaseEvent & {
export type OntimeBlock = OntimeBaseEvent & {
type: SupportedEvent.Block;
+ title: string;
};
export type OntimeEvent = OntimeBaseEvent & {