fix(street-level): settle loading on pan, keep zoom 0, drop late poses

Fixes from gekh's re-review of 061d269, each with a regression test that
fails on the old code:

- A pan that drops a tile still loading now settles the loading count
  (the aborted request no longer owns its key, so its `finally` never
  did), and purges the previous zoom's tiles once nothing is pending.
  LOADING no longer sticks on the status pill and chip.
- Zoom 0 (above 15,000 km) is a zoom: `zoom == null` instead of `!zoom`.
- A pose that arrives after a photo was closed is dropped, in the
  MapillaryJS adapter and in the viewer host, so the marker and sequence
  selection do not come back.
- Ground casting passes the tile's signal to the terrain request, so a
  cancelled tile stops its height batch instead of holding the queue.
- The server's in-memory tile cache honours the 24 h TTL like the disk.
- The viewer's one-time resize runs for every photo, not just the first.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Said TurkseverandClaude Opus 5.5 committed 2026-10-05 12:44:27 +01:00
1 parent 061d2698f5
commit a5e16e9470
11 files changed
+115 -6

No files matched your search

+21 -2
View File
@@ -27,17 +27,22 @@ function fakeAdapter({ failMounts = 0 } = {}) {
calls.mount++;
if (calls.mount <= failMounts) throw new Error('library failed to load');
},
async open(id) {
calls.open.push(id);
/** A pose event from the library, as MapillaryJS fires them. */
emitPose(id) {
emit?.({
providerId: 'mapillary',
imageId: id,
sequenceId: `seq-${id}`,
position: { lon: 1, lat: 2 },
bearing: 90,
isPano: false,
externalUrl: `https://example.test/${id}`,
});
},
async open(id) {
calls.open.push(id);
this.emitPose(id);
},
close() {},
unmount() {
calls.unmount++;
@@ -127,3 +132,17 @@ test('switching one provider off releases only its viewer', async () => {
host.unmount('mapillary');
assert.equal(adapter.calls.unmount, 1);
});
test('a pose that arrives after the photo was closed does not bring it back', async () => {
const adapter = fakeAdapter();
const { state, host } = harness(adapter);
await host.open('mapillary', 'a');
host.close();
const before = { ...state.street };
// The library's `image` event for a photo that was still loading.
adapter.emitPose('a');
assert.equal(state.street.open, false);
assert.equal(state.street.imageId, null);
assert.equal(state.street.sequenceId, null);
assert.deepEqual(state.street, before);
});