diff --git a/src/contextModePolicy.js b/src/contextModePolicy.js index 6e3238ec5..ef485fe97 100644 --- a/src/contextModePolicy.js +++ b/src/contextModePolicy.js @@ -277,6 +277,7 @@ export function recordContextSessionUserChange({ ) { return false; } + snapshot.userSwitched?.add(change.layerId); const isCompanion = CONTEXT_COMPANIONS.has(change.layerId); if (change.enabled) { if (CONTEXT_ENTRY_LAYER_IDS.includes(change.layerId)) return false; diff --git a/src/ui/contextControls.js b/src/ui/contextControls.js index 552a9e3e9..3c297f277 100644 --- a/src/ui/contextControls.js +++ b/src/ui/contextControls.js @@ -61,12 +61,17 @@ export class ContextControls { } /** * Whether Context isolation switched this layer off and leaving the mode - * restores it. Such a layer is suspended, not switched off by the user. + * restores it. Such a layer is suspended, not switched off by the user: a + * layer the user switched during the session is theirs again. * @param {string} layerId Layer id. * @returns {boolean} */ suspendsLayer(layerId) { - return Boolean(this._contextSessionSnapshot?.enabledLayerIds?.has(layerId)); + const snapshot = this._contextSessionSnapshot; + return Boolean( + snapshot?.enabledLayerIds?.has(layerId) && + !snapshot.userSwitched?.has(layerId), + ); } setClearBusy(...args) { if (!this.destroyed) this.actions.setClearBusy(...args); diff --git a/src/ui/contextControls.test.mjs b/src/ui/contextControls.test.mjs index 4a4e69a9f..caaba2060 100644 --- a/src/ui/contextControls.test.mjs +++ b/src/ui/contextControls.test.mjs @@ -1,6 +1,7 @@ import assert from 'node:assert/strict'; import { test } from 'node:test'; import { ContextControls } from './contextControls.js'; +import { recordContextSessionUserChange } from '../contextModePolicy.js'; class Button extends EventTarget { constructor() { @@ -189,3 +190,38 @@ test('a layer Context isolation switches off is suspended until the mode is left await controls._dataManager.setEnabled('cctv', false); assert.deepEqual(seen.at(-1), ['cctv', false, false]); }); + +test('a layer the user switches inside the mode is no longer suspended', async (t) => { + const { controls } = fixture(t); + const enabled = new Set(['cctv']); + controls._dataManager = { + layers: new Map([['cctv', {}]]), + getEnabledLayerIds: () => [...enabled], + getLayerParams: () => null, + isEffectivelyEnabled: (layerId) => enabled.has(layerId), + isEnabled: (layerId) => enabled.has(layerId), + async setEnabled(layerId, next) { + if (next) enabled.add(layerId); + else enabled.delete(layerId); + return true; + }, + }; + controls._captureContextSessionSnapshot(); + await controls._clearLayersOutsideContextMode('flights'); + assert.equal(controls.suspendsLayer('cctv'), true); + // Inside Contacts the user turns CCTV back on, then off again: the panel + // must close like any other switch-off. + for (const next of [true, false]) { + recordContextSessionUserChange({ + snapshot: controls._contextSessionSnapshot, + change: { + type: 'visibility', + origin: 'user', + layerId: 'cctv', + enabled: next, + }, + effectiveContextMode: 'flights', + }); + } + assert.equal(controls.suspendsLayer('cctv'), false); +}); diff --git a/src/ui/contextSession.js b/src/ui/contextSession.js index 936fc2cb1..3f1f2eeb1 100644 --- a/src/ui/contextSession.js +++ b/src/ui/contextSession.js @@ -36,6 +36,8 @@ export function _captureContextSessionSnapshot({ excludeLayerIds = [] } = {}) { ), userAdded: new Set(), userRemoved: new Set(), + // Every layer the user switched during the session, either way. + userSwitched: new Set(), params, }; } @@ -149,6 +151,7 @@ export async function _restoreContextSession({ enabledLayerIds: new Set(restoreState.enabledLayerIds), userAdded: new Set(), userRemoved: new Set(), + userSwitched: new Set(), params: snapshot.params, }; }