mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
fix(street-level): sequence loads, playback and nearest-photo edge cases
From gekh's review of #768, each bug with a test that fails before: - P2: a failed sequence load clears the previous sequence's cones, resets the selection and can be retried; a second click while loading starts no second lookup. sequences.js had no tests: sequences.test.mjs now covers failure and retry, a superseded late answer, the cache and clearSelection. - P2: closing a photo stops MapillaryJS sequence playback, which kept the hidden viewer stepping and downloading images. - openNearest without the panel reports it instead of loading forever, and stops if the layer (or its provider) went off during the lookup; the mesh sampler's misses are bounded and forgotten with its samples; the sequence count is the union of ids across tiles; a provider withdraws only its own error, so a sequence error cannot mask KEY REJECTED; hover stays on (throttled) under an orbiting camera and re-picks when it stops; "since N days" follows the clock in a long-open tab. - A prewarm waits for one in flight and then builds if it gave up. - New coverage for the layer core (credits, KEY REJECTED vs REQUIRED, switching a provider off), the refresh wiring of the tilted-view fix and the draped-to-cast swap completion. - Cleanup: loading is derived from pending; freshStreet(); tile ranking computes distances once and shares one path for the date line; shared metresBetween / whenIdle / redrawTile; one NEAREST_RADIUS_M and one source method list; unused contract fields, exports, getImage and the decoder's image branch removed; PROVIDER_COLORS lists only Mapillary. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
48276450ec
commit
5f98b871e8
31 files changed
+1129
-470
No files matched your search
@@ -126,10 +126,13 @@ test('unmounting while a mount is in flight does not leave it active', async ()
|
||||
const opening = host.open('mapillary', 'a');
|
||||
host.unmount();
|
||||
await opening;
|
||||
assert.equal(host.activeProviderId(), null);
|
||||
assert.equal(adapter.calls.listeners, 0);
|
||||
assert.equal(adapter.calls.unmount, 1);
|
||||
assert.equal(state.street.open, false);
|
||||
// Not left active: the next open mounts again.
|
||||
await host.open('mapillary', 'b');
|
||||
assert.equal(adapter.calls.mount, 2);
|
||||
assert.equal(state.street.imageId, 'b');
|
||||
});
|
||||
|
||||
test('a prewarmed viewer is released when the layer goes off, or at once if it went off mid-load', async () => {
|
||||
@@ -215,12 +218,19 @@ test('switching the layer off and on during a cold first open keeps the shared v
|
||||
/** MapillaryJS, counting live viewers (each holds a WebGL context). */
|
||||
function fakeLibrary() {
|
||||
const gate = deferred();
|
||||
const viewers = { created: 0, live: 0 };
|
||||
const viewers = { created: 0, live: 0, instances: [] };
|
||||
class Viewer {
|
||||
constructor() {
|
||||
viewers.created++;
|
||||
viewers.live++;
|
||||
viewers.instances.push(this);
|
||||
this.removed = false;
|
||||
this.playback = { stops: 0 };
|
||||
}
|
||||
/** The sequence component's play/stop API, as MapillaryJS 4 exposes it. */
|
||||
getComponent(name) {
|
||||
if (name !== 'sequence') return undefined;
|
||||
return { stop: () => this.playback.stops++ };
|
||||
}
|
||||
on() {}
|
||||
async getPosition() {
|
||||
@@ -281,3 +291,30 @@ test('a Mapillary viewer unmounted mid-download is never built; the next mount b
|
||||
adapter.unmount();
|
||||
assert.equal(viewers.live, 0, 'nothing left holding a WebGL context');
|
||||
});
|
||||
|
||||
test('a prewarm that gave up when the layer went off and on mid-download is followed by one that builds', async () => {
|
||||
const { gate, viewers } = fakeLibrary();
|
||||
const adapter = createMapillaryViewer({ source: { token: 't' } });
|
||||
const element = {};
|
||||
const first = adapter.prewarm(element);
|
||||
adapter.unmount(); // layer off while MapillaryJS downloads
|
||||
const second = adapter.prewarm(element); // layer on again
|
||||
gate.resolve();
|
||||
await Promise.all([first, second]);
|
||||
assert.equal(viewers.created, 1, 'the second prewarm built the viewer');
|
||||
assert.equal(viewers.live, 1);
|
||||
adapter.unmount();
|
||||
});
|
||||
|
||||
test('closing the photo stops sequence playback in the hidden viewer (review P2-6)', async () => {
|
||||
const { gate, viewers } = fakeLibrary();
|
||||
gate.resolve();
|
||||
const adapter = createMapillaryViewer({ source: { token: 't' } });
|
||||
const { host } = harness(adapter);
|
||||
await host.open('mapillary', 'img1');
|
||||
const [viewer] = viewers.instances;
|
||||
assert.equal(viewer.playback.stops, 0);
|
||||
host.close();
|
||||
assert.equal(viewer.playback.stops, 1, 'playback stopped with the photo');
|
||||
assert.equal(viewers.live, 1, 'the viewer itself stays warm');
|
||||
});
|
||||
Reference in new issue
Block a user