diff --git a/docs/architecture.adoc b/docs/architecture.adoc index d636e3d..f591418 100644 --- a/docs/architecture.adoc +++ b/docs/architecture.adoc @@ -261,6 +261,21 @@ as "the GNSS simulator, leaking somehow"). Uses MapLibre's own blue pulsing `.maplibregl-user-location-dot` (`maplibre-gl.css`) instead — the same look GeolocateControl itself had here before, and visually distinct from GNSS's green on purpose. ++ +GNSS turning on *while* the browser fallback is already mid-track +switches over immediately rather than waiting for another click — +`GnssControl#onEnabledChange` (a small internal callback list, not a +full pub/sub system — nothing else needs one) notifies `LocateControl` +the moment the sidebar toggle changes; if it was actively tracking via +the browser at that point, it stops that and jumps to GNSS's position +instead. That jump doesn't alert if GNSS has no fix yet (a +`{ alertIfNoFix }` option on the shared jump-to-GNSS helper, `true` +only for a deliberate click) — `GnssControl` only just turned on and +almost certainly hasn't gotten its first poll response back yet at +the exact moment this fires, so alerting here would pop a blocking +dialog (window.alert() halts the whole page until dismissed) on every +single enable; its own next poll shows/recenters once it actually has +something, same as it would either way. === Debug inspector (Alt+click) diff --git a/public/js/gnss.js b/public/js/gnss.js index 186fd14..ce1aa65 100644 --- a/public/js/gnss.js +++ b/public/js/gnss.js @@ -15,6 +15,16 @@ export class GnssControl { this._marker = null; this._timer = null; this._enabled = false; + this._enabledListeners = []; + } + + // Read by LocateControl: if it's mid-tracking on the browser-geolocation + // fallback when GNSS turns on, GNSS should take over there and then, not + // only the next time the button happens to get clicked — the browser's + // own (often less accurate) position has no business still being used + // once the app's own receiver has a fix. + onEnabledChange(callback) { + this._enabledListeners.push(callback); } mount(sidebar) { @@ -46,6 +56,7 @@ export class GnssControl { this._removeMarker(); this._status.textContent = ""; } + this._enabledListeners.forEach((callback) => callback(enabled)); } _stopPolling() { diff --git a/public/js/locate.js b/public/js/locate.js index 87fd68e..5927746 100644 --- a/public/js/locate.js +++ b/public/js/locate.js @@ -37,6 +37,25 @@ export class LocateControl { this._container = document.createElement("div"); this._container.className = "maplibregl-ctrl maplibregl-ctrl-group"; this._container.appendChild(this._button); + + // GNSS turning on mid-track (this control's own browser-geolocation + // fallback, not GnssControl's — that one manages its own tracking) + // should switch over immediately, not just the next time this button + // gets clicked; the browser's own position has no business still + // being used once the app's own receiver has a fix. No alertIfNoFix + // here (unlike a deliberate click, below) — GnssControl only just + // turned on and almost certainly hasn't gotten its first fix back yet + // (its own poll is async, still in flight at the moment this fires), + // so this would otherwise pop a blocking "waiting for a fix" dialog on + // every single enable; GnssControl's own next poll will show/recenter + // once it actually has something, same as it would either way. + this._gnssControl.onEnabledChange((enabled) => { + if (enabled && this._active) { + this._stop(); + this._jumpToGnss(); + } + }); + return this._container; } @@ -51,16 +70,7 @@ export class LocateControl { _click() { if (this._gnssControl.isEnabled()) { - // GNSS already follows the map on its own once enabled (GnssControl) - // and has no separate "off" state to toggle from here — just jump to - // its current fix now, for the same instant feedback clicking this - // button has always given. - const position = this._gnssControl.getPosition(); - if (position) { - this._map.flyTo({ center: position, zoom: Math.max(this._map.getZoom(), 15) }); - } else { - window.alert("Waiting for a GNSS fix…"); - } + this._jumpToGnss({ alertIfNoFix: true }); return; } @@ -71,6 +81,23 @@ export class LocateControl { } } + // GNSS already follows the map on its own once enabled (GnssControl) and + // has no separate "off" state to toggle from here — just jump to its + // current fix, for the same instant feedback clicking this button has + // always given. alertIfNoFix is opt-in (a deliberate click wants to know + // why nothing happened; the automatic mid-track handoff above doesn't — + // see its own comment) since a JS alert() blocks the whole page until + // dismissed, not something to pop up on a path that isn't a direct user + // action. + _jumpToGnss({ alertIfNoFix = false } = {}) { + const position = this._gnssControl.getPosition(); + if (position) { + this._map.flyTo({ center: position, zoom: Math.max(this._map.getZoom(), 15) }); + } else if (alertIfNoFix) { + window.alert("Waiting for a GNSS fix…"); + } + } + _start() { this._active = true; this._button.classList.add("maplibregl-ctrl-geolocate-active");