Websocket changes (#8178)

* use react-use-websockets

* check ready state

* match context shape

* jsonify dispatch

* remove unnecessary ready check

* bring back h

* non-working tests

* skip failing tests

* upgrade some dependencies

---------

Co-authored-by: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com>
This commit is contained in:
Blake Blackshear
2023-10-15 09:14:20 -04:00
committed by GitHub
parent 9ea10f8541
commit e545dfc47b
9 changed files with 906 additions and 581 deletions

View File

@@ -1,12 +1,11 @@
import { h, createContext } from 'preact';
import { baseUrl } from './baseUrl';
import { produce } from 'immer';
import { useCallback, useContext, useEffect, useRef, useReducer } from 'preact/hooks';
import { useCallback, useContext, useEffect, useReducer } from 'preact/hooks';
import useWebSocket, { ReadyState } from 'react-use-websocket';
const initialState = Object.freeze({ __connected: false });
export const WS = createContext({ state: initialState, connection: null });
const defaultCreateWebsocket = (url) => new WebSocket(url);
export const WS = createContext({ state: initialState, readyState: null, sendJsonMessage: () => {} });
function reducer(state, { topic, payload, retain }) {
switch (topic) {
@@ -33,11 +32,18 @@ function reducer(state, { topic, payload, retain }) {
export function WsProvider({
config,
children,
createWebsocket = defaultCreateWebsocket,
wsUrl = `${baseUrl.replace(/^http/, 'ws')}ws`,
}) {
const [state, dispatch] = useReducer(reducer, initialState);
const wsRef = useRef();
const { sendJsonMessage, readyState } = useWebSocket(wsUrl, {
onMessage: (event) => {
dispatch(JSON.parse(event.data));
},
onOpen: () => dispatch({ topic: '__CLIENT_CONNECTED' }),
shouldReconnect: () => true,
});
useEffect(() => {
Object.keys(config.cameras).forEach((camera) => {
@@ -49,46 +55,25 @@ export function WsProvider({
});
}, [config]);
useEffect(
() => {
const ws = createWebsocket(wsUrl);
ws.onopen = () => {
dispatch({ topic: '__CLIENT_CONNECTED' });
};
ws.onmessage = (event) => {
dispatch(JSON.parse(event.data));
};
wsRef.current = ws;
return () => {
ws.close(3000, 'Provider destroyed');
};
},
// Forces reconnecting
[state.__reconnectAttempts, wsUrl] // eslint-disable-line react-hooks/exhaustive-deps
);
return <WS.Provider value={{ state, ws: wsRef.current }}>{children}</WS.Provider>;
return <WS.Provider value={{ state, readyState, sendJsonMessage }}>{children}</WS.Provider>;
}
export function useWs(watchTopic, publishTopic) {
const { state, ws } = useContext(WS);
const { state, readyState, sendJsonMessage } = useContext(WS);
const value = state[watchTopic] || { payload: null };
const send = useCallback(
(payload, retain = false) => {
ws.send(
JSON.stringify({
if (readyState === ReadyState.OPEN) {
sendJsonMessage({
topic: publishTopic || watchTopic,
payload: typeof payload !== 'string' ? JSON.stringify(payload) : payload,
payload,
retain,
})
);
});
}
},
[ws, watchTopic, publishTopic]
[sendJsonMessage, readyState, watchTopic, publishTopic]
);
return { value, send, connected: state.__connected };