diff --git a/admin-web/static/app.js b/admin-web/static/app.js index c11b4ab..a95b128 100644 --- a/admin-web/static/app.js +++ b/admin-web/static/app.js @@ -11,6 +11,10 @@ const i18n = { navLogs: "Logs", navSettings: "Settings", refresh: "Refresh", + autoRefresh: "Auto refresh every 5 seconds", + autoRefreshOn: "Auto refresh is on", + autoRefreshOff: "Auto refresh is off", + autoRefreshOffShort: "off", themeDark: "Dark", themeLight: "Light", metricMode: "Mode", @@ -234,6 +238,10 @@ const i18n = { navLogs: "Логи", navSettings: "Настройки", refresh: "Обновить", + autoRefresh: "Автообновление каждые 5 секунд", + autoRefreshOn: "Автообновление включено", + autoRefreshOff: "Автообновление выключено", + autoRefreshOffShort: "выкл", themeDark: "Тёмная", themeLight: "Светлая", metricMode: "Режим", @@ -469,11 +477,15 @@ const state = { backupSchedule: null, qrLink: "", pendingUsers: new Set(), + refreshingAll: false, + autoRefreshEnabled: localStorage.getItem("gotelegram-auto-refresh") !== "0", }; const t = (key) => (i18n[state.lang] && i18n[state.lang][key]) || i18n.en[key] || key; const trafficRanges = ["15m", "1h", "24h", "month"]; +const AUTO_REFRESH_MS = 5000; +let autoRefreshTimer = null; const fmtBytes = (value = 0) => { const units = ["B", "KB", "MB", "GB", "TB"]; @@ -527,6 +539,34 @@ const addEvent = (title, detail = "") => { renderEvents(); }; +function updateAutoRefreshToggle() { + const button = $("#autoRefreshToggle"); + if (!button) return; + button.classList.toggle("active", state.autoRefreshEnabled); + button.setAttribute("aria-pressed", String(state.autoRefreshEnabled)); + button.title = state.autoRefreshEnabled ? t("autoRefreshOn") : t("autoRefreshOff"); + const label = button.querySelector(".auto-refresh-state"); + if (label) label.textContent = state.autoRefreshEnabled ? "5s" : t("autoRefreshOffShort"); +} + +function syncAutoRefreshTimer() { + if (autoRefreshTimer) { + clearInterval(autoRefreshTimer); + autoRefreshTimer = null; + } + if (!state.autoRefreshEnabled) return; + autoRefreshTimer = setInterval(() => { + refreshAll().catch((err) => toast(err.message)); + }, AUTO_REFRESH_MS); +} + +function setAutoRefresh(enabled) { + state.autoRefreshEnabled = Boolean(enabled); + localStorage.setItem("gotelegram-auto-refresh", state.autoRefreshEnabled ? "1" : "0"); + updateAutoRefreshToggle(); + syncAutoRefreshTimer(); +} + async function api(path, options = {}) { const headers = { "Accept": "application/json", @@ -564,6 +604,7 @@ function applyI18n() { updateUserTrafficControls(); renderBackupSchedule(); updatePageTitle(); + updateAutoRefreshToggle(); } function setTheme(theme) { @@ -1271,6 +1312,8 @@ function renderConfig() { } async function refreshAll() { + if (state.refreshingAll) return; + state.refreshingAll = true; const btn = $("#refreshBtn"); btn.disabled = true; try { @@ -1305,6 +1348,8 @@ async function refreshAll() { toast(err.message); } finally { btn.disabled = false; + state.refreshingAll = false; + updateAutoRefreshToggle(); } } @@ -1684,6 +1729,7 @@ document.addEventListener("submit", (eventObj) => { }); $("#refreshBtn").addEventListener("click", refreshAll); +$("#autoRefreshToggle").addEventListener("click", () => setAutoRefresh(!state.autoRefreshEnabled)); $("#languageSelect").addEventListener("change", (eventObj) => setLanguage(eventObj.target.value)); $("#promoClose").addEventListener("click", () => { $("#promoModal").hidden = true; @@ -1703,6 +1749,7 @@ window.addEventListener("hashchange", () => setPage((location.hash || "#dashboar setPage((location.hash || "#dashboard").slice(1), false); setTheme(state.theme); renderEvents(); +syncAutoRefreshTimer(); refreshAll(); loadLogs(); maybeShowPromo(); diff --git a/admin-web/static/index.html b/admin-web/static/index.html index 67dce75..69eb042 100644 --- a/admin-web/static/index.html +++ b/admin-web/static/index.html @@ -11,7 +11,7 @@ document.documentElement.dataset.theme = theme; }()); - +