Files
rclone_gui/webgui/web/assets/js/app.js
T
2026-06-20 13:54:22 +08:00

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=job-layout-1";
import { t, currentLocale, setLocale, onLocale } from "./i18n.js?v=job-layout-1";
import { renderRemotes } from "./views/remotes.js?v=job-layout-1";
import { renderBrowse } from "./views/browser.js?v=job-layout-1";
import { renderJobs, renderNewJob, stopJobPolling } from "./views/jobs.js?v=job-layout-1";
import {
renderConfigureNew,
renderConfigureEdit,
} from "./views/configure.js?v=job-layout-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();