v2.5.0: add admin auto refresh toggle

This commit is contained in:
Виталий Литвинов
2026-04-25 16:38:19 +03:00
parent 98e4be8831
commit cae552ec87
4 changed files with 126 additions and 2 deletions

View File

@@ -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();

View File

@@ -11,7 +11,7 @@
document.documentElement.dataset.theme = theme;
}());
</script>
<link rel="stylesheet" href="/styles.css?v=2.5.0-admin8">
<link rel="stylesheet" href="/styles.css?v=2.5.0-admin18">
</head>
<body>
<div class="app-shell">
@@ -54,6 +54,11 @@
</select>
<button id="themeToggle" class="ghost" type="button">Theme</button>
<button id="refreshBtn" type="button" data-i18n="refresh">Refresh</button>
<button id="autoRefreshToggle" class="auto-refresh-toggle" type="button" aria-pressed="true" data-i18n-title="autoRefresh" title="Auto refresh">
<span class="auto-refresh-icon" aria-hidden="true"></span>
<span class="auto-refresh-track" aria-hidden="true"><span></span></span>
<span class="auto-refresh-state">5s</span>
</button>
</div>
</header>
@@ -388,6 +393,6 @@
</div>
</div>
</div>
<script src="/app.js?v=2.5.0-admin17" type="module"></script>
<script src="/app.js?v=2.5.0-admin18" type="module"></script>
</body>
</html>

View File

@@ -228,6 +228,65 @@ h2 {
flex-wrap: wrap;
}
.auto-refresh-toggle {
display: inline-flex;
align-items: center;
gap: 8px;
min-height: 42px;
padding: 7px 10px;
border: 1px solid var(--line);
background: var(--panel-strong);
color: var(--text);
}
.auto-refresh-toggle:hover {
transform: translateY(-1px);
}
.auto-refresh-icon {
color: var(--blue);
font-size: 17px;
line-height: 1;
}
.auto-refresh-track {
position: relative;
display: inline-flex;
align-items: center;
width: 38px;
height: 22px;
border-radius: 999px;
background: color-mix(in srgb, var(--muted) 28%, transparent);
transition: background .16s ease;
}
.auto-refresh-track span {
position: absolute;
left: 3px;
width: 16px;
height: 16px;
border-radius: 999px;
background: var(--panel);
box-shadow: 0 2px 8px rgba(15, 23, 42, .18);
transition: transform .16s ease;
}
.auto-refresh-toggle.active .auto-refresh-track {
background: color-mix(in srgb, var(--green) 58%, transparent);
}
.auto-refresh-toggle.active .auto-refresh-track span {
transform: translateX(16px);
}
.auto-refresh-state {
min-width: 22px;
color: var(--muted);
font-size: 12px;
font-weight: 800;
text-transform: uppercase;
}
.language-select {
width: 78px;
min-width: 78px;

View File

@@ -199,6 +199,19 @@ class AdminFeatureTests(unittest.TestCase):
self.assertIn('class="keys-list" id="usersTable"', index)
self.assertNotIn('class="keys-table"', index)
def test_topbar_has_five_second_auto_refresh_toggle(self):
app_js = (ROOT / "admin-web" / "static" / "app.js").read_text(encoding="utf-8")
styles = (ROOT / "admin-web" / "static" / "styles.css").read_text(encoding="utf-8")
index = (ROOT / "admin-web" / "static" / "index.html").read_text(encoding="utf-8")
self.assertIn('id="autoRefreshToggle"', index)
self.assertIn('data-i18n-title="autoRefresh"', index)
self.assertIn("gotelegram-auto-refresh", app_js)
self.assertIn("AUTO_REFRESH_MS = 5000", app_js)
self.assertIn("setInterval", app_js)
self.assertIn("clearInterval", app_js)
self.assertIn(".auto-refresh-toggle", styles)
if __name__ == "__main__":
unittest.main()