Skip to content

Commit 1eb0f1a

Browse files
committed
fix: clear frame previews, update layer style for all frames on changes
1 parent 80e1a2b commit 1eb0f1a

3 files changed

Lines changed: 108 additions & 67 deletions

File tree

web/src/components/sidebars/LayerStyle.vue

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -84,38 +84,23 @@ const setCurrentLayerStyle = (style: LayerStyle) => {
8484
styleStore.selectedLayerStyles[styleKey.value] = style;
8585
};
8686
87-
// After a style save, the backend invalidates multiframe raster previews and
88-
// regenerates them asynchronously. Apply the saved style (which the API returns
89-
// with preview_status "notready" and no previews), drop any stale previews, and
90-
// remove the on-map preview overlay so tiles are shown until regeneration
91-
// completes and the WebSocket handler reattaches fresh previews.
87+
// After a style save, apply the API response. When previews were invalidated,
88+
// clear stale payloads/overlays; onPreviewTaskComplete reloads them when ready.
9289
const markStyleSavedAndInvalidatePreviews = (style: LayerStyle) => {
93-
const previewStatus =
94-
style.preview_status === "ready" && !style.multiframe_previews
95-
? "notready"
96-
: (style.preview_status ?? "notready");
97-
const invalidatedStyle: LayerStyle = {
98-
...style,
99-
preview_status: previewStatus,
100-
multiframe_previews: undefined,
101-
};
102-
setCurrentLayerStyle(invalidatedStyle);
103-
if (invalidatedStyle.is_default) {
104-
layerStore.selectedLayers = layerStore.selectedLayers.map((layer) => {
105-
if (
106-
layer.id !== props.layer.id ||
107-
layer.copy_id !== props.layer.copy_id
108-
) {
109-
return layer;
110-
}
111-
return {
112-
...layer,
113-
preview_status: invalidatedStyle.preview_status,
114-
multiframe_previews: undefined,
115-
};
116-
});
90+
const previewsStillValid =
91+
style.preview_status === "ready" && !!style.multiframe_previews?.length;
92+
setCurrentLayerStyle(
93+
previewsStillValid
94+
? cloneDeep(style)
95+
: {
96+
...style,
97+
preview_status: style.preview_status ?? "notready",
98+
multiframe_previews: undefined,
99+
},
100+
);
101+
if (!previewsStillValid) {
102+
framePreviewStore.clearPreviewsForStyleChange(props.layer, style.id);
117103
}
118-
framePreviewStore.dismissPreviewForLayer(props.layer);
119104
};
120105
121106
const appliedStyleName = computed(() => {
@@ -674,15 +659,38 @@ watch(
674659
);
675660
676661
const debouncedStyleSpecUpdated = debounce(() => {
677-
if (currentStyleSpec.value) {
678-
styleStore.selectedLayerStyles[styleKey.value] = {
679-
...currentLayerStyle.value,
680-
style_spec: currentStyleSpec.value,
681-
};
682-
styleStore.updateLayerStyles(props.layer);
683-
setAvailableGroups();
684-
unsavedChanges.value = true;
662+
if (!currentStyleSpec.value) return;
663+
664+
const prev = currentLayerStyle.value;
665+
const hadPreviews =
666+
prev?.preview_status === "ready" || !!prev?.multiframe_previews?.length;
667+
let clearPreviews = false;
668+
if (hadPreviews && prev?.style_spec && showRasterOptions.value) {
669+
const prevQuery = JSON.stringify(
670+
styleStore.getRasterTilesQuery(prev.style_spec, styleStore.colormaps),
671+
);
672+
const nextQuery = JSON.stringify(
673+
styleStore.getRasterTilesQuery(
674+
currentStyleSpec.value,
675+
styleStore.colormaps,
676+
),
677+
);
678+
clearPreviews = prevQuery !== nextQuery;
679+
}
680+
681+
styleStore.selectedLayerStyles[styleKey.value] = {
682+
...prev,
683+
style_spec: currentStyleSpec.value,
684+
...(clearPreviews
685+
? { preview_status: "notready" as const, multiframe_previews: undefined }
686+
: {}),
687+
};
688+
if (clearPreviews) {
689+
framePreviewStore.clearPreviewsForStyleChange(props.layer, prev.id);
685690
}
691+
styleStore.updateLayerStyles(props.layer);
692+
setAvailableGroups();
693+
unsavedChanges.value = true;
686694
}, 100);
687695
watch(currentStyleSpec, debouncedStyleSpecUpdated, { deep: true });
688696

web/src/store/framePreview.ts

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -347,21 +347,17 @@ export const useFramePreviewStore = defineStore("framePreview", () => {
347347
hidePreviousPreview(map, layerKeyValue);
348348

349349
if (!preview) {
350+
// Drop any leftover overlay (including adjacent preloads) so a stale
351+
// image cannot flash while tiles catch up after a style change.
352+
removeAllPreviewLayersForLayerKey(map, layerKeyValue);
353+
activePreviewByLayerKey.delete(layerKeyValue);
350354
clearPreviewDisplayed(layerKeyValue);
351355
ensureRasterTilesOnMap(
352356
currentFrame,
353357
tileSourceId,
354358
tileLayerId,
355359
targetOpacity,
356360
);
357-
void preloadAdjacentPreviewLayers(
358-
map,
359-
layerKeyValue,
360-
previews,
361-
rasterFrames,
362-
layer.current_frame_index,
363-
targetOpacity,
364-
);
365361
return;
366362
}
367363

@@ -437,6 +433,37 @@ export const useFramePreviewStore = defineStore("framePreview", () => {
437433
}
438434
}
439435

436+
/** Clear stale preview payloads + map overlays after a style change. */
437+
function clearPreviewsForStyleChange(layer: Layer, styleId?: number) {
438+
layerStore.selectedLayers.forEach((candidate) => {
439+
if (candidate.id !== layer.id) return;
440+
441+
const key = styleStore.layerStyleKey(candidate);
442+
const selectedStyle = styleStore.selectedLayerStyles[key];
443+
if (!selectedStyle) return;
444+
445+
const matches =
446+
styleId !== undefined
447+
? selectedStyle.id === styleId
448+
: candidate.copy_id === layer.copy_id;
449+
if (!matches) return;
450+
451+
styleStore.selectedLayerStyles[key] = {
452+
...selectedStyle,
453+
preview_status: "notready",
454+
multiframe_previews: undefined,
455+
};
456+
if (
457+
selectedStyle.is_default ||
458+
usesLayerDefaultPreviews(candidate, selectedStyle)
459+
) {
460+
candidate.preview_status = "notready";
461+
candidate.multiframe_previews = undefined;
462+
}
463+
dismissPreviewForLayer(candidate);
464+
});
465+
}
466+
440467
function cleanupLayer(layer: Layer) {
441468
const layerKeyValue = styleStore.layerStyleKey(layer);
442469
transitionGenerationByLayerKey.delete(layerKeyValue);
@@ -551,6 +578,7 @@ export const useFramePreviewStore = defineStore("framePreview", () => {
551578
hasReadyPreviewForCurrentFrame,
552579
showPreviewThenTiles,
553580
dismissPreviewForLayer,
581+
clearPreviewsForStyleChange,
554582
onPreviewTaskComplete,
555583
cleanupLayer,
556584
clearAll,

web/src/store/style.ts

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -483,33 +483,38 @@ export const useStyleStore = defineStore("style", () => {
483483
(f) => f.index === layer.current_frame_index,
484484
);
485485
if (!currentFrame) return;
486+
487+
const styleKey = layerStyleKey(layer);
488+
const currentStyleSpec: StyleSpec | undefined =
489+
selectedLayerStyles.value[styleKey]?.style_spec;
490+
486491
mapStore.getUserMapLayers().forEach((mapLayerId) => {
487492
if (isPreviewMapLayerId(mapLayerId)) {
488493
return;
489494
}
490495
const { layerId, layerCopyId, frameId } =
491496
mapStore.parseLayerString(mapLayerId);
492-
if (layerId === layer.id && layerCopyId === layer.copy_id) {
493-
if (frameId === currentFrame.id) {
494-
map.setLayoutProperty(
495-
mapLayerId,
496-
"visibility",
497-
layer.visible ? "visible" : "none",
498-
);
499-
const styleKey = layerStyleKey(layer);
500-
const currentStyleSpec: StyleSpec | undefined =
501-
selectedLayerStyles.value[styleKey].style_spec;
502-
if (currentStyleSpec) {
503-
setMapLayerStyle(
504-
mapLayerId,
505-
currentStyleSpec,
506-
currentFrame,
507-
currentFrame.vector,
508-
);
509-
}
510-
} else {
511-
map.setLayoutProperty(mapLayerId, "visibility", "none");
512-
}
497+
if (layerId !== layer.id || layerCopyId !== layer.copy_id) {
498+
return;
499+
}
500+
501+
const frame = frames.find((f) => f.id === frameId);
502+
if (!frame) {
503+
return;
504+
}
505+
506+
const isCurrent = frameId === currentFrame.id;
507+
map.setLayoutProperty(
508+
mapLayerId,
509+
"visibility",
510+
layer.visible && isCurrent ? "visible" : "none",
511+
);
512+
513+
// Restyle every loaded frame, not only the current one. Otherwise a
514+
// previously visited / adjacent frame keeps the old tile URL and flashes
515+
// the previous style when scrubbed to after a style change.
516+
if (currentStyleSpec) {
517+
setMapLayerStyle(mapLayerId, currentStyleSpec, frame, frame.vector);
513518
}
514519
});
515520
networkStore.styleVisibleNetworks();

0 commit comments

Comments
 (0)