Files
gods-eye-view/src/ui/uiLifetime.js
T
Sameh Khamis b20c80e590 Switch language in place, without a reload
Choosing a language in the picker now applies it at once. The choice is
stored and ?lang is kept in step through replaceState, and the picker keeps
focus.

Code that writes translated text itself re-presents it on a switch:
onLocaleChange() lets an owner listen, removed at teardown, and status
writers hand over a function of the locale (voice cards, scene director,
SDR, draw hints, imagery split, map sources, Bhote Koshi media) so the
words are rebuilt rather than re-sent. The HUD, cockpit, radio, CCTV,
launch, awareness and credit texts follow the same hook, and
applyDocumentTranslations can be applied again without undoing text code
wrote since.

The unsaved-work registry and persistLocaleAndReload existed only to guard
the reload, so they go, with their catalog entries.
2026-11-01 16:47:39 -08:00

64 lines
1.7 KiB
JavaScript

/** Own deferred UI presentation and listeners until synchronous teardown. */
export class UiLifetime {
constructor() {
this.destroyed = false;
this.removers = new Set();
this.frames = new Set();
this.timers = new Set();
}
listen(target, type, callback, options) {
if (this.destroyed || !target) return () => {};
const remove = () => {
target.removeEventListener(type, listener, options);
this.removers.delete(remove);
};
const listener = (event) => {
if (options?.once) remove();
if (!this.destroyed) return callback(event);
};
target.addEventListener(type, listener, options);
this.removers.add(remove);
return remove;
}
/** Run `remove` at teardown, or now when already torn down. */
own(remove) {
if (this.destroyed) {
remove();
return remove;
}
this.removers.add(remove);
return remove;
}
frame(callback) {
if (this.destroyed) return null;
const frame = requestAnimationFrame((time) => {
this.frames.delete(frame);
if (!this.destroyed) callback(time);
});
this.frames.add(frame);
return frame;
}
timeout(callback, delay) {
if (this.destroyed) return null;
const timer = setTimeout(() => {
this.timers.delete(timer);
if (!this.destroyed) callback();
}, delay);
this.timers.add(timer);
return timer;
}
cancelTimeout(timer) {
clearTimeout(timer);
this.timers.delete(timer);
}
destroy() {
if (this.destroyed) return;
this.destroyed = true;
for (const remove of this.removers) remove();
for (const frame of this.frames) cancelAnimationFrame(frame);
for (const timer of this.timers) clearTimeout(timer);
this.frames.clear();
this.timers.clear();
}
}