mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-11 17:19:34 +00:00
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:
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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);
|
||||
};
|
||||
Reference in New Issue
Block a user