From e66cfdc043fba12e6e6be515f8075e03ced14272 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Fri, 17 May 2024 12:15:22 -0600 Subject: [PATCH] fix: Adjustments to startup routine to set room key and get room status --- .../runtimeConfig/runtimeConfig.slice.ts | 8 +- .../store/runtimeConfig/runtimeSelectors.ts | 2 +- src/lib/types/classes/room-data.ts | 3 +- src/lib/utils/WebsocketProvider.tsx | 253 ++++++++++-------- 4 files changed, 141 insertions(+), 125 deletions(-) diff --git a/src/lib/store/runtimeConfig/runtimeConfig.slice.ts b/src/lib/store/runtimeConfig/runtimeConfig.slice.ts index eea943a..945ebd5 100644 --- a/src/lib/store/runtimeConfig/runtimeConfig.slice.ts +++ b/src/lib/store/runtimeConfig/runtimeConfig.slice.ts @@ -13,10 +13,10 @@ const initialState: RuntimeConfigState = { pluginVersion: '', disconnectionMessage: '', token: '', + currentRoomKey: '', roomData: { clientId: '', - defaultRoomKey: '', - currentRoomKey: '', + roomKey: '', systemUuid: '', roomUuid: '', userAppUrl: '', @@ -24,7 +24,6 @@ const initialState: RuntimeConfigState = { userCode: '', qrUrl: '', }, - }; @@ -52,7 +51,7 @@ const runtimeConfigSlice = createSlice({ // clear out any existing room/device data store.dispatch(roomsActions.clearRooms()); store.dispatch(devicesActions.clearDevices()); - state.roomData.currentRoomKey = action.payload; + state.currentRoomKey = action.payload; }, setUserCode(state, action: PayloadAction) { state.roomData.userCode = action.payload.userCode; @@ -71,6 +70,7 @@ export interface RuntimeConfigState { disconnectionMessage: string; token: string; roomData: RoomData; + currentRoomKey: string; } export interface UserCode { diff --git a/src/lib/store/runtimeConfig/runtimeSelectors.ts b/src/lib/store/runtimeConfig/runtimeSelectors.ts index 57ede34..381afb2 100644 --- a/src/lib/store/runtimeConfig/runtimeSelectors.ts +++ b/src/lib/store/runtimeConfig/runtimeSelectors.ts @@ -2,6 +2,6 @@ import { useAppSelector } from '../hooks'; export const useWsIsConnected = () => useAppSelector((state) => state.runtimeConfig.websocket.isConnected); -export const useRoomKey = () => useAppSelector((state) => state.runtimeConfig.roomData.currentRoomKey); +export const useRoomKey = () => useAppSelector((state) => state.runtimeConfig.currentRoomKey); export const useClientId = () => useAppSelector((state) => state.runtimeConfig.roomData.clientId); \ No newline at end of file diff --git a/src/lib/types/classes/room-data.ts b/src/lib/types/classes/room-data.ts index f3c5ca3..1c88fda 100644 --- a/src/lib/types/classes/room-data.ts +++ b/src/lib/types/classes/room-data.ts @@ -1,7 +1,6 @@ export interface RoomData { clientId: string | number; - defaultRoomKey: string; - currentRoomKey: string; + roomKey: string; systemUuid: string; roomUuid: string; userAppUrl: string; diff --git a/src/lib/utils/WebsocketProvider.tsx b/src/lib/utils/WebsocketProvider.tsx index e2a4c0d..9028825 100644 --- a/src/lib/utils/WebsocketProvider.tsx +++ b/src/lib/utils/WebsocketProvider.tsx @@ -43,7 +43,9 @@ const WebsocketProvider = ({ children }: { children: ReactNode }) => { * callback: the function to call when the event is received that takes the message as an argument * if additional data is required beyond the eventType */ - const eventHandlers = useRef void>>>({}); + const eventHandlers = useRef< + Record void>> + >({}); /* FUNCTIONS *******************************************************/ @@ -63,8 +65,8 @@ const WebsocketProvider = ({ children }: { children: ReactNode }) => { .catch((err) => { console.log(err); - if(err.repsonse && err.response.status === 498) { - console.error('Invalid token. Unable to join room'); + if (err.repsonse && err.response.status === 498) { + console.error("Invalid token. Unable to join room"); } }); }, @@ -76,6 +78,7 @@ const WebsocketProvider = ({ children }: { children: ReactNode }) => { */ const sendMessage = useCallback( (type: string, content: unknown) => { + if (clientRef.current && isConnected) { clientRef.current.send(JSON.stringify({ type, clientId, content })); } @@ -85,13 +88,15 @@ const WebsocketProvider = ({ children }: { children: ReactNode }) => { /** * Helper function to send a simple message with a boolean, number, or string value - * @param type - * @param value + * @param type + * @param value */ - const sendSimpleMessage = - (type: string, value: boolean | number | string ) => { - sendMessage(type, { value }); - }; + const sendSimpleMessage = ( + type: string, + value: boolean | number | string + ) => { + sendMessage(type, { value }); + }; const addEventHandler = useCallback( (eventType: string, key: string, callback: (data: Message) => void) => { @@ -101,26 +106,21 @@ const WebsocketProvider = ({ children }: { children: ReactNode }) => { eventHandlers.current[eventType][key] = callback; - console.log('event handler added', eventType, key); + console.log("event handler added", eventType, key); }, [] ); - const removeEventHandler = useCallback( - (eventType: string, key: string) => { - if (eventHandlers.current[eventType]) { - delete eventHandlers.current[eventType][key]; - - console.log('event handler removed', eventType, key); - } - }, - [] - ); + const removeEventHandler = useCallback((eventType: string, key: string) => { + if (eventHandlers.current[eventType]) { + delete eventHandlers.current[eventType][key]; + console.log("event handler removed", eventType, key); + } + }, []); //* EFFECTS *********************************************************/ useEffect(() => { - // Get the join token from the url params or from session storage and sets it as a local state variable const qp = new URLSearchParams(window.location.search); @@ -149,126 +149,143 @@ const WebsocketProvider = ({ children }: { children: ReactNode }) => { * Connect to the websocket and get the room data when the apiPath changes */ useEffect(() => { - if (!appConfig.apiPath || waitingToReconnect || !token) return; - - getRoomData(appConfig.apiPath); - - if (!clientRef.current) { - const wsPath = appConfig.apiPath.replace("http", "ws"); - const url = `${wsPath}/ui/join/${token}`; - - const newWs = new WebSocket(url); + async function joinWebsocket() { + if (!appConfig.apiPath || waitingToReconnect || !token) return; - clientRef.current = newWs; - - newWs.onopen = () => { - console.log("connected"); - store.dispatch(runtimeConfigActions.setWebsocketIsConnected(true)); - }; - - newWs.onerror = (err) => { - console.log(err); - }; + await getRoomData(appConfig.apiPath); - newWs.onclose = () => { - console.log("disconnected"); - if (clientRef.current) { - console.log("WebSocket closed by server."); - } else { - console.log("WebSocket closed by client."); - return; - } + if (!clientRef.current) { + const wsPath = appConfig.apiPath.replace("http", "ws"); + const url = `${wsPath}/ui/join/${token}`; - if (waitingToReconnect) { - return; - } + const newWs = new WebSocket(url); - store.dispatch(runtimeConfigActions.setWebsocketIsConnected(false)); + clientRef.current = newWs; - setWaitingToReconnect(true); + newWs.onopen = () => { + console.log("connected"); + store.dispatch(runtimeConfigActions.setWebsocketIsConnected(true)); + }; - setTimeout(() => setWaitingToReconnect(undefined), 5000); - }; + newWs.onerror = (err) => { + console.log(err); + }; + + newWs.onclose = () => { + console.log("disconnected"); + if (clientRef.current) { + console.log("WebSocket closed by server."); + } else { + console.log("WebSocket closed by client."); + return; + } - newWs.onmessage = (e) => { - try { - const message: Message = JSON.parse(e.data); - console.log(message); - - if (message.type.startsWith("/system/")) { - switch (message.type) { - case "/system/roomKey": - store.dispatch( - runtimeConfigActions.setCurrentRoomKey( - message.content as string - ) - ); - break; - case "/system/userCodeChanged": - store.dispatch( - runtimeConfigActions.setUserCode(message.content as UserCode) - ); - break; - case "/system/roomCombinationChanged": - window.location.reload(); - break; - default: - console.log("unhandled system message", message); - break; - } - } else if (message.type.startsWith("/event/")) { - console.log('event message received', message); - // const eventType = (message.content as EventContent).eventType; - // if (!eventType) return; - const handlers = eventHandlers.current[message.type]; - - if(!handlers) { - console.log('no handlers found for event type', message.type); - } + if (waitingToReconnect) { + return; + } - if (handlers) { - Object.values(handlers).forEach((handler) => { - try { - handler(message) - } - catch (err) { - console.error(err); - } - }); + store.dispatch(runtimeConfigActions.setWebsocketIsConnected(false)); + store.dispatch(devicesActions.clearDevices()); + store.dispatch(roomsActions.clearRooms()); + + setWaitingToReconnect(true); + + setTimeout(() => setWaitingToReconnect(undefined), 5000); + }; + + newWs.onmessage = (e) => { + try { + const message: Message = JSON.parse(e.data); + console.log(message); + + if (message.type.startsWith("/system/")) { + switch (message.type) { + case "/system/roomKey": + store.dispatch( + runtimeConfigActions.setCurrentRoomKey( + message.content as string + ) + ); + break; + case "/system/userCodeChanged": + store.dispatch( + runtimeConfigActions.setUserCode( + message.content as UserCode + ) + ); + break; + case "/system/roomCombinationChanged": + // TODO: Revisit if this is the right way to handle combination scenario changes + window.location.reload(); + break; + default: + console.log("unhandled system message", message); + break; + } + } else if (message.type.startsWith("/event/")) { + console.log("event message received", message); + // const eventType = (message.content as EventContent).eventType; + // if (!eventType) return; + const handlers = eventHandlers.current[message.type]; + + if (!handlers) { + console.log("no handlers found for event type", message.type); + } + + if (handlers) { + Object.values(handlers).forEach((handler) => { + try { + handler(message); + } catch (err) { + console.error(err); + } + }); + } + } else if (message.type.startsWith("/room/")) { + store.dispatch(roomsActions.setRoomState(message)); + } else if (message.type.startsWith("/device/")) { + store.dispatch(devicesActions.setDeviceState(message)); } - } else if (message.type.startsWith("/room/")) { - store.dispatch(roomsActions.setRoomState(message)); - } else if (message.type.startsWith("/device/")) { - store.dispatch(devicesActions.setDeviceState(message)); + } catch (err) { + console.log(err); } - } catch (err) { - console.log(err); + }; + } + // Cleanup first websocket in dev mode due to double render cycle + return () => { + if (clientRef.current) { + clientRef.current.close(); } + + clientRef.current = null; }; } - // Cleanup first websocket in dev mode due to double render cycle - return () => { - if (clientRef.current) { - clientRef.current.close(); - } - clientRef.current = null; - }; + joinWebsocket(); }, [appConfig.apiPath, getRoomData, token, waitingToReconnect]); /** * Send a status message to the server to get the current state of the room when the roomKey changes * */ useEffect(() => { - if (roomKey) { - console.log("requesting status from room: ", roomKey); - sendMessage(`/room/${roomKey}/status`, null); - } - }, [roomKey, sendMessage]); + if (!roomKey || !isConnected) return; + console.log("clientId: ", clientId); + if (!clientId) return; + + console.log("requesting status from room: ", roomKey); + sendMessage(`/room/${roomKey}/status`, null); + }, [roomKey, clientId, isConnected, sendMessage]); //* RENDER **********************************************************/ return ( - + {isConnected ? children : } );