mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
fix: retain shell feedback bindings and stop disposed panel work
This commit is contained in:
1 parent
03ad8142e6
commit
945a809994
5 files changed
+63
-29
No files matched your search
@@ -33,7 +33,10 @@ try {
|
|||||||
const ui = window.__godsEyeView.styleManager;
|
const ui = window.__godsEyeView.styleManager;
|
||||||
const counts = {};
|
const counts = {};
|
||||||
const watchObserver = (name) => {
|
const watchObserver = (name) => {
|
||||||
const owner = name === '_draggableResizeObserver' ? ui._panelPosition : ui._panelLayout;
|
const owner =
|
||||||
|
name === '_draggableResizeObserver'
|
||||||
|
? ui._panelPosition
|
||||||
|
: ui._panelLayout;
|
||||||
const observer = owner[name];
|
const observer = owner[name];
|
||||||
if (!observer) return false;
|
if (!observer) return false;
|
||||||
const disconnect = observer.disconnect.bind(observer);
|
const disconnect = observer.disconnect.bind(observer);
|
||||||
@@ -110,6 +113,12 @@ try {
|
|||||||
if (type === 'resize' && callback === resizeHandler) counts.resize++;
|
if (type === 'resize' && callback === resizeHandler) counts.resize++;
|
||||||
return removeEventListener.call(this, type, callback, options);
|
return removeEventListener.call(this, type, callback, options);
|
||||||
};
|
};
|
||||||
|
const { setSplitFlapText } = await import('/src/splitFlap.js');
|
||||||
|
const feedbackLabel = document.getElementById('global-loading-label');
|
||||||
|
setSplitFlapText(feedbackLabel, 'CHECKING LIVE DATA');
|
||||||
|
setSplitFlapText(feedbackLabel, 'LOAD COMPLETE');
|
||||||
|
const permanentText =
|
||||||
|
feedbackLabel.querySelector('.gev-flap-text')?.firstChild;
|
||||||
try {
|
try {
|
||||||
const disposal = ui.dispose();
|
const disposal = ui.dispose();
|
||||||
const focusBefore = document.activeElement;
|
const focusBefore = document.activeElement;
|
||||||
@@ -167,7 +176,11 @@ try {
|
|||||||
counts[field] === 1 && ui._contextControls[field] === null,
|
counts[field] === 1 && ui._contextControls[field] === null,
|
||||||
),
|
),
|
||||||
observersReleased: observed.every(
|
observersReleased: observed.every(
|
||||||
(name) => counts[name] === 1 && ui[name] === null,
|
(name) =>
|
||||||
|
counts[name] === 1 &&
|
||||||
|
(name === '_draggableResizeObserver'
|
||||||
|
? ui._panelPosition
|
||||||
|
: ui._panelLayout)[name] === null,
|
||||||
),
|
),
|
||||||
subscriptionReleased:
|
subscriptionReleased:
|
||||||
counts.cctv === 1 && ui._cctvControls._cctvUnsubscribe === null,
|
counts.cctv === 1 && ui._cctvControls._cctvUnsubscribe === null,
|
||||||
@@ -176,6 +189,19 @@ try {
|
|||||||
ui._radioControls.destroyed &&
|
ui._radioControls.destroyed &&
|
||||||
ui._locationControls.destroyed &&
|
ui._locationControls.destroyed &&
|
||||||
ui._cctvControls.destroyed,
|
ui._cctvControls.destroyed,
|
||||||
|
feedbackTextPreserved:
|
||||||
|
permanentText?.nodeType === Node.TEXT_NODE &&
|
||||||
|
feedbackLabel.querySelector('.gev-flap-text')?.firstChild ===
|
||||||
|
permanentText &&
|
||||||
|
permanentText.data === 'LOAD COMPLETE' &&
|
||||||
|
!feedbackLabel.classList.contains('gev-flap-active') &&
|
||||||
|
feedbackLabel.querySelector('.gev-flap-cells')?.childNodes.length ===
|
||||||
|
0,
|
||||||
|
shellWorkReleased:
|
||||||
|
ui._lifetime.destroyed &&
|
||||||
|
ui._lifetime.frames.size === 0 &&
|
||||||
|
ui._lifetime.timers.size === 0 &&
|
||||||
|
ui._lifetime.removers.size === 0,
|
||||||
idempotent: once === JSON.stringify(counts),
|
idempotent: once === JSON.stringify(counts),
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
@@ -215,6 +241,14 @@ try {
|
|||||||
'Cockpit cleanup releases listeners and returns all Display groups home',
|
'Cockpit cleanup releases listeners and returns all Display groups home',
|
||||||
result.cockpitReleased,
|
result.cockpitReleased,
|
||||||
);
|
);
|
||||||
|
check(
|
||||||
|
'feedback teardown preserves the permanent accessible text node',
|
||||||
|
result.feedbackTextPreserved,
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
'shell teardown cancels deferred work and releases listeners',
|
||||||
|
result.shellWorkReleased,
|
||||||
|
);
|
||||||
check('repeated UI disposal is inert', result.idempotent);
|
check('repeated UI disposal is inert', result.idempotent);
|
||||||
check(
|
check(
|
||||||
'disposal and subsequent resize produce no uncaught browser errors',
|
'disposal and subsequent resize produce no uncaught browser errors',
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { setSplitFlapText } from '../splitFlap.js';
|
||||||
import { UiLifetime } from './uiLifetime.js';
|
import { UiLifetime } from './uiLifetime.js';
|
||||||
import { RecordingControls } from './recordingControls.js';
|
import { RecordingControls } from './recordingControls.js';
|
||||||
import { readShellElements } from './shellElements.js';
|
import { readShellElements } from './shellElements.js';
|
||||||
@@ -86,8 +87,6 @@ import {
|
|||||||
stampInitialShareGesture,
|
stampInitialShareGesture,
|
||||||
} from '../navigationPolicy.js';
|
} from '../navigationPolicy.js';
|
||||||
|
|
||||||
/** Versioned localStorage namespace prefix to invalidate stale panel layouts. */
|
|
||||||
|
|
||||||
const SHARE_PANEL_STATE_SPECS = Object.freeze([
|
const SHARE_PANEL_STATE_SPECS = Object.freeze([
|
||||||
{ id: 'control-panel', pinnable: true },
|
{ id: 'control-panel', pinnable: true },
|
||||||
{ id: 'location-bar', pinnable: true },
|
{ id: 'location-bar', pinnable: true },
|
||||||
@@ -108,28 +107,7 @@ const COCKPIT_ENTRY_COLLAPSE_PANEL_IDS = Object.freeze([
|
|||||||
'global-context-panel',
|
'global-context-panel',
|
||||||
'radio-panel',
|
'radio-panel',
|
||||||
]);
|
]);
|
||||||
/**
|
|
||||||
* Position keys are versioned separately from collapsed-state keys so layout
|
|
||||||
* default changes (e.g. right-rail origin) can reset positions without also
|
|
||||||
* resetting every panel's open/closed preference.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const DETECTION_ALLOCATION_STORAGE_KEY = 'gev:detection-allocation:v1';
|
const DETECTION_ALLOCATION_STORAGE_KEY = 'gev:detection-allocation:v1';
|
||||||
/** Z ladder: panels promote within [100, 139]; voice pill 150, toast 200, clean-view-exit 300. */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fixed UI regions that can occupy the left accordion's vertical lane.
|
|
||||||
* Rectangles are filtered at runtime for visibility and horizontal overlap,
|
|
||||||
* so right-side/center controls do not reduce the lane unless they actually
|
|
||||||
* intersect it at the current viewport size.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fixed UI regions that can occupy the right control lane. Runtime rectangle
|
|
||||||
* filtering keeps the rail clear of whichever HUD variant is currently
|
|
||||||
* visible without tying the layout to one screen height.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Display labels shown in the mini-status readout for each active style. */
|
/** Display labels shown in the mini-status readout for each active style. */
|
||||||
const STYLE_STATUS_LABELS = {
|
const STYLE_STATUS_LABELS = {
|
||||||
normal: 'NORMAL',
|
normal: 'NORMAL',
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
/** Own rail measurement, layout scheduling and dock tray observation. */
|
/** Own rail measurement, layout scheduling and dock tray observation. */
|
||||||
import { layoutLeftPanelRail, layoutRightPanelRail } from './panelRails.js';
|
import { layoutLeftPanelRail, layoutRightPanelRail } from './panelRails.js';
|
||||||
const COCKPIT_LAYOUT_SETTLE_MS = 240;
|
const COCKPIT_LAYOUT_SETTLE_MS = 240;
|
||||||
|
/**
|
||||||
|
* Fixed UI regions that can occupy the left accordion's vertical lane.
|
||||||
|
* Rectangles are filtered at runtime for visibility and horizontal overlap,
|
||||||
|
* so right-side/center controls do not reduce the lane unless they actually
|
||||||
|
* intersect it at the current viewport size.
|
||||||
|
*/
|
||||||
const LEFT_STACK_OBSTACLE_SELECTOR = [
|
const LEFT_STACK_OBSTACLE_SELECTOR = [
|
||||||
'#cockpit-hud .cockpit-topline',
|
'#cockpit-hud .cockpit-topline',
|
||||||
'#cockpit-hud .cockpit-topline > div',
|
'#cockpit-hud .cockpit-topline > div',
|
||||||
@@ -26,6 +32,11 @@ const LEFT_STACK_OBSTACLE_SELECTOR = [
|
|||||||
'#pp-toggles',
|
'#pp-toggles',
|
||||||
'#param-slider-panel',
|
'#param-slider-panel',
|
||||||
].join(', ');
|
].join(', ');
|
||||||
|
/**
|
||||||
|
* Fixed UI regions that can occupy the right control lane. Runtime rectangle
|
||||||
|
* filtering keeps the rail clear of whichever HUD variant is currently
|
||||||
|
* visible without tying the layout to one screen height.
|
||||||
|
*/
|
||||||
const RIGHT_STACK_OBSTACLE_SELECTOR = [
|
const RIGHT_STACK_OBSTACLE_SELECTOR = [
|
||||||
'#cockpit-hud .cockpit-topline',
|
'#cockpit-hud .cockpit-topline',
|
||||||
'#cockpit-hud .cockpit-topline > div',
|
'#cockpit-hud .cockpit-topline > div',
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
/** Own panel position preferences, viewport clamping and drag listeners. */
|
/** Own panel position preferences, viewport clamping and drag listeners. */
|
||||||
|
/** Versioned localStorage namespace prefix to invalidate stale panel layouts. */
|
||||||
const PANEL_LAYOUT_STORAGE_VERSION = 'v6';
|
const PANEL_LAYOUT_STORAGE_VERSION = 'v6';
|
||||||
|
/**
|
||||||
|
* Position keys are versioned separately from collapsed-state keys so layout
|
||||||
|
* default changes (e.g. right-rail origin) can reset positions without also
|
||||||
|
* resetting every panel's open/closed preference.
|
||||||
|
*/
|
||||||
const PANEL_POSITION_STORAGE_VERSION = 'v8';
|
const PANEL_POSITION_STORAGE_VERSION = 'v8';
|
||||||
|
/** Z ladder: panels promote within [100, 139]; voice pill 150, toast 200, clean-view-exit 300. */
|
||||||
const PANEL_Z_BASE = 100;
|
const PANEL_Z_BASE = 100;
|
||||||
const PANEL_Z_MAX = 139;
|
const PANEL_Z_MAX = 139;
|
||||||
export class PanelPositionControls {
|
export class PanelPositionControls {
|
||||||
@@ -22,6 +29,7 @@ export class PanelPositionControls {
|
|||||||
this.destroyed = false;
|
this.destroyed = false;
|
||||||
}
|
}
|
||||||
listen(target, type, callback) {
|
listen(target, type, callback) {
|
||||||
|
if (this.destroyed || !target) return;
|
||||||
const listener = (event) => {
|
const listener = (event) => {
|
||||||
if (!this.destroyed) callback(event);
|
if (!this.destroyed) callback(event);
|
||||||
};
|
};
|
||||||
@@ -29,6 +37,7 @@ export class PanelPositionControls {
|
|||||||
this.removers.push(() => target.removeEventListener(type, listener));
|
this.removers.push(() => target.removeEventListener(type, listener));
|
||||||
}
|
}
|
||||||
_reclampDraggablePanels() {
|
_reclampDraggablePanels() {
|
||||||
|
if (this.destroyed) return;
|
||||||
const el = this._ppToggles;
|
const el = this._ppToggles;
|
||||||
if (!el || !el.style.top || el.style.top === 'auto') return;
|
if (!el || !el.style.top || el.style.top === 'auto') return;
|
||||||
const top = parseInt(el.style.top, 10);
|
const top = parseInt(el.style.top, 10);
|
||||||
@@ -56,6 +65,7 @@ export class PanelPositionControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_initPanelDrag() {
|
_initPanelDrag() {
|
||||||
|
if (this.destroyed) return;
|
||||||
const dragSpecs = [
|
const dragSpecs = [
|
||||||
{
|
{
|
||||||
id: 'pp-toggles',
|
id: 'pp-toggles',
|
||||||
@@ -258,10 +268,6 @@ export class PanelPositionControls {
|
|||||||
};
|
};
|
||||||
const onUp = () => {
|
const onUp = () => {
|
||||||
cancel();
|
cancel();
|
||||||
panelEl.classList.remove('panel-dragging');
|
|
||||||
window.removeEventListener('pointermove', onMove);
|
|
||||||
window.removeEventListener('pointerup', onUp);
|
|
||||||
window.removeEventListener('pointercancel', onUp);
|
|
||||||
if (panelId === 'pp-toggles') {
|
if (panelId === 'pp-toggles') {
|
||||||
this._pinPanelToRight(panelEl);
|
this._pinPanelToRight(panelEl);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,6 +201,11 @@ test('panel disposal cancels a live drag without saving or accepting later point
|
|||||||
window.dispatchEvent(new Event('pointerup'));
|
window.dispatchEvent(new Event('pointerup'));
|
||||||
assert.deepEqual(panel.style, before);
|
assert.deepEqual(panel.style, before);
|
||||||
assert.equal(panel.classList.contains('panel-dragging'), false);
|
assert.equal(panel.classList.contains('panel-dragging'), false);
|
||||||
|
owner._makePanelDraggable('sample', panel, handle);
|
||||||
|
owner._initPanelDrag();
|
||||||
|
owner._ppToggles.style.top = '10000px';
|
||||||
|
owner._reclampDraggablePanels();
|
||||||
|
assert.equal(owner._ppToggles.style.top, '10000px');
|
||||||
assert.equal(owner.removers.length, 0);
|
assert.equal(owner.removers.length, 0);
|
||||||
assert.deepEqual(f.writes, []);
|
assert.deepEqual(f.writes, []);
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in new issue
Block a user