Switch to GNSS immediately if it turns on mid-track
CI / check (push) Successful in 47s
CI / deploy (push) Successful in 18s

If Locate was already tracking via the browser-geolocation fallback
(GNSS was off when clicked) and GNSS then gets turned on via the
sidebar, it kept using the browser position until the button was
clicked again -- the browser's own (often less accurate) position had
no business still being used once the app's own receiver had a fix.

GnssControl#onEnabledChange (a small internal callback list) notifies
LocateControl the moment the sidebar toggle changes; if it's actively
tracking via the browser at that point, it stops that and jumps to
GNSS's position instead.

Caught a real bug while verifying this: the automatic handoff reused
the same jump-to-GNSS helper a deliberate click uses, which alerts if
GNSS has no fix yet -- but GNSS only just turned on and almost
certainly hasn't gotten its first poll response back yet at the exact
moment the handoff fires, so this popped a blocking window.alert()
(which halts the whole page until dismissed) on every single enable.
A test hung on exactly this before I noticed. Fixed with an
alertIfNoFix option, true only for the deliberate-click path.

Verified end to end (simulating the "already tracking" state directly
since headless geolocation isn't reliable enough to trust as the
trigger): toggling GNSS mid-track removes the blue browser marker,
shows the green GNSS one once its poll lands, and no dialog appears.
This commit is contained in:
randy committed 2026-09-08 08:50:43 +02:00
1 parent f0ac8ac5d4
commit d9879bad60
3 files changed
+63 -10

No files matched your search

+11
View File
@@ -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() {
+37 -10
View File
@@ -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");