diff --git a/web/package-lock.json b/web/package-lock.json index c8c8fdf29..c3347cdd9 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -20,6 +20,7 @@ "preact-router": "^4.1.0", "react": "npm:@preact/compat@^17.1.2", "react-dom": "npm:@preact/compat@^17.1.2", + "react-use-websocket": "^3.0.0", "strftime": "^0.10.1", "swr": "^1.3.0", "video.js": "^8.5.2", @@ -7721,6 +7722,15 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/react-use-websocket": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-use-websocket/-/react-use-websocket-3.0.0.tgz", + "integrity": "sha512-BInlbhXYrODBPKIplDAmI0J1VPM+1KhCLN09o+dzgQ8qMyrYs4t5kEYmCrTqyRuMTmpahylHFZWQXpfYyDkqOw==", + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -14902,6 +14912,12 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "react-use-websocket": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/react-use-websocket/-/react-use-websocket-3.0.0.tgz", + "integrity": "sha512-BInlbhXYrODBPKIplDAmI0J1VPM+1KhCLN09o+dzgQ8qMyrYs4t5kEYmCrTqyRuMTmpahylHFZWQXpfYyDkqOw==", + "requires": {} + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/web/package.json b/web/package.json index 08d4896bf..8a95584d1 100644 --- a/web/package.json +++ b/web/package.json @@ -24,6 +24,7 @@ "preact-router": "^4.1.0", "react": "npm:@preact/compat@^17.1.2", "react-dom": "npm:@preact/compat@^17.1.2", + "react-use-websocket": "^3.0.0", "strftime": "^0.10.1", "swr": "^1.3.0", "video.js": "^8.5.2", diff --git a/web/src/api/ws.jsx b/web/src/api/ws.jsx index 8324632be..727bb59d0 100644 --- a/web/src/api/ws.jsx +++ b/web/src/api/ws.jsx @@ -1,13 +1,12 @@ -import { h, createContext } from 'preact'; +import { 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 from 'react-use-websocket'; const initialState = Object.freeze({ __connected: false }); export const WS = createContext({ state: initialState, connection: null }); -const defaultCreateWebsocket = (url) => new WebSocket(url); - function reducer(state, { topic, payload, retain }) { switch (topic) { case '__CLIENT_CONNECTED': @@ -33,14 +32,23 @@ 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(); + console.log(dispatch); + + const { sendJsonMessage } = useWebSocket(wsUrl, { + + onMessage: (event) => { + dispatch(event.data); + }, + onOpen: () => dispatch({ topic: '__CLIENT_CONNECTED' }), + shouldReconnect: () => true, + }); useEffect(() => { Object.keys(config.cameras).forEach((camera) => { + console.log("Setting up") const { name, record, detect, snapshots, audio } = config.cameras[camera]; dispatch({ topic: `${name}/recordings/state`, payload: record.enabled ? 'ON' : 'OFF', retain: false }); dispatch({ topic: `${name}/detect/state`, payload: detect.enabled ? 'ON' : 'OFF', retain: false }); @@ -49,46 +57,27 @@ 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 {children}; + return {children}; } export function useWs(watchTopic, publishTopic) { - const { state, ws } = useContext(WS); + const { state, sendJsonMessage } = useContext(WS); + + console.log(state); const value = state[watchTopic] || { payload: null }; const send = useCallback( (payload, retain = false) => { - ws.send( - JSON.stringify({ - topic: publishTopic || watchTopic, - payload: typeof payload !== 'string' ? JSON.stringify(payload) : payload, - retain, - }) - ); + console.log("sending json"); + sendJsonMessage({ + topic: publishTopic || watchTopic, + payload: payload, + retain, + }); }, - [ws, watchTopic, publishTopic] + [sendJsonMessage, watchTopic, publishTopic] ); return { value, send, connected: state.__connected };