* refactor: cleanup routes

* style: smaller base font

* chore: upgrade dependencies

* chore: lock node version to electron

* refactor: pass HTTP to integration controller (#652)

* refactor: deprecate onair control

* refactor: remove playback router

* Several project files user folder (#617)

* chore: automated screenshots (#667)

* feat: app settings (#658)

* refactor: remove deprecated event data (#674)

* Studio clock (#663)

---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>

* Feat: reorder events with alt+ctrl + arrow up/down (#645)

* Warning and danger per event (#677)

---------

Co-authored-by: Fabian Posenau <fabian@fphome.de>

* refactor: stabilise actionHandler (#683)

Co-authored-by: Fabian Posenau <fabian@fphome.de>

* improvement: hide seconds (#675)

* wip: overview (#688)

* fix: focus cursor (#695)

* refactor: update lower third (#665)

* Refactor/time formatting (#696)

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
Co-authored-by: Carlos Valente <carlosvalente@pm.me>

* feat: multiple selection (#703)

---------

Co-authored-by: asharonbaltazar <asharonbaltazar@outlook.com>
Co-authored-by: Alex <ac@omnivox.dk>

* fix: test - go to `Edit mode` befor tying to click `Event options` button (#708)

* refactor: runtime service (#715)

* fix: issue with loosing cursor position on message (#719)

* remove info panel (#721)

* Event editor continue (#722)

* update API - part  (#709)

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
Co-authored-by: Carlos Valente <carlosvalente@pm.me>

* refactor: update timers (#729)

* feat: many timers (#706)

---------

Co-authored-by: arc-alex <ac@omnivox.dk>

* refactor: excel cleanup (#734)

* refactor: allow import of blocks and skip import (#735)

* Project manager (#697)

* refactor: UI for linking events (#763)

* upgraded pipeline actions (#777)

* Over under (#771)

* custom fields (#744)


---------

Co-authored-by: Carlos Valente <carlosvalente@pm.me>

* Sheets settings (#774)

---------

Co-authored-by: arc-alex <ac@omnivox.dk>

* style: tweaks to lower thirds (#785)

* refactor: delays account for gaps (#784)

* refactor: partial state updates (#780)

* feat: generate crash report (#787)

* Sheet use limited input device auth flow (#782)

---------

Co-authored-by: cv <34649812+cpvalente@users.noreply.github.com>
Co-authored-by: Carlos Valente <carlosvalente@pm.me>

* Custom fields views (#789)

* refactor: deprecate presenter and subtitle (#795)

* refactor: organise API around resources (#798)

---------

Co-authored-by: Bianca Procopio <biancahprocopio@gmail.com>

* Time to end (#804)

* Skip fixes (#805)

* fix: onair derives from playback

* Param nav (#822)

---------

Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk>

* refactor: download files from interface (#831)

* Quick options (#814)

* End pause (#832)

* chore: bump node version in docker (#834)

* refactor: follow in run mode (#840)

* fix: uncaught error in http integration (#837)

* Apply project (#843)

Co-authored-by: Matteo Gheza <matteo.gheza07@gmail.com>
Co-authored-by: Ary <arylmoraesn@gmail.com>
Co-authored-by: Alex Christoffer Rasmussen <ac@omnivox.dk>
Co-authored-by: Fabian Posenau <19673098+kellhogs@users.noreply.github.com>
Co-authored-by: Fabian Posenau <fabian@fphome.de>
Co-authored-by: Alex Rohleder <alexrohleder96@gmail.com>
Co-authored-by: asharonbaltazar <asharonbaltazar@outlook.com>
Co-authored-by: Bianca Procopio <biancahprocopio@gmail.com>
Co-authored-by: Fabian Posenau <fabianpos99+github@gmail.com>
This commit is contained in:
Carlos Valente
2024-04-13 10:06:46 +02:00
committed by GitHub
parent debdbd1c5a
commit 55d1aca9b6
640 changed files with 26702 additions and 21623 deletions
@@ -1,18 +1,16 @@
import { Message } from 'ontime-types';
import { DeepPartial, Message, TimerMessage, MessageState } from 'ontime-types';
import { TimerMessage } from 'ontime-types/src/definitions/runtime/MessageControl.type.js';
import { throttle } from '../../utils/throttle.js';
import type { PublishFn } from '../../stores/EventStore.js';
let instance;
let instance: MessageService | null = null;
class MessageService {
timerMessage: TimerMessage;
publicMessage: Message;
lowerMessage: Message;
externalMessage: Message;
onAir: boolean;
timer: TimerMessage;
public: Message;
lower: Message;
external: Message;
private throttledSet: PublishFn;
private publish: PublishFn | null;
@@ -25,165 +23,62 @@ class MessageService {
// eslint-disable-next-line @typescript-eslint/no-this-alias -- this logic is used to ensure singleton
instance = this;
this.timerMessage = {
text: '',
visible: false,
timerBlink: false,
timerBlackout: false,
};
this.publicMessage = {
text: '',
visible: false,
};
this.lowerMessage = {
text: '',
visible: false,
};
this.externalMessage = {
text: '',
visible: false,
};
this.onAir = false;
this.throttledSet = () => {
throw new Error('Published called before initialisation');
};
this.clear();
}
clear() {
this.timer = {
text: '',
visible: false,
blink: false,
blackout: false,
};
this.public = {
text: '',
visible: false,
};
this.lower = {
text: '',
visible: false,
};
this.external = {
text: '',
visible: false,
};
}
init(publish: PublishFn) {
this.publish = publish;
this.throttledSet = throttle((key, value) => this.publish(key, value), 100);
this.throttledSet = throttle((key, value) => this.publish?.(key, value), 100);
}
/**
* @description sets message on stage timer screen
*/
setExternalText(payload: string) {
if (this.externalMessage.text !== payload) {
this.externalMessage.text = payload;
this.throttledSet('externalMessage', this.externalMessage);
}
return this.getAll();
}
/**
* @description sets message visibility on stage timer screen
*/
setExternalVisibility(status: boolean) {
this.externalMessage.visible = status;
this.throttledSet('externalMessage', this.externalMessage);
return this.getAll();
}
/**
* @description sets message on stage timer screen
*/
setTimerText(payload: string) {
this.timerMessage.text = payload;
this.throttledSet('timerMessage', this.timerMessage);
return this.getAll();
}
/**
* @description sets message visibility on stage timer screen
*/
setTimerVisibility(status: boolean) {
this.timerMessage.visible = status;
this.throttledSet('timerMessage', this.timerMessage);
return this.getAll();
}
/**
* @description sets message on public screen
*/
setPublicText(payload: string) {
this.publicMessage.text = payload;
this.throttledSet('publicMessage', this.publicMessage);
return this.getAll();
}
/**
* @description sets message visibility on public screen
*/
setPublicVisibility(status: boolean) {
this.publicMessage.visible = status;
this.throttledSet('publicMessage', this.publicMessage);
return this.getAll();
}
/**
* @description sets message on lower third screen
*/
setLowerText(payload: string) {
this.lowerMessage.text = payload;
this.throttledSet('lowerMessage', this.lowerMessage);
return this.getAll();
}
/**
* @description sets message visibility on lower third screen
*/
setLowerVisibility(status: boolean) {
this.lowerMessage.visible = status;
this.throttledSet('lowerMessage', this.lowerMessage);
return this.getAll();
}
/**
* @description set state of onAir, toggles if parameters are offered
*/
setOnAir(status?: boolean) {
if (typeof status === 'undefined') {
this.onAir = !this.onAir;
} else {
this.onAir = status;
}
this.throttledSet('onAir', this.onAir);
return this.getAll();
}
/**
* @description set state of timer blink, toggles if parameters are offered
*/
setTimerBlink(status?: boolean) {
if (typeof status === 'undefined') {
this.timerMessage.timerBlink = !this.timerMessage.timerBlink;
} else {
this.timerMessage.timerBlink = status;
}
this.throttledSet('timerMessage', this.timerMessage);
return this.getAll();
}
/**
* @description set state of timer blackout, toggles if parameters are offered
*/
setTimerBlackout(status?: boolean) {
if (typeof status === 'undefined') {
this.timerMessage.timerBlackout = !this.timerMessage.timerBlackout;
} else {
this.timerMessage.timerBlackout = status;
}
this.throttledSet('timerMessage', this.timerMessage);
return this.getAll();
}
/**
* @description Returns feature data
*/
getAll() {
getState(): MessageState {
return {
timerMessage: this.timerMessage,
publicMessage: this.publicMessage,
lowerMessage: this.lowerMessage,
onAir: this.onAir,
timer: this.timer,
public: this.public,
lower: this.lower,
external: this.external,
};
}
patch(message: DeepPartial<MessageState>) {
if (message.timer) this.timer = { ...this.timer, ...message.timer };
if (message.public) this.public = { ...this.public, ...message.public };
if (message.lower) this.lower = { ...this.lower, ...message.lower };
if (message.external) this.external = { ...this.external, ...message.external };
const newState = this.getState();
this.throttledSet('message', newState);
return newState;
}
}
export const messageService = new MessageService();
@@ -0,0 +1,59 @@
import { messageService } from '../MessageService.js';
describe('MessageService', () => {
const publishFunction = () => {};
beforeAll(() => {
messageService.init(publishFunction);
});
beforeEach(() => {
messageService.clear();
});
it('should patch the message state', () => {
const message = {
timer: { text: 'new text', visible: true },
public: { text: 'public text', visible: false },
lower: { text: 'lower text' },
external: { visible: true },
};
const newState = messageService.patch(message);
expect(newState).toEqual({
timer: { text: 'new text', visible: true, blackout: false, blink: false },
public: { text: 'public text', visible: false },
lower: {
text: 'lower text',
visible: false,
},
external: {
text: '',
visible: true,
},
});
});
it('should not affect other properties when patching', () => {
const initialMessage = {
timer: { text: 'initial text', visible: true },
public: { text: 'public text', visible: false },
};
const newState = messageService.patch(initialMessage);
expect(newState).toEqual({
timer: { text: 'initial text', visible: true, blackout: false, blink: false },
public: { text: 'public text', visible: false },
lower: {
text: '',
visible: false,
},
external: {
text: '',
visible: false,
},
});
});
});
@@ -0,0 +1,55 @@
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);
});
});
describe('validateTimerMessage()', () => {
it('returns a valid Timer Message object', () => {
const payload = {
text: '12312',
visible: 'true',
blink: 'true',
blackout: 'true',
};
const expected = {
text: '12312',
visible: true,
blink: true,
blackout: true,
};
expect(validateTimerMessage(payload)).toEqual(expected);
});
it('skips keys not given', () => {
const payload = {
visible: 'true',
};
const expected = {
visible: true,
};
expect(validateTimerMessage(payload)).toStrictEqual(expected);
});
});
@@ -0,0 +1,35 @@
import { Message, TimerMessage } from 'ontime-types';
import * as assert from '../../utils/assert.js';
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;
}
/**
* Creates a valid Timer Message object from a payload
* @throws if the payload is not an object
*/
export function validateTimerMessage(message: unknown): Partial<TimerMessage> {
assert.isObject(message);
const result: Partial<TimerMessage> = {};
if ('text' in message) result.text = 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);
return result;
}