mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-13 03:13:47 +00:00
feat: timer preview
This commit is contained in:
committed by
Carlos Valente
parent
fa709dc9be
commit
4d8bea2940
@@ -1,68 +1,63 @@
|
||||
import { DeepPartial, Message, TimerMessage, MessageState } from 'ontime-types';
|
||||
import { TimerMessage, MessageState } from 'ontime-types';
|
||||
import { DeepPartial } from 'ts-essentials';
|
||||
|
||||
import { throttle } from '../../utils/throttle.js';
|
||||
|
||||
import type { PublishFn } from '../../stores/EventStore.js';
|
||||
|
||||
let instance: MessageService | null = null;
|
||||
const defaultTimer: TimerMessage = {
|
||||
text: '',
|
||||
visible: false,
|
||||
blink: false,
|
||||
blackout: false,
|
||||
secondarySource: null,
|
||||
};
|
||||
|
||||
class MessageService {
|
||||
timer: TimerMessage;
|
||||
external: Message;
|
||||
let timer = { ...defaultTimer };
|
||||
let external = '';
|
||||
|
||||
private throttledSet: PublishFn;
|
||||
private publish: PublishFn | null;
|
||||
let throttledSet: PublishFn | null = null;
|
||||
|
||||
constructor() {
|
||||
if (instance) {
|
||||
throw new Error('There can be only one');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias -- this logic is used to ensure singleton
|
||||
instance = this;
|
||||
|
||||
this.throttledSet = () => {
|
||||
throw new Error('Published called before initialisation');
|
||||
};
|
||||
|
||||
this.clear();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.timer = {
|
||||
text: '',
|
||||
visible: false,
|
||||
blink: false,
|
||||
blackout: false,
|
||||
};
|
||||
|
||||
this.external = {
|
||||
text: '',
|
||||
visible: false,
|
||||
};
|
||||
}
|
||||
|
||||
init(publish: PublishFn) {
|
||||
this.publish = publish;
|
||||
this.throttledSet = throttle((key, value) => this.publish?.(key, value), 100);
|
||||
}
|
||||
|
||||
getState(): MessageState {
|
||||
return {
|
||||
timer: this.timer,
|
||||
external: this.external,
|
||||
};
|
||||
}
|
||||
|
||||
patch(message: DeepPartial<MessageState>) {
|
||||
if (message.timer) this.timer = { ...this.timer, ...message.timer };
|
||||
if (message.external) this.external = { ...this.external, ...message.external };
|
||||
|
||||
const newState = this.getState();
|
||||
|
||||
this.throttledSet('message', newState);
|
||||
return newState;
|
||||
}
|
||||
/**
|
||||
* Initialises the message service with a publish function
|
||||
* @param publishFn
|
||||
*/
|
||||
export function init(publishFn: PublishFn) {
|
||||
throttledSet = throttle(publishFn, 100);
|
||||
}
|
||||
|
||||
export const messageService = new MessageService();
|
||||
/**
|
||||
* Exposes function to reset the internal state
|
||||
*/
|
||||
export function clear() {
|
||||
timer = { ...defaultTimer };
|
||||
external = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Exposes the internal state of the message service
|
||||
*/
|
||||
export function getState(): MessageState {
|
||||
return {
|
||||
external,
|
||||
timer,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function allows patching internal object
|
||||
*/
|
||||
export function patch(patch: DeepPartial<MessageState>): MessageState {
|
||||
// we cannot call patch before init
|
||||
// eslint-disable-next-line no-unused-labels -- dev code path
|
||||
DEV: {
|
||||
if (throttledSet === null) {
|
||||
throw new Error('MessageService.patch() called before init()');
|
||||
}
|
||||
}
|
||||
|
||||
if ('timer' in patch) timer = { ...timer, ...patch.timer };
|
||||
if ('external' in patch && patch.external !== undefined) external = patch.external;
|
||||
const newState = getState();
|
||||
throttledSet?.('message', newState);
|
||||
return newState;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { messageService } from '../MessageService.js';
|
||||
import * as messageService from '../MessageService.js';
|
||||
|
||||
describe('MessageService', () => {
|
||||
const publishFunction = () => {};
|
||||
@@ -14,17 +14,14 @@ describe('MessageService', () => {
|
||||
it('should patch the message state', () => {
|
||||
const message = {
|
||||
timer: { text: 'new text', visible: true },
|
||||
external: { visible: true },
|
||||
external: 'external',
|
||||
};
|
||||
|
||||
const newState = messageService.patch(message);
|
||||
|
||||
expect(newState).toEqual({
|
||||
timer: { text: 'new text', visible: true, blackout: false, blink: false },
|
||||
external: {
|
||||
text: '',
|
||||
visible: true,
|
||||
},
|
||||
timer: { text: 'new text', visible: true, blackout: false, blink: false, secondarySource: null },
|
||||
external: 'external',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,11 +33,8 @@ describe('MessageService', () => {
|
||||
const newState = messageService.patch(initialMessage);
|
||||
|
||||
expect(newState).toEqual({
|
||||
timer: { text: 'initial text', visible: true, blackout: false, blink: false },
|
||||
external: {
|
||||
text: '',
|
||||
visible: false,
|
||||
},
|
||||
timer: { text: 'initial text', visible: true, blackout: false, blink: false, secondarySource: null },
|
||||
external: '',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,26 +2,7 @@ import { validateMessage, validateTimerMessage } from '../messageUtils.js';
|
||||
|
||||
describe('validateMessage()', () => {
|
||||
it('returns a valid Message object', () => {
|
||||
const payload = {
|
||||
text: '12312',
|
||||
visible: 'true',
|
||||
};
|
||||
const expected = {
|
||||
text: '12312',
|
||||
visible: true,
|
||||
};
|
||||
|
||||
expect(validateMessage(payload)).toEqual(expected);
|
||||
});
|
||||
it('skips keys not given', () => {
|
||||
const payload = {
|
||||
visible: 'true',
|
||||
};
|
||||
const expected = {
|
||||
visible: true,
|
||||
};
|
||||
|
||||
expect(validateMessage(payload)).toStrictEqual(expected);
|
||||
expect(validateMessage('test')).toEqual('test');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Message, TimerMessage } from 'ontime-types';
|
||||
import { TimerMessage } from 'ontime-types';
|
||||
|
||||
import * as assert from '../../utils/assert.js';
|
||||
import { coerceBoolean, coerceString } from '../../utils/coerceType.js';
|
||||
@@ -7,14 +7,8 @@ import { coerceBoolean, coerceString } from '../../utils/coerceType.js';
|
||||
* Creates a valid Message object from a payload
|
||||
* @throws if the payload is not an object
|
||||
*/
|
||||
export function validateMessage(message: unknown): Partial<Message> {
|
||||
assert.isObject(message);
|
||||
|
||||
const result: Partial<Message> = {};
|
||||
if ('text' in message) result.text = coerceString(message.text);
|
||||
if ('visible' in message) result.visible = coerceBoolean(message.visible);
|
||||
|
||||
return result;
|
||||
export function validateMessage(message: unknown): string {
|
||||
return decodeURI(coerceString(message));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -26,10 +20,28 @@ export function validateTimerMessage(message: unknown): Partial<TimerMessage> {
|
||||
|
||||
const result: Partial<TimerMessage> = {};
|
||||
|
||||
if ('text' in message) result.text = coerceString(message.text);
|
||||
if ('text' in message) result.text = decodeURI(coerceString(message.text));
|
||||
if ('visible' in message) result.visible = coerceBoolean(message.visible);
|
||||
if ('blink' in message) result.blink = coerceBoolean(message.blink);
|
||||
if ('blackout' in message) result.blackout = coerceBoolean(message.blackout);
|
||||
if ('secondarySource' in message) result.secondarySource = coerceSecondary(message.secondarySource);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the secondary value is one of the permitted values
|
||||
*/
|
||||
function assertSecondary(source: unknown): source is TimerMessage['secondarySource'] {
|
||||
return source === 'aux' || source === 'external' || source === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the secondary value is one of the permitted values
|
||||
*/
|
||||
function coerceSecondary(source: unknown): TimerMessage['secondarySource'] {
|
||||
if (!assertSecondary(source)) {
|
||||
return null;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user