mirror of
https://github.com/andrey271192/amnezia_web-PRO.git
synced 2026-09-20 14:42:00 +00:00
fix(ui): show server clock in browser TZ; sync button refetches
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8,6 +8,7 @@ const logoutBtn = document.querySelector("#logout");
|
|||||||
const refreshBtn = document.querySelector("#refresh");
|
const refreshBtn = document.querySelector("#refresh");
|
||||||
const clockServerEl = document.querySelector("#clock-server");
|
const clockServerEl = document.querySelector("#clock-server");
|
||||||
const clockLocalEl = document.querySelector("#clock-local");
|
const clockLocalEl = document.querySelector("#clock-local");
|
||||||
|
const clockSyncBtn = document.querySelector("#clock-sync");
|
||||||
const rowsEl = document.querySelector("#rows");
|
const rowsEl = document.querySelector("#rows");
|
||||||
const statusEl = document.querySelector("#status");
|
const statusEl = document.querySelector("#status");
|
||||||
const peerCountEl = document.querySelector("#peer-count");
|
const peerCountEl = document.querySelector("#peer-count");
|
||||||
@@ -216,6 +217,10 @@ refreshBtn.addEventListener("click", () => {
|
|||||||
loadClients();
|
loadClients();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
clockSyncBtn.addEventListener("click", () => {
|
||||||
|
void refreshServerClock();
|
||||||
|
});
|
||||||
|
|
||||||
const clockFmt = new Intl.DateTimeFormat("ru-RU", {
|
const clockFmt = new Intl.DateTimeFormat("ru-RU", {
|
||||||
dateStyle: "medium",
|
dateStyle: "medium",
|
||||||
timeStyle: "medium",
|
timeStyle: "medium",
|
||||||
@@ -226,16 +231,45 @@ let clockTickId = null;
|
|||||||
/** @type {ReturnType<typeof setInterval> | null} */
|
/** @type {ReturnType<typeof setInterval> | null} */
|
||||||
let clockServerPollId = null;
|
let clockServerPollId = null;
|
||||||
|
|
||||||
|
/** Метка UTC сервера (мс) по последнему ответу API */
|
||||||
|
let serverAnchorUtcMs = /** @type {number | null} */ (null);
|
||||||
|
/** Date.now() в момент установки якоря (компенсация сети только между опросами) */
|
||||||
|
let serverAnchorWallMs = 0;
|
||||||
|
|
||||||
|
function browserTimeZoneLabel() {
|
||||||
|
try {
|
||||||
|
return Intl.DateTimeFormat().resolvedOptions().timeZone || "";
|
||||||
|
} catch {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tickServerClockDisplay() {
|
||||||
|
if (serverAnchorUtcMs === null) {
|
||||||
|
clockServerEl.dateTime = "";
|
||||||
|
clockServerEl.textContent = "—";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const estimatedUtcMs = serverAnchorUtcMs + (Date.now() - serverAnchorWallMs);
|
||||||
|
const d = new Date(estimatedUtcMs);
|
||||||
|
clockServerEl.dateTime = d.toISOString();
|
||||||
|
const tz = browserTimeZoneLabel();
|
||||||
|
clockServerEl.textContent = tz ? `${clockFmt.format(d)} · ${tz}` : clockFmt.format(d);
|
||||||
|
}
|
||||||
|
|
||||||
async function refreshServerClock() {
|
async function refreshServerClock() {
|
||||||
try {
|
try {
|
||||||
const t = await api("/api/server-time");
|
const t = await api("/api/server-time");
|
||||||
const iso = typeof t.iso === "string" ? t.iso : "";
|
const iso = typeof t.iso === "string" ? t.iso : "";
|
||||||
clockServerEl.dateTime = iso;
|
const parsed = new Date(iso).getTime();
|
||||||
const tz = typeof t.timeZone === "string" ? t.timeZone : "";
|
if (!iso || Number.isNaN(parsed)) {
|
||||||
const formatted = typeof t.formatted === "string" ? t.formatted : "";
|
throw new Error("нет времени");
|
||||||
clockServerEl.textContent =
|
}
|
||||||
formatted.trim() ? `${formatted}${tz ? ` · ${tz}` : ""}` : "—";
|
serverAnchorUtcMs = parsed;
|
||||||
|
serverAnchorWallMs = Date.now();
|
||||||
|
tickServerClockDisplay();
|
||||||
} catch {
|
} catch {
|
||||||
|
serverAnchorUtcMs = null;
|
||||||
clockServerEl.dateTime = "";
|
clockServerEl.dateTime = "";
|
||||||
clockServerEl.textContent = "—";
|
clockServerEl.textContent = "—";
|
||||||
}
|
}
|
||||||
@@ -244,7 +278,13 @@ async function refreshServerClock() {
|
|||||||
function tickLocalClock() {
|
function tickLocalClock() {
|
||||||
const n = new Date();
|
const n = new Date();
|
||||||
clockLocalEl.dateTime = n.toISOString();
|
clockLocalEl.dateTime = n.toISOString();
|
||||||
clockLocalEl.textContent = clockFmt.format(n);
|
const tz = browserTimeZoneLabel();
|
||||||
|
clockLocalEl.textContent = tz ? `${clockFmt.format(n)} · ${tz}` : clockFmt.format(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tickClocks() {
|
||||||
|
tickLocalClock();
|
||||||
|
tickServerClockDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopClocks() {
|
function stopClocks() {
|
||||||
@@ -256,6 +296,8 @@ function stopClocks() {
|
|||||||
clearInterval(clockServerPollId);
|
clearInterval(clockServerPollId);
|
||||||
clockServerPollId = null;
|
clockServerPollId = null;
|
||||||
}
|
}
|
||||||
|
serverAnchorUtcMs = null;
|
||||||
|
serverAnchorWallMs = 0;
|
||||||
clockServerEl.dateTime = "";
|
clockServerEl.dateTime = "";
|
||||||
clockLocalEl.dateTime = "";
|
clockLocalEl.dateTime = "";
|
||||||
clockServerEl.textContent = "—";
|
clockServerEl.textContent = "—";
|
||||||
@@ -264,9 +306,9 @@ function stopClocks() {
|
|||||||
|
|
||||||
function startClocks() {
|
function startClocks() {
|
||||||
stopClocks();
|
stopClocks();
|
||||||
tickLocalClock();
|
tickClocks();
|
||||||
void refreshServerClock();
|
void refreshServerClock();
|
||||||
clockTickId = setInterval(tickLocalClock, 1000);
|
clockTickId = setInterval(tickClocks, 1000);
|
||||||
clockServerPollId = setInterval(() => void refreshServerClock(), 30_000);
|
clockServerPollId = setInterval(() => void refreshServerClock(), 30_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,12 @@
|
|||||||
<span class="muted clock-kind">Браузер</span>
|
<span class="muted clock-kind">Браузер</span>
|
||||||
<time id="clock-local" class="clock-time">—</time>
|
<time id="clock-local" class="clock-time">—</time>
|
||||||
</div>
|
</div>
|
||||||
|
<p class="clock-hint muted">
|
||||||
|
Сервер показывается в вашем часовом поясе (как браузер). «Сверить» — заново запросить момент с VPS.
|
||||||
|
</p>
|
||||||
|
<button type="button" id="clock-sync" class="btn small ghost clock-sync-btn">
|
||||||
|
Сверить время
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" id="refresh" class="btn primary">Обновить</button>
|
<button type="button" id="refresh" class="btn primary">Обновить</button>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -234,6 +234,18 @@ h1 {
|
|||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clock-hint {
|
||||||
|
margin: 0.35rem 0 0;
|
||||||
|
font-size: 0.74rem;
|
||||||
|
line-height: 1.35;
|
||||||
|
max-width: 22rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clock-sync-btn {
|
||||||
|
margin-top: 0.35rem;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
.pill {
|
.pill {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
Reference in New Issue
Block a user