Let agents temporarily override native map layer visibility.

This commit is contained in:
Ethan Morchy
2026-08-08 02:04:20 -07:00
parent 8676a98945
commit 3ef45508b2
4 changed files with 168 additions and 8 deletions
+30 -3
View File
@@ -191,6 +191,9 @@ export default function Dashboard() {
}, []);
const [activeLayers, setActiveLayers] = useState<ActiveLayers>(getDefaultActiveLayers);
// Backend-driven layer overrides. Additive on top of activeLayers, never
// persisted and never pushed back — the operator's own toggles stay theirs.
const [layerOverrides, setLayerOverrides] = useState<Partial<ActiveLayers>>({});
const [activeStyle, setActiveStyle] = useState<MapStyle>(getDefaultMapStyle);
const [activeFilters, setActiveFilters] = useState<Record<string, string[]>>({});
const [layerPrefsHydrated, setLayerPrefsHydrated] = useState(false);
@@ -323,6 +326,27 @@ export default function Dashboard() {
};
}, [activeLayers, secondaryBootReady, layerPrefsHydrated]);
// Poll for backend layer overrides so an agent can switch an overlay on
// without the operator reloading. Overrides carry a TTL server-side, so a
// missed poll self-corrects and a dropped backend just lets them lapse.
useEffect(() => {
if (!secondaryBootReady) return;
let cancelled = false;
const pollOverrides = () =>
fetch(`${API_BASE}/api/layers`)
.then((r) => r.json())
.then((d) => {
if (!cancelled) setLayerOverrides(d?.overrides ?? {});
})
.catch(() => {});
void pollOverrides();
const id = setInterval(pollOverrides, 5000);
return () => {
cancelled = true;
clearInterval(id);
};
}, [secondaryBootReady]);
// Left panel accordion state
const [leftDataMinimized, setLeftDataMinimized] = useState(false);
const [leftMeshExpanded, setLeftMeshExpanded] = useState(true);
@@ -457,8 +481,10 @@ export default function Dashboard() {
});
};
// Overrides merge last so they win over both the operator's toggles and the
// first-paint suppressions below.
const firstPaintActiveLayers = useMemo<ActiveLayers>(() => {
if (secondaryBootReady) return activeLayers;
if (secondaryBootReady) return { ...activeLayers, ...layerOverrides };
return {
...activeLayers,
cctv: false,
@@ -472,8 +498,9 @@ export default function Dashboard() {
tinygs: false,
datacenters: false,
power_plants: false,
...layerOverrides,
};
}, [activeLayers, secondaryBootReady]);
}, [activeLayers, layerOverrides, secondaryBootReady]);
// Agent fly_to handler (sar_focus_aoi etc.) — wired here now that
// setFlyToLocation is in scope. show_image is routed through
// useAgentActions at the top of Dashboard.
@@ -587,7 +614,7 @@ export default function Dashboard() {
{secondaryBootReady ? (
<ErrorBoundary name="WorldviewLeftPanel">
<WorldviewLeftPanel
activeLayers={activeLayers}
activeLayers={firstPaintActiveLayers}
setActiveLayers={setActiveLayers}
onResetLayers={resetActiveLayers}
shodanResultCount={shodanResults.length}