fix(phase26): debounce trip clicks, guard against stale renders

This commit is contained in:
Mathieu Moran
2026-08-11 05:07:35 -04:00
parent cdb3fc19c7
commit 90edc73735
+43 -47
View File
@@ -28,6 +28,7 @@
let replayPoints = [];
let replayTimer = null;
let replayController = null;
let _replayDebounce = null;
const recenterBtn = document.getElementById("btn-recenter-history");
const replayLocation = document.getElementById("trip-replay-location");
@@ -193,48 +194,48 @@
replayController.abort();
}
replayController = new AbortController();
const response = await fetch(
`/api/trip/${tripId}`,
{ signal: replayController.signal }
);
const result = await response.json();
if (!response.ok) {
throw new Error(result.error || "Could not load trip route.");
}
const signal = replayController.signal;
try {
const response = await fetch(`/api/trip/${tripId}`, { signal });
const result = await response.json();
if (!response.ok) throw new Error(result.error || "Could not load trip route.");
if (signal.aborted) return;
if (routeLayer) {
routeLayer.remove();
}
const coordinates = result.points.map(function (point) {
return [point.latitude, point.longitude];
});
routeLayer = L.featureGroup();
L.polyline(coordinates, { color: "#4e73df", weight: 5 }).addTo(routeLayer);
if (coordinates.length) {
L.marker(coordinates[0]).bindPopup("Trip start").addTo(routeLayer);
L.marker(coordinates[coordinates.length - 1]).bindPopup("Trip end").addTo(routeLayer);
}
routeLayer.addTo(map);
if (coordinates.length > 1) {
map.fitBounds(routeLayer.getBounds(), { padding: [24, 24] });
} else if (coordinates.length === 1) {
map.setView(coordinates[0], 15);
}
recenterBtn.hidden = false;
recenterBtn.onclick = function () {
if (routeLayer) routeLayer.remove();
const coordinates = result.points.map(p => [p.latitude, p.longitude]);
routeLayer = L.featureGroup();
L.polyline(coordinates, { color: "#4e73df", weight: 5 }).addTo(routeLayer);
if (coordinates.length) {
L.marker(coordinates[0]).bindPopup("Trip start").addTo(routeLayer);
L.marker(coordinates[coordinates.length - 1]).bindPopup("Trip end").addTo(routeLayer);
}
routeLayer.addTo(map);
if (signal.aborted) { routeLayer.remove(); return; }
if (coordinates.length > 1) {
map.fitBounds(routeLayer.getBounds(), { padding: [24, 24] });
} else if (coordinates.length === 1) {
map.setView(coordinates[0], 15);
}
};
summary.textContent = `${formatDistance(result.trip.distance_km)} · `
+ `${formatDuration(result.trip.duration_seconds)} · `
+ `avg ${formatSpeed(result.trip.average_speed_kmh)} · `
+ `max ${formatSpeed(result.trip.max_speed_kmh)} · `
+ `${result.points.length} route points`;
preloadGeocodes(result.points);
initializeTimeline(result.points);
if (signal.aborted) { routeLayer.remove(); return; }
recenterBtn.hidden = false;
recenterBtn.onclick = function () {
if (coordinates.length > 1) {
map.fitBounds(routeLayer.getBounds(), { padding: [24, 24] });
} else if (coordinates.length === 1) {
map.setView(coordinates[0], 15);
}
};
summary.textContent = `${formatDistance(result.trip.distance_km)} · `
+ `${formatDuration(result.trip.duration_seconds)} · `
+ `avg ${formatSpeed(result.trip.average_speed_kmh)} · `
+ `max ${formatSpeed(result.trip.max_speed_kmh)} · `
+ `${result.points.length} route points`;
if (signal.aborted) { routeLayer.remove(); return; }
preloadGeocodes(result.points);
initializeTimeline(result.points);
} catch (e) {
if (e.name !== "AbortError") showError(e.message);
}
}
const tripList = document.getElementById("trip-list");
@@ -351,18 +352,13 @@
}
}
// Replay button click (desktop) or row click (mobile)
const button = event.target.closest("[data-trip-id]");
const row = event.target.closest("tr[data-trip-id]");
// Replay button click (desktop) or row click (mobile) — debounced
const tripId = button ? button.dataset.tripId : (row ? row.dataset.tripId : null);
if (!tripId) {
return;
}
replayTrip(tripId).catch(function (replayError) {
if (replayError.name !== "AbortError") {
showError(replayError.message);
}
});
if (!tripId) return;
clearTimeout(_replayDebounce);
_replayDebounce = setTimeout(() => {
replayTrip(tripId);
}, 100);
});
scrubber.addEventListener("input", function () {