diff --git a/apps/client/src/common/stores/runtime.ts b/apps/client/src/common/stores/runtime.ts index 6bbb68c0b..e8a291932 100644 --- a/apps/client/src/common/stores/runtime.ts +++ b/apps/client/src/common/stores/runtime.ts @@ -16,11 +16,17 @@ export const useRuntimeStore = (selector: (state: RuntimeStore) => T) => /** * Allows patching a property of the runtime store - * @param key - * @param value */ -export function patchRuntime(key: K, value: RuntimeStore[K]): void { +export function patchRuntimeProperty(key: K, value: RuntimeStore[K]) { const state = runtimeStore.getState(); state[key] = value; runtimeStore.setState({ ...state }); } + +/** + * Allows patching the entire runtime store + */ +export function patchRuntime(patch: Partial) { + const state = runtimeStore.getState(); + runtimeStore.setState({ ...state, ...patch }); +} diff --git a/apps/client/src/common/utils/socket.ts b/apps/client/src/common/utils/socket.ts index ff0013bd0..6b7f26c3b 100644 --- a/apps/client/src/common/utils/socket.ts +++ b/apps/client/src/common/utils/socket.ts @@ -14,7 +14,7 @@ import { } from '../stores/clientStore'; import { addDialog } from '../stores/dialogStore'; import { addLog } from '../stores/logger'; -import { patchRuntime, runtimeStore } from '../stores/runtime'; +import { patchRuntime, patchRuntimeProperty } from '../stores/runtime'; export let websocket: WebSocket | null = null; let reconnectTimeout: NodeJS.Timeout | null = null; @@ -39,12 +39,14 @@ export const connectSocket = () => { } socketSendJson('set-client-type', 'ontime'); - socketSendJson('set-client-path', location.pathname + location.search); + setOnlineStatus(true); }; websocket.onclose = () => { console.warn('WebSocket disconnected'); + setOnlineStatus(false); + if (shouldReconnect) { reconnectTimeout = setTimeout(() => { console.warn('WebSocket: attempting reconnect'); @@ -73,8 +75,8 @@ export const connectSocket = () => { switch (type) { case 'pong': { const offset = (new Date().getTime() - new Date(payload).getTime()) * 0.5; - patchRuntime('ping', offset); - updateDevTools({ ping: offset }, ['PING']); + patchRuntimeProperty('ping', offset); + updateDevTools({ ping: offset }); break; } case 'client-id': { @@ -131,64 +133,65 @@ export const connectSocket = () => { break; } case 'ontime': { - runtimeStore.setState(payload as RuntimeStore); - if (!isProduction) { - ontimeQueryClient.setQueryData(RUNTIME, data.payload); - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars -- removing the key from the payload + const { ping, ...serverPayload } = payload as Partial; + + patchRuntime(serverPayload); + updateDevTools(serverPayload); break; } case 'ontime-clock': { - patchRuntime('clock', payload); + patchRuntimeProperty('clock', payload); updateDevTools({ clock: payload }); break; } case 'ontime-timer': { - patchRuntime('timer', payload); + patchRuntimeProperty('timer', payload); updateDevTools({ timer: payload }); break; } case 'ontime-onAir': { - patchRuntime('onAir', payload); + patchRuntimeProperty('onAir', payload); updateDevTools({ onAir: payload }); break; } case 'ontime-message': { - patchRuntime('message', payload); + patchRuntimeProperty('message', payload); updateDevTools({ message: payload }); break; } case 'ontime-runtime': { - patchRuntime('runtime', payload); + patchRuntimeProperty('runtime', payload); updateDevTools({ runtime: payload }); break; } case 'ontime-eventNow': { - patchRuntime('eventNow', payload); + patchRuntimeProperty('eventNow', payload); updateDevTools({ eventNow: payload }); break; } case 'ontime-currentBlock': { - patchRuntime('currentBlock', payload); + patchRuntimeProperty('currentBlock', payload); updateDevTools({ currentBlock: payload }); break; } case 'ontime-publicEventNow': { - patchRuntime('publicEventNow', payload); + patchRuntimeProperty('publicEventNow', payload); updateDevTools({ publicEventNow: payload }); break; } case 'ontime-eventNext': { - patchRuntime('eventNext', payload); + patchRuntimeProperty('eventNext', payload); updateDevTools({ eventNext: payload }); break; } case 'ontime-publicEventNext': { - patchRuntime('publicEventNext', payload); + patchRuntimeProperty('publicEventNext', payload); updateDevTools({ publicEventNext: payload }); break; } case 'ontime-auxtimer1': { - patchRuntime('auxtimer1', payload); + patchRuntimeProperty('auxtimer1', payload); updateDevTools({ auxtimer1: payload }); break; } @@ -232,11 +235,23 @@ export const socketSendJson = (type: string, payload?: unknown) => { ); }; -function updateDevTools(newData: Partial, store = RUNTIME) { +function updateDevTools(newData: Partial) { if (!isProduction) { - ontimeQueryClient.setQueryData(store, (oldData: RuntimeStore) => ({ + ontimeQueryClient.setQueryData(RUNTIME, (oldData: RuntimeStore) => ({ ...oldData, ...newData, })); } } + +/** + * Allows setting the status of the client + * We leverage the ping as an indication of the client's online status + * @example ping < 0 - client is offline + * @example ping > 0 -> client is online + */ +function setOnlineStatus(status: boolean) { + const derivedPing = status ? 1 : -1; + patchRuntimeProperty('ping', derivedPing); + updateDevTools({ ping: derivedPing }); +}