();
const theme = useTheme();
const [openTransaction, setOpenTransaction] = useState(false);
+ const [{ user }] = useGlobalState();
useEffect(() => {
setFillLevel((grainBag.bushels() / grainBag.capacity()) * 100);
@@ -83,13 +84,13 @@ export default function GrainBagVisualizer(props: Props) {
const grainOverlay = () => {
let displayPending = pendingGrainAmount;
let displayDiff = grainDiff;
- if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && grainBag.bushelsPerTonne() > 1) {
+ if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && grainBag.bushelsPerTonne() > 1) {
if (displayPending && displayDiff) {
displayPending = Math.round((displayPending / grainBag.bushelsPerTonne()) * 100) / 100;
displayDiff = Math.round((displayDiff / grainBag.bushelsPerTonne()) * 100) / 100;
}
}
- if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && grainBag.bushelsPerTonne() > 1) {
+ if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && grainBag.bushelsPerTonne() > 1) {
if (displayPending && displayDiff) {
displayPending = Math.round((displayPending / (grainBag.bushelsPerTonne() * 0.907)) * 100) / 100;
displayDiff = Math.round((displayDiff / (grainBag.bushelsPerTonne()* 0.907)) * 100) / 100;
@@ -148,7 +149,7 @@ export default function GrainBagVisualizer(props: Props) {
};
const grainAmountDisplay = () => {
- if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && grainBag.bushelsPerTonne() > 1) {
+ if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TONNE && grainBag.bushelsPerTonne() > 1) {
return (
@@ -157,7 +158,7 @@ export default function GrainBagVisualizer(props: Props) {
({grainBag.fillPercent()}%)
);
- } else if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && grainBag.bushelsPerTonne() > 1) {
+ } else if (user.grainUnit() === pond.GrainUnit.GRAIN_UNIT_TON && grainBag.bushelsPerTonne() > 1) {
return (
diff --git a/src/hooks/useDeviceStatusStreams.ts b/src/hooks/useDeviceStatusStreams.ts
index 24ca8bb..76d7c68 100644
--- a/src/hooks/useDeviceStatusStreams.ts
+++ b/src/hooks/useDeviceStatusStreams.ts
@@ -1,18 +1,9 @@
import { pond } from "protobuf-ts/pond";
-import { useEffect, useRef, useCallback, useMemo } from "react";
+import { useEffect, useRef, useMemo } from "react";
+import { getWsBaseUrl } from "utils/getWsBaseUrl";
-/**
- * Derives the WebSocket base URL from VITE_APP_API_URL.
- * Matches the logic in useWebSocket.ts.
- */
-function getWsBaseUrl(): string {
- const apiUrl = import.meta.env.VITE_APP_API_URL;
- if (apiUrl) {
- return apiUrl.replace(/^http/, "ws");
- }
- const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
- return `${protocol}//${window.location.host}`;
-}
+/** Off by default to reduce server load; set VITE_ENABLE_WEBSOCKETS=true to enable live streams. */
+const WEBSOCKETS_ENABLED = import.meta.env.VITE_ENABLE_WEBSOCKETS === "true";
export interface UseDeviceStatusStreamsOptions {
/** Device IDs to stream status for (e.g. from the visible devices list) */
@@ -21,6 +12,12 @@ export interface UseDeviceStatusStreamsOptions {
onStatusUpdate: (deviceId: string, status: pond.DeviceStatus) => void;
/** Auth token passed as ?token= query param */
token?: string;
+ /** Same as REST/listDevices (?keys=) — required when listing under a context path or group tab */
+ keys?: string[];
+ /** Same as REST (?types=) */
+ types?: string[];
+ /** View-as / impersonation (?as=), same as device list when not already team-first in URL */
+ as?: string;
/** Whether the connections should be active. Default true. */
enabled?: boolean;
}
@@ -31,7 +28,7 @@ export interface UseDeviceStatusStreamsOptions {
* Readings (plenum, sen5x, co, co2, no2, o2, etc.) stream in real time.
*/
export function useDeviceStatusStreams(options: UseDeviceStatusStreamsOptions) {
- const { deviceIds, onStatusUpdate, token, enabled = true } = options;
+ const { deviceIds, onStatusUpdate, token, keys, types, as, enabled = true } = options;
const onStatusUpdateRef = useRef(onStatusUpdate);
onStatusUpdateRef.current = onStatusUpdate;
@@ -39,25 +36,67 @@ export function useDeviceStatusStreams(options: UseDeviceStatusStreamsOptions) {
const wsMapRef = useRef