fix(street-level): a sequence that fails to load opens the panel too

A Mapillary line click loads the sequence's cones, not a photo: when the
Graph API refused them, the error went to sequence.error, which the new
auto-open did not watch, so with the panel collapsed the click seemed to do
nothing. The panel now opens for an open error or a sequence error alike.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-19 23:41:04 +04:00
1 parent fce328b7cd
commit 78fc5262f4
4 files changed
+35 -13

No files matched your search

+3 -3
View File
@@ -1,5 +1,5 @@
- Street Level panel: SINCE is a dropdown of its nine windows (the tooltip
names the cut-off date), IMAGERY and FIT / FILL are plain toggle buttons,
and the panel opens by itself only when a photo opens or fails to, no
longer when the layer is switched on. FIT / FILL starts at FIT each
session (gekh, #1241).
and the panel opens by itself only when a photo opens or a click fails to
load one, no longer when the layer is switched on. FIT / FILL starts at
FIT each session (gekh, #1241).
+3 -2
View File
@@ -3539,8 +3539,9 @@ image; FIT/FILL starts at FIT each session. IMAGERY and FIT/FILL are rows of
toggle buttons (`aria-pressed`), each its own tab stop. SINCE is a select of
the nine windows; a link's day count between them gets its own option, and
the tooltip names the cut-off date. The panel opens by itself only when a
photo opens or fails to (a click on the map or a line would otherwise load
it, or its error, out of sight), never when the layer is switched on.
photo opens, or a click fails to open one or to load its sequence (the
result, or its error, would otherwise be out of sight), never when the layer
is switched on.
MapillaryJS loads on the first photo. Opening one frames it with a single
camera flight through the application's deferred navigation (`begin` /
+10 -7
View File
@@ -29,7 +29,7 @@ export class StreetLevelControls {
this._state = null;
this._view = null;
this._wasOpen = false;
/** The open error last shown, so the same one never reopens the panel. */
/** The error last shown: the same one never reopens the panel. */
this._streetError = null;
/** The SINCE option for a day count between the stops, while one is set. */
this._customSince = null;
@@ -432,17 +432,20 @@ export class StreetLevelControls {
}
/**
* Open the panel when an image opens, or fails to (a click on the map or a
* line would otherwise load it, or its error, out of sight). Never
* persisted: the stored collapse state stays the user's own choice.
* Open the panel when an image opens, or when a click fails to open one or
* to load its sequence (the result, or its error, would otherwise be out of
* sight). Never persisted: the stored collapse state stays the user's own
* choice.
*/
_reactToTransitions(view) {
// Other size changes reach the viewer through the ResizeObserver; opening
// asks for one resize because the element may not have a size yet.
const open = view.viewer.open;
// A photo that failed to open (a line or ground click, OPEN) says why in
// the panel: it opens for that too, or the click would seem to do nothing.
const error = this._state?.street?.error || null;
// A photo that failed to open (a line or ground click, OPEN), or a
// Mapillary line whose sequence failed to load, says why in the panel:
// it opens for that too, or the click would seem to do nothing.
const error =
this._state?.street?.error || this._state?.sequence?.error || null;
if ((open && !this._wasOpen) || (error && error !== this._streetError))
this.setCollapsed(false, { persist: false });
this._wasOpen = open;
+19 -1
View File
@@ -426,6 +426,7 @@ function uiState({
renderMode = 'letterbox',
keyRequired = false,
streetError = null,
sequenceError = null,
} = {}) {
return {
enabled,
@@ -434,7 +435,12 @@ function uiState({
keyRejected: false,
filter: { pano, sinceDays },
coverage: { count: 3, loading: false, hint: '', error: null },
sequence: { selectedId: null, images: 0, loading: false },
sequence: {
selectedId: null,
images: 0,
loading: false,
error: sequenceError,
},
street: {
open,
loading: false,
@@ -803,6 +809,18 @@ test('a photo that fails to open opens the panel too, so its error shows', () =>
controls.destroy();
}));
test('a sequence that fails to load opens the panel, so its error shows', () =>
withDom(async (dom) => {
const { layer, calls, controls } = stubPanel(dom, uiState());
// A Mapillary line click whose sequence lookup the Graph API refused.
layer.publish(uiState({ sequenceError: 'Mapillary did not answer' }));
assert.deepEqual(calls.collapsed, [[false, { persist: false }]]);
assert.equal(dom.root.querySelector('#sl-error').hidden, false);
layer.publish(uiState({ sequenceError: 'Mapillary did not answer' }));
assert.equal(calls.collapsed.length, 1, 'the same error opens it once');
controls.destroy();
}));
test('FIT / FILL show the selected render mode (P3)', () =>
withDom(async (dom) => {
const { layer, controls } = stubPanel(dom, uiState({ open: true }));