[fix](trx-frontend-http): fix about tab data and sub-tab navigation broken by deferred templates
About-tab element refs were cached at script load time but the elements live inside a <template> that hasn't been cloned yet, so all refs were null. Convert to lazy resolution via _resolveAboutEls() called on first about-tab render. Also extract _wireSubTabBar() so sub-tab click listeners are attached after template cloning (the about Client sub-tab was unreachable). Decoder status elements use the same lazy pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -435,29 +435,57 @@ const headerRigSwitchSelect = document.getElementById("header-rig-switch-select"
|
|||||||
const headerStylePickSelect = document.getElementById("header-style-pick-select");
|
const headerStylePickSelect = document.getElementById("header-style-pick-select");
|
||||||
const rdsPsOverlay = document.getElementById("rds-ps-overlay");
|
const rdsPsOverlay = document.getElementById("rds-ps-overlay");
|
||||||
const tabMainEl = document.getElementById("tab-main");
|
const tabMainEl = document.getElementById("tab-main");
|
||||||
// Cached About-tab elements (avoid getElementById on every SSE render)
|
// About-tab elements — resolved lazily after the about template is cloned.
|
||||||
const aboutServerVerEl = document.getElementById("about-server-ver");
|
let aboutServerVerEl = null;
|
||||||
const aboutServerBuildDateEl = document.getElementById("about-server-build-date");
|
let aboutServerBuildDateEl = null;
|
||||||
const aboutServerAddrEl = document.getElementById("about-server-addr");
|
let aboutServerAddrEl = null;
|
||||||
const aboutServerCallEl = document.getElementById("about-server-call");
|
let aboutServerCallEl = null;
|
||||||
const aboutServerLocationEl = document.getElementById("about-server-location");
|
let aboutServerLocationEl = null;
|
||||||
const aboutRigInfoEl = document.getElementById("about-rig-info");
|
let aboutRigInfoEl = null;
|
||||||
const aboutRigAccessEl = document.getElementById("about-rig-access");
|
let aboutRigAccessEl = null;
|
||||||
const aboutModesEl = document.getElementById("about-modes");
|
let aboutModesEl = null;
|
||||||
const aboutVfosEl = document.getElementById("about-vfos");
|
let aboutVfosEl = null;
|
||||||
const aboutActiveRigEl = document.getElementById("about-active-rig");
|
let aboutActiveRigEl = null;
|
||||||
const aboutAudioCodecEl = document.getElementById("about-audio-codec");
|
let aboutAudioCodecEl = null;
|
||||||
const aboutAudioSamplerateEl = document.getElementById("about-audio-samplerate");
|
let aboutAudioSamplerateEl = null;
|
||||||
const aboutAudioChannelsEl = document.getElementById("about-audio-channels");
|
let aboutAudioChannelsEl = null;
|
||||||
const aboutAudioBitrateEl = document.getElementById("about-audio-bitrate");
|
let aboutAudioBitrateEl = null;
|
||||||
const aboutAudioFrameEl = document.getElementById("about-audio-frame");
|
let aboutAudioFrameEl = null;
|
||||||
const aboutAudioRxEl = document.getElementById("about-audio-rx");
|
let aboutAudioRxEl = null;
|
||||||
const aboutAudioStreamsEl = document.getElementById("about-audio-streams");
|
let aboutAudioStreamsEl = null;
|
||||||
const aboutPskreporterEl = document.getElementById("about-pskreporter");
|
let aboutPskreporterEl = null;
|
||||||
const aboutAprsIsEl = document.getElementById("about-aprs-is");
|
let aboutAprsIsEl = null;
|
||||||
const aboutRigctlClientsEl = document.getElementById("about-rigctl-clients");
|
let aboutRigctlClientsEl = null;
|
||||||
const aboutRigctlEndpointEl = document.getElementById("about-rigctl-endpoint");
|
let aboutRigctlEndpointEl = null;
|
||||||
const aboutClientsEl = document.getElementById("about-clients");
|
let aboutClientsEl = null;
|
||||||
|
let _aboutElsResolved = false;
|
||||||
|
function _resolveAboutEls() {
|
||||||
|
if (_aboutElsResolved) return;
|
||||||
|
aboutServerVerEl = document.getElementById("about-server-ver");
|
||||||
|
if (!aboutServerVerEl) return; // template not cloned yet
|
||||||
|
_aboutElsResolved = true;
|
||||||
|
aboutServerBuildDateEl = document.getElementById("about-server-build-date");
|
||||||
|
aboutServerAddrEl = document.getElementById("about-server-addr");
|
||||||
|
aboutServerCallEl = document.getElementById("about-server-call");
|
||||||
|
aboutServerLocationEl = document.getElementById("about-server-location");
|
||||||
|
aboutRigInfoEl = document.getElementById("about-rig-info");
|
||||||
|
aboutRigAccessEl = document.getElementById("about-rig-access");
|
||||||
|
aboutModesEl = document.getElementById("about-modes");
|
||||||
|
aboutVfosEl = document.getElementById("about-vfos");
|
||||||
|
aboutActiveRigEl = document.getElementById("about-active-rig");
|
||||||
|
aboutAudioCodecEl = document.getElementById("about-audio-codec");
|
||||||
|
aboutAudioSamplerateEl = document.getElementById("about-audio-samplerate");
|
||||||
|
aboutAudioChannelsEl = document.getElementById("about-audio-channels");
|
||||||
|
aboutAudioBitrateEl = document.getElementById("about-audio-bitrate");
|
||||||
|
aboutAudioFrameEl = document.getElementById("about-audio-frame");
|
||||||
|
aboutAudioRxEl = document.getElementById("about-audio-rx");
|
||||||
|
aboutAudioStreamsEl = document.getElementById("about-audio-streams");
|
||||||
|
aboutPskreporterEl = document.getElementById("about-pskreporter");
|
||||||
|
aboutAprsIsEl = document.getElementById("about-aprs-is");
|
||||||
|
aboutRigctlClientsEl = document.getElementById("about-rigctl-clients");
|
||||||
|
aboutRigctlEndpointEl = document.getElementById("about-rigctl-endpoint");
|
||||||
|
aboutClientsEl = document.getElementById("about-clients");
|
||||||
|
}
|
||||||
// Cached CW elements (avoid getElementById on every SSE render)
|
// Cached CW elements (avoid getElementById on every SSE render)
|
||||||
const cwAutoEl = document.getElementById("cw-auto");
|
const cwAutoEl = document.getElementById("cw-auto");
|
||||||
const cwWpmEl = document.getElementById("cw-wpm");
|
const cwWpmEl = document.getElementById("cw-wpm");
|
||||||
@@ -488,11 +516,18 @@ function syncDecoderToggle(entry, enabled, label) {
|
|||||||
entry.el.style.color = enabled ? "#00d17f" : "";
|
entry.el.style.color = enabled ? "#00d17f" : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cached About-tab decoder status elements — avoids 8× getElementById per render().
|
// About-tab decoder status elements — resolved lazily after template clone.
|
||||||
const _aboutDecEls = [
|
const _aboutDecIds = [
|
||||||
"about-dec-ft8", "about-dec-ft4", "about-dec-ft2", "about-dec-wspr",
|
"about-dec-ft8", "about-dec-ft4", "about-dec-ft2", "about-dec-wspr",
|
||||||
"about-dec-cw", "about-dec-aprs", "about-dec-lrpt",
|
"about-dec-cw", "about-dec-aprs", "about-dec-lrpt",
|
||||||
].map((id) => ({ el: document.getElementById(id), last: null }));
|
];
|
||||||
|
let _aboutDecEls = _aboutDecIds.map(() => ({ el: null, last: null }));
|
||||||
|
function _resolveAboutDecEls() {
|
||||||
|
if (_aboutDecEls[0].el) return;
|
||||||
|
for (let i = 0; i < _aboutDecIds.length; i++) {
|
||||||
|
_aboutDecEls[i].el = document.getElementById(_aboutDecIds[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function syncAboutDecoder(idx, enabled) {
|
function syncAboutDecoder(idx, enabled) {
|
||||||
const entry = _aboutDecEls[idx];
|
const entry = _aboutDecEls[idx];
|
||||||
@@ -3511,7 +3546,9 @@ function render(update) {
|
|||||||
if (typeof update.clients === "number") lastClientCount = update.clients;
|
if (typeof update.clients === "number") lastClientCount = update.clients;
|
||||||
// Populate About tab — only update DOM when the about tab is visible
|
// Populate About tab — only update DOM when the about tab is visible
|
||||||
if (_activeTab === "about") {
|
if (_activeTab === "about") {
|
||||||
// About — Server card (uses cached DOM refs)
|
_resolveAboutEls();
|
||||||
|
_resolveAboutDecEls();
|
||||||
|
// About — Server card
|
||||||
if (update.server_version && aboutServerVerEl) {
|
if (update.server_version && aboutServerVerEl) {
|
||||||
aboutServerVerEl.textContent = `trx-server v${update.server_version}`;
|
aboutServerVerEl.textContent = `trx-server v${update.server_version}`;
|
||||||
}
|
}
|
||||||
@@ -4451,6 +4488,10 @@ function navigateToTab(name, options = {}) {
|
|||||||
if (tmpl) {
|
if (tmpl) {
|
||||||
panel.appendChild(tmpl.content.cloneNode(true));
|
panel.appendChild(tmpl.content.cloneNode(true));
|
||||||
tmpl.remove();
|
tmpl.remove();
|
||||||
|
// Wire sub-tab bars inside the freshly cloned content.
|
||||||
|
panel.querySelectorAll(".sub-tab-bar").forEach(_wireSubTabBar);
|
||||||
|
// Re-run decoder visibility for about-tab elements now in the DOM.
|
||||||
|
if (decoderRegistry.length) hideUnsupportedDecoderTabs();
|
||||||
}
|
}
|
||||||
if (updateHistory) {
|
if (updateHistory) {
|
||||||
updateTabHistory(name, replaceHistory);
|
updateTabHistory(name, replaceHistory);
|
||||||
@@ -4825,7 +4866,9 @@ function latLonToMaidenhead(lat, lon) {
|
|||||||
|
|
||||||
|
|
||||||
// --- Sub-tab navigation ---
|
// --- Sub-tab navigation ---
|
||||||
document.querySelectorAll(".sub-tab-bar").forEach((bar) => {
|
function _wireSubTabBar(bar) {
|
||||||
|
if (bar._subtabWired) return;
|
||||||
|
bar._subtabWired = true;
|
||||||
bar.addEventListener("click", (e) => {
|
bar.addEventListener("click", (e) => {
|
||||||
const btn = e.target.closest(".sub-tab[data-subtab]");
|
const btn = e.target.closest(".sub-tab[data-subtab]");
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
@@ -4845,7 +4888,8 @@ document.querySelectorAll(".sub-tab-bar").forEach((bar) => {
|
|||||||
window.clearSatPredictionDom();
|
window.clearSatPredictionDom();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
document.querySelectorAll(".sub-tab-bar").forEach(_wireSubTabBar);
|
||||||
|
|
||||||
window.addEventListener("resize", () => {
|
window.addEventListener("resize", () => {
|
||||||
const mapTab = document.getElementById("tab-map");
|
const mapTab = document.getElementById("tab-map");
|
||||||
|
|||||||
Reference in New Issue
Block a user