Fix OSM data refreshes not immediately rendering
CI / Test (pull_request) Failing after 8s
CI / Lint (pull_request) Failing after 12s

This commit is contained in:
2026-08-19 11:01:42 -06:00
parent 61242420d1
commit 1de276fcc8
5 changed files with 121 additions and 10 deletions
+39 -4
View File
@@ -1,6 +1,23 @@
import Route from '@ember/routing/route';
import { service } from '@ember/service';
// Detects whether the fresh OSM data differs from the currently-shown model
// by more than ~1m of coordinate drift, or in any tag. Mirrors the relevant
// subset of `storage.refreshPlace`'s diff for the non-bookmark path.
function hasOsmChanges(place, fresh) {
const latDiff = Math.abs((place.lat ?? 0) - (fresh.lat ?? 0));
const lonDiff = Math.abs((place.lon ?? 0) - (fresh.lon ?? 0));
if (latDiff > 0.00001 || lonDiff > 0.00001) return true;
const oldTags = place.osmTags || {};
const newTags = fresh.osmTags || {};
const allKeys = new Set([...Object.keys(oldTags), ...Object.keys(newTags)]);
for (const key of allKeys) {
if (oldTags[key] !== newTags[key]) return true;
}
return false;
}
export default class PlaceRoute extends Route {
@service storage;
@service osm;
@@ -131,14 +148,32 @@ export default class PlaceRoute extends Route {
}
async checkUpdates(place) {
// Only check for updates if it's a saved place (has ID) and is an OSM object
if (place && place.id && place.osmId && place.osmType) {
if (!place || !place.osmId || !place.osmType) return;
// Bookmarked place — refresh via storage, which persists the update.
if (place.id) {
const updatedPlace = await this.storage.refreshPlace(place);
if (updatedPlace) {
// If an update occurred, refresh the map UI selection without moving the camera
// This ensures the sidebar shows the new data
// If an update occurred, refresh the map UI selection without moving
// the camera. This ensures the sidebar shows the new data.
this.mapUi.selectPlace(updatedPlace, { preventZoom: true });
}
return;
}
// Non-bookmarked explicit OSM place — fetch fresh and update the model
// in-place if anything changed, so the sidebar reflects the latest OSM
// data without requiring a re-open.
try {
const fresh = await this.osm.fetchOsmObject(place.osmId, place.osmType, {
forceFresh: true,
});
if (fresh && hasOsmChanges(place, fresh)) {
Object.assign(place, fresh);
this.mapUi.selectPlace(place, { preventZoom: true });
}
} catch (e) {
console.debug('[place] Fresh fetch failed for', place.osmId, e);
}
}
+12 -4
View File
@@ -328,19 +328,27 @@ out center;
return this.normalizePoi(data.elements[0]);
}
async fetchOsmObject(osmId, osmType) {
async fetchOsmObject(osmId, osmType, { forceFresh = false } = {}) {
if (!osmId || !osmType) return null;
const cacheKey = this._buildCacheKey(osmType, osmId);
// Force a fresh fetch from the API, bypassing the cache. Used by callers
// that need genuinely current data (e.g. storage.refreshPlace, which
// diffs the bookmark against freshly-fetched OSM data).
if (forceFresh) {
return this._fetchAndCacheOsmObject(osmId, osmType, cacheKey);
}
// Cached-first path: return the in-memory entry if it's still warm.
const cached = this.cachedPlaces.get(cacheKey);
if (cached && Date.now() - cached.timestamp < OsmService.IN_MEMORY_TTL_MS) {
console.debug(`Using in-memory cached OSM object for ${cacheKey}`);
return cached.data;
}
// If we have a persistent (IndexedDB) cache entry, return it immediately and
// kick off a background refresh. This keeps the UI snappy while still ensuring
// the cache is updated with the latest OSM data.
// Otherwise return the persistent IndexedDB entry and refresh in the
// background so the next visit is fresh.
const localCached = await this._readLocalCache(osmType, osmId);
if (localCached) {
console.debug(`Using IndexedDB cached OSM object for ${cacheKey}`);
+2 -1
View File
@@ -426,7 +426,8 @@ export default class StorageService extends Service {
console.debug(`Checking for updates for ${place.title} (${place.osmId})`);
const freshData = await this.osm.fetchOsmObject(
place.osmId,
place.osmType
place.osmType,
{ forceFresh: true }
);
if (!freshData) {