mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-14 07:12:21 +00:00
Suspend only the layers Context isolation is switching off
The session snapshot recorded a user's switch only after the CCTV panel had already rendered the layer off, so a first switch-off by the user inside Contacts, or after a partly failed restore, still kept the panel open. Context controls now hold the layers _clearLayersOutsideContextMode is switching off while its transitions run; suspendsLayer reads that set, and the snapshot is no longer consulted.
This commit is contained in:
1 parent
f0ee1b95ca
commit
9b46692c64
6 files changed
+50
-63
No files matched your search
@@ -2235,7 +2235,9 @@ the collapse, so a panel the desktop accordion only folded has its saved and
|
||||
shared state closed too. Clear selected data layers and Context isolation
|
||||
(Contacts, Space Missions) switch the layer off without the user asking: the
|
||||
panel and its share preference stay as they are, and the layer comes back to an
|
||||
open panel. A panel opened by hand while the layer is off stays open, and its
|
||||
open panel. Only the switch-off isolation makes counts (`suspendsLayer`, true
|
||||
while `_clearLayersOutsideContextMode` switches the layer off): the user
|
||||
switching CCTV off inside Contacts closes the panel as anywhere else. A panel opened by hand while the layer is off stays open, and its
|
||||
meta line reads the catalog count, not the last active camera.
|
||||
|
||||
## UI disposal
|
||||
|
||||
@@ -277,7 +277,6 @@ 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;
|
||||
|
||||
@@ -51,6 +51,8 @@ export class ContextControls {
|
||||
this._contextRestoreState = null;
|
||||
this._contextLayerReactionPromises = new Set();
|
||||
this._preservePanelStateDuringLayerClear = false;
|
||||
/** Layers Context isolation is switching off right now. */
|
||||
this._isolatingLayerIds = new Set();
|
||||
this._userFacingContextNotificationTokens = new Set();
|
||||
this._clearSelectedLayersPromise = null;
|
||||
this._clearSelectedLayersManagerPromise = null;
|
||||
@@ -60,18 +62,14 @@ export class ContextControls {
|
||||
if (!this.destroyed) this.actions.showToast(...args);
|
||||
}
|
||||
/**
|
||||
* Whether Context isolation switched this layer off and leaving the mode
|
||||
* restores it. Such a layer is suspended, not switched off by the user: a
|
||||
* layer the user switched during the session is theirs again.
|
||||
* Whether Context isolation is switching this layer off right now: it is
|
||||
* suspended, and leaving the mode restores it. A switch-off by the user,
|
||||
* inside the mode or out of it, is not.
|
||||
* @param {string} layerId Layer id.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
suspendsLayer(layerId) {
|
||||
const snapshot = this._contextSessionSnapshot;
|
||||
return Boolean(
|
||||
snapshot?.enabledLayerIds?.has(layerId) &&
|
||||
!snapshot.userSwitched?.has(layerId),
|
||||
);
|
||||
return this._isolatingLayerIds.has(layerId);
|
||||
}
|
||||
setClearBusy(...args) {
|
||||
if (!this.destroyed) this.actions.setClearBusy(...args);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
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() {
|
||||
@@ -155,7 +154,7 @@ test('a rejected tracked reaction settles without an unhandled rejection', async
|
||||
assert.equal(controls._contextLayerReactionPromises.size, 0);
|
||||
});
|
||||
|
||||
test('a layer Context isolation switches off is suspended until the mode is left', async (t) => {
|
||||
test('only a layer Context isolation is switching off is suspended', async (t) => {
|
||||
const { controls } = fixture(t);
|
||||
const enabled = new Set(['cctv']);
|
||||
// What the CCTV panel is told at each visibility change of its layer.
|
||||
@@ -168,6 +167,7 @@ test('a layer Context isolation switches off is suspended until the mode is left
|
||||
isEnabled: (layerId) => enabled.has(layerId),
|
||||
isEffectivelyEnabled: (layerId) => enabled.has(layerId),
|
||||
async setEnabled(layerId, next) {
|
||||
// The layer tells its panel it is off while it is being disabled.
|
||||
if (next) enabled.add(layerId);
|
||||
else enabled.delete(layerId);
|
||||
seen.push([layerId, next, controls.suspendsLayer(layerId)]);
|
||||
@@ -181,47 +181,28 @@ test('a layer Context isolation switches off is suspended until the mode is left
|
||||
controls._captureContextSessionSnapshot();
|
||||
await controls._clearLayersOutsideContextMode('flights');
|
||||
assert.deepEqual(seen, [['cctv', false, true]]);
|
||||
assert.equal(controls.suspendsLayer('cctv'), true);
|
||||
assert.equal(controls.suspendsLayer('street-level'), false);
|
||||
assert.equal(controls.suspendsLayer('cctv'), false, 'only while isolating');
|
||||
// Inside the mode the user turns CCTV back on, then off: their own.
|
||||
await controls._dataManager.setEnabled('cctv', true);
|
||||
await controls._dataManager.setEnabled('cctv', false);
|
||||
assert.deepEqual(seen.at(-1), ['cctv', false, false]);
|
||||
await controls._restoreContextSession();
|
||||
assert.equal(enabled.has('cctv'), true);
|
||||
assert.equal(controls.suspendsLayer('cctv'), false);
|
||||
// A switch-off after the mode is left is the user's own.
|
||||
// A switch-off after the mode is left is the user's own too.
|
||||
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) => {
|
||||
test('a failed isolation leaves no layer 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;
|
||||
isEffectivelyEnabled: () => true,
|
||||
isEnabled: () => true,
|
||||
setEnabled: async () => {
|
||||
throw new Error('disable failed');
|
||||
},
|
||||
};
|
||||
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',
|
||||
});
|
||||
}
|
||||
await assert.rejects(controls._clearLayersOutsideContextMode('flights'));
|
||||
assert.equal(controls.suspendsLayer('cctv'), false);
|
||||
});
|
||||
@@ -36,8 +36,6 @@ export function _captureContextSessionSnapshot({ excludeLayerIds = [] } = {}) {
|
||||
),
|
||||
userAdded: new Set(),
|
||||
userRemoved: new Set(),
|
||||
// Every layer the user switched during the session, either way.
|
||||
userSwitched: new Set(),
|
||||
params,
|
||||
};
|
||||
}
|
||||
@@ -151,7 +149,6 @@ export async function _restoreContextSession({
|
||||
enabledLayerIds: new Set(restoreState.enabledLayerIds),
|
||||
userAdded: new Set(),
|
||||
userRemoved: new Set(),
|
||||
userSwitched: new Set(),
|
||||
params: snapshot.params,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -240,25 +240,35 @@ export async function _clearLayersOutsideContextMode(
|
||||
) {
|
||||
const allowed = contextAllowedLayerIds(mode);
|
||||
const pending = [];
|
||||
for (const [layerId] of this._dataManager.layers || []) {
|
||||
// Effective visibility: a disallowed layer still mid-ENABLING must be
|
||||
// isolated too, or it settles ON inside the exclusive mode.
|
||||
if (
|
||||
!allowed.has(layerId) &&
|
||||
this._dataManager.isEffectivelyEnabled(layerId)
|
||||
) {
|
||||
pending.push({
|
||||
layerId,
|
||||
transition: this._dataManager.setEnabled(layerId, false, {
|
||||
notificationToken,
|
||||
...(signal ? { signal } : {}),
|
||||
}),
|
||||
});
|
||||
// Panels read this while their layer goes off (suspendsLayer): a layer
|
||||
// isolation switches off is suspended, not switched off by the user.
|
||||
const isolating = [];
|
||||
let results;
|
||||
try {
|
||||
for (const [layerId] of this._dataManager.layers || []) {
|
||||
// Effective visibility: a disallowed layer still mid-ENABLING must be
|
||||
// isolated too, or it settles ON inside the exclusive mode.
|
||||
if (
|
||||
!allowed.has(layerId) &&
|
||||
this._dataManager.isEffectivelyEnabled(layerId)
|
||||
) {
|
||||
if (!this._isolatingLayerIds.has(layerId)) {
|
||||
this._isolatingLayerIds.add(layerId);
|
||||
isolating.push(layerId);
|
||||
}
|
||||
pending.push({
|
||||
layerId,
|
||||
transition: this._dataManager.setEnabled(layerId, false, {
|
||||
notificationToken,
|
||||
...(signal ? { signal } : {}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
results = await Promise.all(pending.map(({ transition }) => transition));
|
||||
} finally {
|
||||
for (const layerId of isolating) this._isolatingLayerIds.delete(layerId);
|
||||
}
|
||||
const results = await Promise.all(
|
||||
pending.map(({ transition }) => transition),
|
||||
);
|
||||
const failed = pending
|
||||
.filter(
|
||||
({ layerId }, index) =>
|
||||
|
||||
Reference in new issue
Block a user