97 lines
3.1 KiB
JavaScript
97 lines
3.1 KiB
JavaScript
// app.js — entry point. Routes hash changes to view renderers, keeps
|
|
// the top-nav active state in sync, and re-renders chrome strings when
|
|
// the locale changes.
|
|
|
|
import { escapeHtml, onRoute } from "./state.js?v=security-fixes-1";
|
|
import { t, currentLocale, setLocale, onLocale } from "./i18n.js?v=security-fixes-1";
|
|
import { renderRemotes } from "./views/remotes.js?v=security-fixes-1";
|
|
import { renderBrowse } from "./views/browser.js?v=security-fixes-1";
|
|
import { renderJobs, renderNewJob, stopJobPolling } from "./views/jobs.js?v=security-fixes-1";
|
|
import {
|
|
renderConfigureNew,
|
|
renderConfigureEdit,
|
|
} from "./views/configure.js?v=security-fixes-1";
|
|
|
|
const views = {
|
|
remotes: renderRemotes,
|
|
browse: renderBrowse,
|
|
jobs: renderJobs,
|
|
"jobs-new": renderNewJob,
|
|
"configure-new": renderConfigureNew,
|
|
"configure-edit": renderConfigureEdit,
|
|
};
|
|
|
|
let currentRoute = { name: "remotes", params: {} };
|
|
|
|
function setActiveNav(routeName) {
|
|
const links = document.querySelectorAll("#nav-links a");
|
|
for (const a of links) {
|
|
const target = a.dataset.route;
|
|
let isActive = target === routeName;
|
|
if (target === "configure" && routeName.startsWith("configure-")) {
|
|
isActive = true;
|
|
}
|
|
a.classList.toggle("active", isActive);
|
|
}
|
|
}
|
|
|
|
function applyChromeStrings() {
|
|
// nav links
|
|
document.querySelectorAll("#nav-links a").forEach((a) => {
|
|
const key = a.dataset.route === "configure" ? "nav.configure" : `nav.${a.dataset.route}`;
|
|
a.textContent = t(key);
|
|
});
|
|
const newJobBtn = document.querySelector(".nav-new-job");
|
|
if (newJobBtn) newJobBtn.textContent = t("nav.new_job");
|
|
const lt = document.getElementById("loading-title");
|
|
if (lt) lt.textContent = t("loading.title");
|
|
const lb = document.getElementById("loading-body");
|
|
if (lb) lb.innerHTML = t("loading.connecting");
|
|
// Anything with data-i18n attribute (footer)
|
|
document.querySelectorAll("[data-i18n]").forEach((el) => {
|
|
el.textContent = t(el.dataset.i18n);
|
|
});
|
|
// lang buttons
|
|
const locale = currentLocale();
|
|
document.querySelectorAll(".lang-btn").forEach((btn) => {
|
|
btn.classList.toggle("active", btn.dataset.lang === locale);
|
|
});
|
|
}
|
|
|
|
function wireLangSwitcher() {
|
|
document.querySelectorAll(".lang-btn").forEach((btn) => {
|
|
btn.addEventListener("click", () => setLocale(btn.dataset.lang));
|
|
});
|
|
}
|
|
|
|
onRoute((route) => {
|
|
currentRoute = route;
|
|
setActiveNav(route.name);
|
|
|
|
if (route.name !== "jobs") {
|
|
stopJobPolling();
|
|
}
|
|
|
|
const renderer = views[route.name] || renderRemotes;
|
|
renderer(route.params).catch((e) => {
|
|
console.error("view error", e);
|
|
const app = document.getElementById("app");
|
|
if (app) {
|
|
app.innerHTML = `<div class="empty"><h3>${t("error.title")}</h3><p>${escapeHtml(e.message)}</p></div>`;
|
|
}
|
|
});
|
|
});
|
|
|
|
// Re-render everything on locale change so views pick up new strings.
|
|
onLocale(() => {
|
|
applyChromeStrings();
|
|
const renderer = views[currentRoute.name] || renderRemotes;
|
|
renderer(currentRoute.params).catch((e) => {
|
|
console.error("view error on locale change", e);
|
|
});
|
|
});
|
|
|
|
// Initial chrome paint + switcher wiring.
|
|
applyChromeStrings();
|
|
wireLangSwitcher();
|