From b2369bcc203dd2b29912e05f4dcd78affbe29810 Mon Sep 17 00:00:00 2001 From: Sirius DevOps Date: Thu, 27 Aug 2026 17:38:48 -0400 Subject: [PATCH] map: fix cameras vanishing after opening a camera popup and zooming Root cause: Leaflet 1.9.4's Map.closePopup() never nulls map._popup (only unbindPopup does), so the moveend guard 'if (map._popup) return' skipped every overlay reload forever after the first popup was ever opened. Opening one camera popup froze the marker layer: zoom out and the map kept showing the stale zoomed-in subset (or nothing) until a full page refresh. Track real popup state via popupopen/popupclose events instead, and close the popup on user zoom/drag so the moveend reload always runs after navigation; the autopan skip still protects the popup when it pans itself into view. --- app/static/index.html | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/app/static/index.html b/app/static/index.html index d5944f1..0b91845 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -833,13 +833,22 @@ async function initMap() { if (activeHls) { try { activeHls.destroy(); } catch (_) {} activeHls = null; } }); updateHeatLegend(); - // Reload overlays when the user pans/zooms. Skip when a popup is open: - // opening a camera popup auto-pans the map to fit it, and that moveend - // must NOT rebuild the marker group under the open popup (which would - // close it). Leaflet closes popups on manual drag, so a live popup here - // means the move was autopan — safe to skip. + // Reload overlays when the user pans/zooms. A live popup makes the + // rebuild skip (so the popup's autopan doesn't destroy it), but user + // zoom/drag must close the popup FIRST — otherwise zooming out with a + // camera popup open leaves the stale, zoomed-in marker group on the + // map and the cameras "vanish" until a refresh. + // + // NOTE: `map._popup` is NOT a reliable open-check in Leaflet 1.9.4 — + // Map.closePopup() never nulls it, so once any popup has been opened + // it stays truthy forever. Track the real state via popupopen/close. + let camPopupOpen = false; + map.on('popupopen', () => { camPopupOpen = true; }); + map.on('popupclose', () => { camPopupOpen = false; }); + map.on('zoomstart', () => { if (camPopupOpen) map.closePopup(); }); + map.on('dragstart', () => { if (camPopupOpen) map.closePopup(); }); map.on('moveend', () => { - if (map._popup) return; + if (camPopupOpen) return; // only the popup's own autopan now if (firesOn) loadFires(); if (camsOn) loadCams(); });