V2 ws store (#310)

* Update TimerService.ts

* refactor: message service publishes to store

* refactor: several type improvements

* V2 ws store wss (#309)

* refactor: shared logging types

* refactor: simplify message service consumption

* refactor: create discrete logging system

* refactor: move socket.io > websocket
This commit is contained in:
Carlos Valente
2023-03-15 22:06:47 +01:00
committed by GitHub
parent 11648ee546
commit 76c8f8a4d5
86 changed files with 1876 additions and 1936 deletions
@@ -6,7 +6,6 @@ import {
millisToSeconds,
timeStringToMillis,
} from '../dateConfig';
import { stringFromMillis } from '../time';
describe('test string from formatDisplay function', () => {
it('test with null values', () => {
@@ -58,7 +57,7 @@ describe('test string from formatDisplay function', () => {
describe('test formatDisplay handles partial secs', () => {
it('test with 1795829', () => {
const t = { val: 1795829, result: '00:29:55' };
expect(stringFromMillis(t.val)).toBe(t.result);
expect(formatDisplay(t.val)).toBe(t.result);
});
});
+134 -13
View File
@@ -1,17 +1,138 @@
import { serverURL } from '../api/apiConstants';
import { io } from 'socket.io-client';
import { Log } from 'ontime-types';
const socket = io(serverURL, { transports: ['websocket'] });
const subscriptions = new Set();
import { RUNTIME, websocketUrl } from '../api/apiConstants';
import { ontimeQueryClient } from '../queryClient';
import { addLog } from '../stores/logger';
import { runtime } from '../stores/runtime';
export function subscribeOnce<T>(key: string, callback: (data: T) => void, requestString?: string) {
if (subscriptions.has(key)) {
return;
export let websocket: WebSocket | null = null;
let reconnectTimeout: NodeJS.Timeout | null = null;
const reconnectInterval = 1000;
let shouldReconnect = true;
export const connectSocket = () => {
websocket = new WebSocket(websocketUrl);
websocket.onopen = () => {
clearTimeout(reconnectTimeout as NodeJS.Timeout);
};
websocket.onclose = () => {
console.warn('WebSocket disconnected');
if (shouldReconnect) {
reconnectTimeout = setTimeout(() => {
console.warn('WebSocket: attempting reconnect');
if (websocket && websocket.readyState === WebSocket.CLOSED) {
connectSocket();
}
}, reconnectInterval);
}
};
websocket.onerror = (error) => {
console.error('WebSocket error:', error);
};
websocket.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
const { type, payload } = data;
if (!type) {
return;
}
// TODO: implement partial store updates
switch (type) {
case 'ontime-log': {
addLog(payload as Log);
break;
}
case 'ontime': {
runtime.setState(payload);
if (import.meta.env.DEV) {
ontimeQueryClient.setQueryData(RUNTIME, data.payload);
}
break;
}
case 'ontime-playback': {
const state = runtime.getState();
state.playback = payload;
runtime.setState(state);
break;
}
case 'ontime-timer': {
const state = runtime.getState();
state.timer = payload;
runtime.setState(state);
break;
}
case 'ontime-loaded': {
const state = runtime.getState();
state.loaded = payload;
runtime.setState(state);
break;
}
case 'ontime-titles': {
const state = runtime.getState();
state.titles = payload;
runtime.setState(state);
break;
}
case 'ontime-titlesPublic': {
const state = runtime.getState();
state.titlesPublic = payload;
runtime.setState(state);
break;
}
case 'ontime-timerMessage': {
const state = runtime.getState();
state.timerMessage = payload;
runtime.setState(state);
break;
}
case 'ontime-publicMessage': {
const state = runtime.getState();
state.publicMessage = payload;
runtime.setState(state);
break;
}
case 'ontime-lowerMessage': {
const state = runtime.getState();
state.lowerMessage = payload;
runtime.setState(state);
break;
}
case 'ontime-onAir': {
const state = runtime.getState();
state.onAir = payload;
runtime.setState(state);
break;
}
}
} catch (_) {
// ignore unhandled
}
};
};
export const disconnectSocket = () => {
shouldReconnect = false;
websocket?.close();
};
export const socketSend = (message: any) => {
if (websocket && websocket.readyState === WebSocket.OPEN) {
websocket.send(message);
}
subscriptions.add(key);
};
requestString ? socket.emit(requestString) : socket.emit(`get-${key}`);
socket.on(key, callback);
}
export default socket;
export const socketSendJson = (type: string, payload?: any) => {
socketSend(
JSON.stringify({
type,
payload,
}),
);
};
-86
View File
@@ -1,86 +0,0 @@
import { DateTime } from 'luxon';
import { APP_SETTINGS } from '../api/apiConstants';
import { ontimeQueryClient } from '../queryClient';
import { mth, mtm, mts } from './timeConstants';
/**
* Returns current time in milliseconds
* @returns {number}
*/
export const nowInMillis = () => {
const now = new Date();
// extract milliseconds since midnight
let elapsed = now.getHours() * 3600000;
elapsed += now.getMinutes() * 60000;
elapsed += now.getSeconds() * 1000;
elapsed += now.getMilliseconds();
return elapsed;
};
/**
* @description Converts milliseconds to string representing time
* @param {number | null} ms - time in milliseconds
* @param {boolean} showSeconds - weather to show the seconds
* @param {string} delim - character between HH MM SS
* @param {string} ifNull - what to return if value is null
* @returns {string} String representing time 00:12:02
*/
export const stringFromMillis = (ms, showSeconds = true, delim = ':', ifNull = '...') => {
if (ms == null || isNaN(ms)) return ifNull;
const isNegative = ms < 0 ? '-' : '';
const millis = Math.abs(ms);
/**
* @description ensures value is double digit
* @param value
* @return {string|*}
*/
const showWith0 = (value) => (value < 10 ? `0${value}` : value);
const hours = showWith0(Math.floor(((millis / mth) % 60) % 24));
const minutes = showWith0(Math.floor((millis / mtm) % 60));
const seconds = showWith0(Math.floor((millis / mts) % 60));
return showSeconds
? `${isNegative}${
parseInt(hours, 10) ? `${hours}${delim}` : `00${delim}`
}${minutes}${delim}${seconds}`
: `${isNegative}${parseInt(hours, 10) ? `${hours}` : '00'}${delim}${minutes}`;
};
/**
* @description Resolves format from url and store
* @return {string|undefined}
*/
export const resolveTimeFormat = () => {
const params = new URL(document.location).searchParams;
const urlOptions = params.get('format');
const settings = ontimeQueryClient.getQueryData(APP_SETTINGS);
return urlOptions || settings?.timeFormat;
};
/**
/**
* @description utility function to format a date in 12 or 24 hour format
* @param {number} milliseconds
* @param {object} [options]
* @param {boolean} [options.showSeconds]
* @param {string} [options.format]
* @param {function} resolver
* @return {string}
*/
export const formatTime = (milliseconds, options, resolver = resolveTimeFormat) => {
if (milliseconds === null) {
return '...';
}
const timeFormat = resolver();
const { showSeconds = false, format: formatString = 'hh:mm a' } = options || {};
return timeFormat === '12'
? DateTime.fromMillis(milliseconds).toUTC().toFormat(formatString)
: stringFromMillis(milliseconds, showSeconds);
};
+60
View File
@@ -0,0 +1,60 @@
import { DateTime } from 'luxon';
import { Settings } from 'ontime-types';
import { millisToString } from 'ontime-utils';
import { APP_SETTINGS } from '../api/apiConstants';
import { ontimeQueryClient } from '../queryClient';
/**
* Returns current time in milliseconds
* @returns {number}
*/
export const nowInMillis = () => {
const now = new Date();
// extract milliseconds since midnight
let elapsed = now.getHours() * 3600000;
elapsed += now.getMinutes() * 60000;
elapsed += now.getSeconds() * 1000;
elapsed += now.getMilliseconds();
return elapsed;
};
/**
* @description Resolves format from url and store
* @return {string|undefined}
*/
export const resolveTimeFormat = () => {
const params = new URL(document.location.href).searchParams;
const urlOptions = params.get('format');
const settings: Settings | undefined = ontimeQueryClient.getQueryData(APP_SETTINGS);
return urlOptions || settings?.timeFormat;
};
type FormatOptions = {
showSeconds?: boolean;
format?: string;
};
/**
/**
* @description utility function to format a date in 12 or 24 hour format
* @param {number | null} milliseconds
* @param {object} [options]
* @param {boolean} [options.showSeconds]
* @param {string} [options.format]
* @param {function} resolver
* @return {string}
*/
export const formatTime = (milliseconds: number | null, options: FormatOptions, resolver = resolveTimeFormat) => {
if (milliseconds === null) {
return '...';
}
const timeFormat = resolver();
const { showSeconds = false, format: formatString = 'hh:mm a' } = options || {};
return timeFormat === '12'
? DateTime.fromMillis(milliseconds).toUTC().toFormat(formatString)
: millisToString(milliseconds, showSeconds);
};