refactor(street-level): no sequence image cache; one pose listener

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-08 15:16:29 +04:00
1 parent 1db3290e0b
commit 09e2fd96a4
4 files changed
+11 -51

No files matched your search

@@ -11,9 +11,6 @@ import {
const SPRITE_ID = 'street-level:mapillary-cones';
/** How many recently viewed sequences keep their image list in memory. */
const SEQUENCE_CACHE_SIZE = 40;
/** Normalize a graph image record into the shape the cones use. */
function normalizeSequenceImage(record) {
const coordinates = record?.geometry?.coordinates;
@@ -115,14 +112,6 @@ export function createSequences({ state, source, parts }) {
requestRender();
}
function remember(sequenceId, images) {
const { cache } = state.sequence;
cache.delete(sequenceId);
cache.set(sequenceId, images);
while (cache.size > SEQUENCE_CACHE_SIZE)
cache.delete(cache.keys().next().value);
}
/** Select a sequence: highlight its line and load its image cones. */
async function select(sequenceId) {
if (!sequenceId || !state.viewer) return;
@@ -135,16 +124,6 @@ export function createSequences({ state, source, parts }) {
state.sequence.abort?.abort();
state.sequence.selectedId = sequenceId;
parts.coverage.highlight(sequenceId);
const cached = state.sequence.cache.get(sequenceId);
if (cached) {
state.sequence.abort = null;
state.sequence.loading = false;
state.sequence.images = cached;
renderCones(cached);
state.sequence.error = null;
notify();
return;
}
// The previous sequence's cones must not stay clickable under the new
// highlight while this one loads, nor after its load fails.
clearCones();
@@ -164,7 +143,6 @@ export function createSequences({ state, source, parts }) {
.filter(Boolean)
.sort((a, b) => a.capturedAt - b.capturedAt),
);
remember(sequenceId, images);
state.sequence.images = images;
renderCones(images);
state.sequence.error = null;
@@ -67,7 +67,6 @@ function setup() {
sequence: {
selectedId: null,
images: [],
cache: new Map(),
collection: cones(),
loading: false,
error: null,
@@ -112,7 +111,7 @@ test('a failed sequence load leaves no stale cones and can be retried', async ()
assert.equal(state.sequence.error, null, 'cleared once B loads');
});
test('a late answer for a superseded sequence neither draws nor is cached', async () => {
test('a late answer for a superseded sequence draws nothing', async () => {
const { state, source, sequences, drawn } = setup();
sequences.select('A');
sequences.select('B');
@@ -123,7 +122,6 @@ test('a late answer for a superseded sequence neither draws nor is cached', asyn
await settle();
assert.deepEqual(drawn(), ['B-0', 'B-1']);
assert.equal(state.sequence.selectedId, 'B');
assert.equal(state.sequence.cache.has('A'), false);
assert.equal(state.sequence.loading, false);
});
@@ -135,21 +133,6 @@ test('a second click on a loading sequence does not start another lookup', async
assert.equal(source.calls[0].signal.aborted, false);
});
test('a cached sequence is re-selected without a lookup', async () => {
const { state, source, sequences, drawn } = setup();
sequences.select('A');
source.last('A').resolve(records('A'));
await settle();
sequences.select('B');
source.last('B').resolve(records('B'));
await settle();
sequences.select('A');
assert.equal(source.calls.length, 2, 'A came from the cache');
assert.deepEqual(drawn(), ['A-0', 'A-1'], 'drawn at once');
assert.equal(state.sequence.loading, false);
assert.equal(state.sequence.selectedId, 'A');
});
test('clearSelection cancels the lookup and drops the highlight', async () => {
const { state, source, sequences, highlights, drawn } = setup();
sequences.select('A');
@@ -27,7 +27,8 @@ export function createMapillaryViewer({ source, render } = {}) {
let renderMode = 'letterbox';
/** Metadata of the image on screen; pov/position events reuse it. */
let current = null;
const listeners = new Set();
/** The pose listener (the viewer host); one at a time. */
let poseListener = null;
function requestRender() {
render?.governorRequestRender?.('mapillary-viewer');
@@ -52,12 +53,10 @@ export function createMapillaryViewer({ source, render } = {}) {
}
function emit(pose) {
for (const listener of [...listeners]) {
try {
listener(pose);
} catch {
/* listener errors are the core's to log */
}
try {
poseListener?.(pose);
} catch {
/* listener errors are the core's to log */
}
}
@@ -201,8 +200,10 @@ export function createMapillaryViewer({ source, render } = {}) {
},
onPose(listener) {
listeners.add(listener);
return () => listeners.delete(listener);
poseListener = listener;
return () => {
if (poseListener === listener) poseListener = null;
};
},
};
}
-2
View File
@@ -71,8 +71,6 @@ export function createState({ services }) {
sequence: {
selectedId: null,
images: [],
/** Recent sequences' thinned images, by sequence id. */
cache: new Map(),
collection: null,
loading: false,
/** Why the selected sequence's images could not load, until it goes. */