fix(street-level): a photo that fails to open opens the panel too

With the panel collapsed (the layer switched on from DATA LAYERS no longer
opens it), a line or ground click whose lookup failed — KartaView timing out
or answering 503, no Street View within 50 m — left its error inside the
collapsed panel: the click seemed to do nothing. A new open error now opens
the panel, unstored, as a photo opening does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
German KhokhlovandClaude Opus 5.5 committed 2026-10-19 23:17:24 +04:00
1 parent 44d9455738
commit fce328b7cd
4 files changed
+33 -9

No files matched your search

+3 -2
View File
@@ -1,4 +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, 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 fails to, no
longer when the layer is switched on. FIT / FILL starts at FIT each
session (gekh, #1241).
+2 -2
View File
@@ -3539,8 +3539,8 @@ 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 (a click on the map or a line would otherwise load it out of
sight), never when the layer is switched on.
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.
MapillaryJS loads on the first photo. Opening one frames it with a single
camera flight through the application's deferred navigation (`begin` /
+11 -4
View File
@@ -29,6 +29,8 @@ 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. */
this._streetError = null;
/** The SINCE option for a day count between the stops, while one is set. */
this._customSince = null;
this._resizeQueued = false;
@@ -430,16 +432,21 @@ export class StreetLevelControls {
}
/**
* Open the panel when an image opens (a click on the map or a line would
* otherwise load it out of sight). Never persisted: the stored collapse
* state stays the user's own choice.
* 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.
*/
_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;
if (open && !this._wasOpen) this.setCollapsed(false, { persist: false });
// 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;
if ((open && !this._wasOpen) || (error && error !== this._streetError))
this.setCollapsed(false, { persist: false });
this._wasOpen = open;
this._streetError = error;
}
destroy() {
+17 -1
View File
@@ -425,6 +425,7 @@ function uiState({
sinceDays = 0,
renderMode = 'letterbox',
keyRequired = false,
streetError = null,
} = {}) {
return {
enabled,
@@ -439,7 +440,7 @@ function uiState({
loading: false,
renderMode,
imageId: open ? 'img-1' : null,
error: null,
error: streetError,
},
};
}
@@ -787,6 +788,21 @@ test('a photo opening opens the panel, unstored; switching the layer on does not
controls.destroy();
}));
test('a photo that fails to open opens the panel too, so its error shows', () =>
withDom(async (dom) => {
const { layer, calls, controls } = stubPanel(dom, uiState());
// A line click whose lookup KartaView never answered.
layer.publish(uiState({ streetError: 'KartaView did not answer in time' }));
assert.deepEqual(calls.collapsed, [[false, { persist: false }]]);
// The same error again opens nothing more; a new one does.
layer.publish(uiState({ streetError: 'KartaView did not answer in time' }));
assert.equal(calls.collapsed.length, 1);
layer.publish(uiState());
layer.publish(uiState({ streetError: 'No street-level imagery here' }));
assert.equal(calls.collapsed.length, 2);
controls.destroy();
}));
test('FIT / FILL show the selected render mode (P3)', () =>
withDom(async (dom) => {
const { layer, controls } = stubPanel(dom, uiState({ open: true }));