diff --git a/src/pages/Device.tsx b/src/pages/Device.tsx index 0a47a05..281842d 100644 --- a/src/pages/Device.tsx +++ b/src/pages/Device.tsx @@ -6,7 +6,7 @@ import { useDeviceAPI, useGlobalState, useSnackbar } from "providers"; import { useEffect, useState } from "react"; import { useLocation, useParams } from "react-router-dom"; import PageContainer from "./PageContainer"; -import { useMobile } from "hooks"; +import { useHTTP, useMobile } from "hooks"; import LoadingScreen from "app/LoadingScreen"; import SmartBreadcrumb from "common/SmartBreadcrumb"; import DeviceActions from "device/DeviceActions"; @@ -21,6 +21,7 @@ import { or } from "utils"; import ComponentDiagnostics from "component/ComponentDiagnostics"; import DeviceWizard from "device/DeviceWizard"; import DeviceScannedComponents from "device/autoDetect/DeviceScannedComponents"; +import { useWebSocket } from "hooks/useWebSocket"; export interface DevicePageData { device: Device; @@ -65,6 +66,8 @@ export default function DevicePage() { // const [devicePageData, setDevicePageData] = useState(pond.GetDevicePageDataResponse.create()) + const { token } = useHTTP(); + const loadDevice = () => { if (loading) return if (state?.devicePageData) { @@ -165,6 +168,41 @@ export default function DevicePage() { .finally(() => setLoading(false)); } + // --- Real-time component updates --- + // Streams all component changes for this device. + // When the backend receives a new reading and updates a component, + // this fires and updates the component in state without a page refresh. + useWebSocket<{ key: string; component: Component }>({ + path: `/v1/live/devices/${deviceID}/components`, + parse: (e) => { + const raw = JSON.parse(e.data); + const comp = Component.any(raw); + return { key: comp.key(), component: comp }; + }, + onMessage: ({ key, component }) => { + // Functional update so we always work with the latest state + setComponents((prev) => { + const updated = new Map(prev); + updated.set(key, component); + return updated; + }); + }, + token, + enabled: !!deviceID, + }); + + // --- Real-time device updates (optional) --- + // Updates device-level info (name, status, lastActive, etc.) + useWebSocket({ + path: `/v1/live/devices/${deviceID}`, + parse: (e) => Device.any(JSON.parse(e.data)), + onMessage: (updatedDevice) => { + setDevice(updatedDevice); + }, + token, + enabled: !!deviceID, + }); + const loadPortScan = () => { deviceAPI.listFoundComponents(parseInt(deviceID)) .then(resp => { diff --git a/src/providers/http.tsx b/src/providers/http.tsx index c4d9f64..3e93f04 100644 --- a/src/providers/http.tsx +++ b/src/providers/http.tsx @@ -19,6 +19,7 @@ interface IHTTPContext { ) => Promise>; del: (url: string, spreadOptions?: AxiosRequestConfig) => Promise>; options: (demo?: boolean) => AxiosRequestConfig; + token: string; } interface Props extends PropsWithChildren{ @@ -104,7 +105,8 @@ export default function HTTPProvider(props: Props) { put, post, del, - options: defaultOptions + options: defaultOptions, + token }}> {/* */}