diff --git a/website/src/lib/components/map/layer-control/extension-api.test.ts b/website/src/lib/components/map/layer-control/extension-api.test.ts index 3fee37769..8fab8157a 100644 --- a/website/src/lib/components/map/layer-control/extension-api.test.ts +++ b/website/src/lib/components/map/layer-control/extension-api.test.ts @@ -399,13 +399,43 @@ describe('ExtensionAPI v2', () => { assert.deepEqual(calls, [{ a: true }]); }); - it('does not tell the extension that all is hidden when the page closes', () => { - const calls: unknown[] = []; - api.onVisibilityChange('Heatmaps', (visible) => calls.push(visible)); - api.setOverlays('Heatmaps', [v2('a', { visible: true })]); - (api as unknown as { destroy(): void }).destroy(); - assert.deepEqual(calls, []); + it('forgets the state of the overlays with the next session', () => { + const saved = () => ({ + currentOverlays: JSON.stringify(get(settings.currentOverlays)), + previousOverlays: JSON.stringify(get(settings.previousOverlays)), + selectedOverlayTree: JSON.stringify(get(settings.selectedOverlayTree)), + opacities: JSON.stringify(get(settings.opacities)), + }); + // what was saved, not the default trees, which are the ones that are allowed + settings.connect(saved()); + + api.setOverlays('Heatmaps', [v2('a', { visible: true }), v2('b')]); + api.addOrUpdateOverlay(overlay('one')); + settings.selectedOverlayTree.update((tree) => { + group(tree)![id('b')] = false; + return tree; + }); + settings.opacities.update((opacities) => ({ ...opacities, [id('a')]: 0.3 })); + const stored = saved(); + assert.ok(stored.currentOverlays.includes(id('a'))); + + settings.connect(stored); + for (const tree of [ + get(settings.currentOverlays), + get(settings.previousOverlays), + get(settings.selectedOverlayTree), + ]) { + assert.ok(!JSON.stringify(tree).includes('extension-')); + } + assert.equal(get(settings.opacities)[id('a')], 0.3); + + // an extension that is still there starts again, as the source of what it remembers + const next = new ExtensionAPI(); + next.setOverlays('Heatmaps', [v2('a'), v2('b', { visible: true })]); assert.equal(isSelected(get(settings.currentOverlays)!, id('a')), false); + assert.equal(isSelected(get(settings.currentOverlays)!, id('b')), true); + assert.equal(isSelected(get(settings.selectedOverlayTree), id('b')), true); + next.setOverlays('Heatmaps', []); }); it('leaves a style URL for the map to fetch', () => { diff --git a/website/src/lib/components/map/layer-control/extension-api.ts b/website/src/lib/components/map/layer-control/extension-api.ts index ac08bf550..2e2a90373 100644 --- a/website/src/lib/components/map/layer-control/extension-api.ts +++ b/website/src/lib/components/map/layer-control/extension-api.ts @@ -1,7 +1,7 @@ import { settings } from '$lib/state/settings'; import type { StyleSpecification } from 'maplibre-gl'; import { derived, get, writable, type Writable } from 'svelte/store'; -import { isSelected, prefixStyle, remove, removeAll } from './utils'; +import { isSelected, prefixStyle, removeAll } from './utils'; import { layerGroup, overlays, overlayTree, type LayerTreeType } from '$lib/data/layers'; import { browser } from '$app/environment'; import { map } from '$lib/components/map/map'; @@ -101,9 +101,6 @@ export class ExtensionAPI { Object.defineProperty(window, 'gpxstudio', { value: this, }); - addEventListener('beforeunload', () => { - this.destroy(); - }); } } @@ -432,30 +429,6 @@ export class ExtensionAPI { const names = new Set(Array.from($overlays.values(), (overlay) => overlay.extensionName)); return (id: string) => names.has(id); }); - - private destroy() { - // the page is closing: the user has not hidden anything, which extensions would then save - this._visibilityCallbacks.clear(); - const ids = Array.from(get(this._overlays).keys()); - currentOverlays.update((current) => { - ids.forEach((id) => { - remove(current, id); - }); - return current; - }); - previousOverlays.update((previous) => { - ids.forEach((id) => { - remove(previous, id); - }); - return previous; - }); - selectedOverlayTree.update((selected) => { - ids.forEach((id) => { - remove(selected, id); - }); - return selected; - }); - } } export const extensionAPI = new ExtensionAPI(); diff --git a/website/src/lib/state/setting.test.ts b/website/src/lib/state/setting.test.ts index 3a5e6686a..5422fc41e 100644 --- a/website/src/lib/state/setting.test.ts +++ b/website/src/lib/state/setting.test.ts @@ -141,11 +141,13 @@ describe('layer trees', () => { assert.deepEqual(filterLayerTree({}, allowed), allowed); }); - it('drops the layers that are not allowed anymore, but not the custom ones', () => { + it('drops the layers that are not allowed anymore and those of extensions, not the custom ones', () => { const filtered = filterLayerTree( { - world: { a: true, gone: true, 'custom-1': true, 'extension-x': false }, + world: { a: true, gone: true, 'custom-1': true, 'extension-x': true }, old: { x: true }, + extension: { 'extension-Name/y': true }, + custom: { 'custom-2': false }, }, allowed ); @@ -153,10 +155,10 @@ describe('layer trees', () => { a: true, b: false, 'custom-1': true, - 'extension-x': false, }); - assert.equal('old' in filtered, true); - assert.deepEqual(filtered.old, {}); + assert.equal('old' in filtered, false); + assert.equal('extension' in filtered, false); + assert.deepEqual(filtered.custom, { 'custom-2': false }); }); it('does not take a branch for a layer, or a layer for a branch', () => { diff --git a/website/src/lib/state/setting.ts b/website/src/lib/state/setting.ts index ca6234500..6189e4af7 100644 --- a/website/src/lib/state/setting.ts +++ b/website/src/lib/state/setting.ts @@ -196,7 +196,9 @@ function isRecord(value: unknown): value is Record { /** * The saved tree `t`, with the layers of `allowed`: the choices saved for them are kept, the new - * ones have their default, and of the others only the custom and extension layers are kept. + * ones have their default, and of the others only the custom layers are kept. The layers of + * extensions are saved with the others, but not read back: they come again when an extension sets + * them, if it is still there. */ export function filterLayerTree(t: unknown, allowed: LayerTreeType | undefined): LayerTreeType { const tree = isRecord(t) ? t : {}; @@ -214,11 +216,14 @@ export function filterLayerTree(t: unknown, allowed: LayerTreeType | undefined): Object.entries(tree).forEach(([key, value]) => { if (!Object.hasOwn(filtered, key)) { if (typeof value === 'boolean') { - if (key.startsWith('custom-') || key.startsWith('extension-')) { + if (key.startsWith('custom-')) { filtered[key] = value; } } else if (isRecord(value)) { - filtered[key] = filterLayerTree(value, undefined); + const group = filterLayerTree(value, undefined); + if (Object.keys(group).length > 0) { + filtered[key] = group; + } } } });