// 更新日志弹层:点右栏底部版本号打开。数据 GET /v1/changelog(公开端点,直接 fetch, // 不走 api() 免绑登录态)。红点:localStorage 记 last seen 版本,与 /healthz 版本不一致 // → 版本号旁亮小红点提示"有更新",打开弹层即视为已看、红点清除。 import { $ } from "./dom.js"; import { escapeHtml } from "./format.js"; import { renderMd, highlightIn } from "./markdown.js"; const SEEN_KEY = "zcbot_seen_version"; const PAGE_SIZE = 5; let _current = ""; // /healthz 返回的当前版本(main.js loadVersion 灌入) let _entries = []; let _nextOffset = 0; let _hasMore = false; let _loading = false; let _loadError = ""; // main.js loadVersion 拿到版本号后调:决定红点亮不亮(localStorage 不可用时静默不亮) export function markVersion(v) { _current = v || ""; const el = $("app-version"); if (!el) return; let seen = ""; try { seen = localStorage.getItem(SEEN_KEY) || ""; } catch (e) {} el.classList.toggle("hasnew", !!_current && seen !== _current); } export function closeChangelogModal() { $("changelog-modal").classList.remove("show"); } function renderChangelog() { const body = $("cl-body"); if (!_entries.length && _loading) { body.innerHTML = '
加载中…
'; return; } if (!_entries.length && _loadError) { body.innerHTML = `
加载失败: ${escapeHtml(_loadError)}
`; return; } if (!_entries.length) { body.innerHTML = '
暂无更新说明。
'; return; } body.innerHTML = _entries.map((e, i) => `
v${escapeHtml(e.version)} ${i === 0 ? '最新' : ""} ${e.date ? `${escapeHtml(e.date)}` : ""}
${renderMd(e.body_md)}
`).join("") + (_loadError ? `
加载失败: ${escapeHtml(_loadError)}
` : "") + (_hasMore ? `
` : ""); highlightIn(body); const more = $("cl-more"); if (more) more.onclick = () => loadChangelogPage(); } async function loadChangelogPage(reset = false) { if (_loading) return; if (reset) { _entries = []; _nextOffset = 0; _hasMore = false; } _loading = true; _loadError = ""; renderChangelog(); try { const r = await fetch(`/v1/changelog?limit=${PAGE_SIZE}&offset=${_nextOffset}`); if (!r.ok) throw new Error("HTTP " + r.status); const d = await r.json(); $("cl-cur").textContent = d.current ? "当前 v" + d.current : ""; const page = d.entries || []; _entries = _entries.concat(page); _nextOffset = Number.isInteger(d.next_offset) ? d.next_offset : _nextOffset + page.length; _hasMore = !!d.has_more; } catch (e) { _loadError = e.message || String(e); _hasMore = _entries.length > 0; } finally { _loading = false; renderChangelog(); } } function openChangelogModal() { $("changelog-modal").classList.add("show"); // 打开即视为"看过":记住当前版本 + 清红点 if (_current) { try { localStorage.setItem(SEEN_KEY, _current); } catch (e) {} $("app-version").classList.remove("hasnew"); } loadChangelogPage(true); } // ───── 顶层绑定 ───── $("app-version").onclick = openChangelogModal; $("cl-close").onclick = closeChangelogModal; $("changelog-modal").addEventListener("click", (e) => { if (e.target.id === "changelog-modal") closeChangelogModal(); // 点遮罩关闭 });