mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 15:52:21 +00:00
Create the application once per page and switch engines through the scene host
createStandaloneApplication opens its first globe, attaches the scene host and returns attachScene/detachScene: a switch hands the session off, parks the outgoing renderer and attaches the incoming one with its catalog to the parts the phases registered once. A failed candidate releases what it attached; the working engine attaches again on the same shell, voice, tools and stores. ?switch=legacy keeps the route that builds an application per engine for one release. scripts/qa-app-once.mjs now asserts identity, UI, tool and listener continuity, the rollback and the legacy route.
This commit is contained in:
1 parent
2bac200c01
commit
a99b862580
6 files changed
+533
-81
No files matched your search
+61
-21
@@ -97,11 +97,19 @@ function liveComponents() {
|
||||
: null;
|
||||
}
|
||||
|
||||
// The page's application is created once; a switch detaches and attaches
|
||||
// only its engine-bound parts. `?switch=legacy` keeps the previous route, one
|
||||
// application per engine, for one release.
|
||||
const legacySwitch =
|
||||
new URLSearchParams(globalThis.location?.search ?? '').get('switch') ===
|
||||
'legacy';
|
||||
|
||||
// Hand the session off and release the running engine. Its renderer is
|
||||
// parked for a later switch back.
|
||||
async function leave(from) {
|
||||
const components = liveComponents();
|
||||
if (components) {
|
||||
const attached = Boolean(components?.scene.engine);
|
||||
if (attached) {
|
||||
cover ??= keepGlobeFrame(components.scene.engine);
|
||||
cockpit = components.controls.styleManager.getCockpitState();
|
||||
handOffSession(layerSession, {
|
||||
@@ -111,36 +119,68 @@ async function leave(from) {
|
||||
});
|
||||
renderers.retain(from);
|
||||
}
|
||||
await application?.destroy();
|
||||
if (legacySwitch) await application?.destroy();
|
||||
else if (attached) await application.detachScene({ handoff: true });
|
||||
session.count('detaches');
|
||||
}
|
||||
|
||||
// Start an engine on the retained session. Success means the engine runs
|
||||
// with the session attached and the address's view restored; its first frame
|
||||
// only decides when the cover goes (settle).
|
||||
async function enter(kind) {
|
||||
async function enter(kind, { recovery = false } = {}) {
|
||||
if (legacySwitch) return enterLegacy(kind);
|
||||
if (!application || application.getState().status !== 'ready') {
|
||||
// The first engine never started: there is no application to attach to.
|
||||
return enterLegacy(kind);
|
||||
}
|
||||
layerSession.restoringView = true;
|
||||
try {
|
||||
await application.attachScene(kind);
|
||||
signal.throwIfAborted();
|
||||
await resume(application.getComponents().controls.styleManager);
|
||||
if (recovery) session.count('rollbacks');
|
||||
} catch (error) {
|
||||
// pagehide owns the teardown once the lifetime is aborted. A failed
|
||||
// candidate is detached; the session stays for the working engine.
|
||||
if (!signal.aborted)
|
||||
await application
|
||||
.detachScene({ handoff: true })
|
||||
.catch((cleanupError) =>
|
||||
console.error('A failed globe could not be released:', cleanupError),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// The session continues on the engine that attached: the view is restored,
|
||||
// parked work resumes and Cockpit re-enters on the aircraft it had.
|
||||
async function resume(style) {
|
||||
await style.initialRestorePromise;
|
||||
signal.throwIfAborted();
|
||||
layerSession.restoringView = false;
|
||||
session.count('attachments');
|
||||
resumeSession(layerSession);
|
||||
// Only resume the aircraft that actually survived the handoff. A history
|
||||
// entry may select a different subject, and a stale track may disappear.
|
||||
const target = style.getAircraftTrackingTarget();
|
||||
if (
|
||||
cockpit?.active &&
|
||||
target &&
|
||||
target?.id === cockpit.subject?.id &&
|
||||
target?.layerId === cockpit.subject?.layerId &&
|
||||
style.controlCockpit('enter').ok
|
||||
) {
|
||||
style.cockpitView.setVisionMode(cockpit.visionMode);
|
||||
}
|
||||
}
|
||||
|
||||
// The legacy route: a new application on the retained session.
|
||||
async function enterLegacy(kind) {
|
||||
const app = createApplication(kind, true);
|
||||
try {
|
||||
const { controls } = await app.start();
|
||||
signal.throwIfAborted();
|
||||
const style = controls.styleManager;
|
||||
await style.initialRestorePromise;
|
||||
signal.throwIfAborted();
|
||||
layerSession.restoringView = false;
|
||||
session.count('attachments');
|
||||
resumeSession(layerSession);
|
||||
// Only resume the aircraft that actually survived the handoff. A history
|
||||
// entry may select a different subject, and a stale track may disappear.
|
||||
const target = style.getAircraftTrackingTarget();
|
||||
if (
|
||||
cockpit?.active &&
|
||||
target &&
|
||||
target?.id === cockpit.subject?.id &&
|
||||
target?.layerId === cockpit.subject?.layerId &&
|
||||
style.controlCockpit('enter').ok
|
||||
) {
|
||||
style.cockpitView.setVisionMode(cockpit.visionMode);
|
||||
}
|
||||
await resume(controls.styleManager);
|
||||
} catch (error) {
|
||||
// pagehide owns the teardown once the lifetime is aborted.
|
||||
if (!signal.aborted)
|
||||
|
||||
Reference in new issue
Block a user