feat(web): cache dashboard aggregations with version invalidation
This commit is contained in:
1 parent
91aa3e1f95
commit
9fd277c05f
15 files changed
+399
-30
No files matched your search
@@ -62,7 +62,7 @@ function icon(name) {
|
||||
}
|
||||
|
||||
function renderNav() {
|
||||
const nav = document.querySelector("#bottom-nav");
|
||||
const nav = document.querySelector("#nav-items");
|
||||
const items = NAV_ITEMS.filter(
|
||||
(item) => !item.protected || canAccessProtected(),
|
||||
);
|
||||
@@ -86,8 +86,9 @@ function activeNav(path) {
|
||||
function setTitle(route, path) {
|
||||
const title = route?.title || "看板";
|
||||
document.title = `${title} · 咻咻满 Reaction`;
|
||||
const element = document.querySelector("#topbar-title");
|
||||
if (element) element.textContent = title;
|
||||
document.querySelectorAll(".brand-subtitle").forEach((element) => {
|
||||
element.textContent = title;
|
||||
});
|
||||
activeNav(path);
|
||||
}
|
||||
|
||||
@@ -113,8 +114,6 @@ window.addEventListener("error", (event) => {
|
||||
|
||||
async function boot() {
|
||||
const banner = document.querySelector("#preview-banner");
|
||||
const hint = document.querySelector("#topbar-hint");
|
||||
const authLink = document.querySelector("#topbar-auth");
|
||||
try {
|
||||
const data = await session();
|
||||
authState = {
|
||||
@@ -123,10 +122,14 @@ async function boot() {
|
||||
};
|
||||
if (data?.preview) {
|
||||
banner.hidden = false;
|
||||
if (hint) hint.hidden = false;
|
||||
document.querySelectorAll(".topbar-preview").forEach((element) => {
|
||||
element.hidden = false;
|
||||
});
|
||||
}
|
||||
if (authLink && authState.authEnabled && !authState.authenticated) {
|
||||
authLink.hidden = false;
|
||||
if (authState.authEnabled && !authState.authenticated) {
|
||||
document.querySelectorAll(".topbar-auth").forEach((element) => {
|
||||
element.hidden = false;
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
toast("无法连接看板服务", "error");
|
||||
|
||||
@@ -12,25 +12,38 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="app" id="app">
|
||||
<header class="topbar">
|
||||
<header class="topbar mobile-topbar">
|
||||
<a class="brand" href="/home">
|
||||
<span class="brand-mark">咻</span>
|
||||
<span class="brand-mark">满</span>
|
||||
<span class="brand-text">
|
||||
<strong>Reaction 看板</strong>
|
||||
<small id="topbar-title">最新动态</small>
|
||||
<small class="brand-subtitle">最新动态</small>
|
||||
</span>
|
||||
</a>
|
||||
<a class="topbar-hint" id="topbar-auth" href="/login" hidden>登录</a>
|
||||
<span class="topbar-hint" id="topbar-hint" hidden>预览</span>
|
||||
<a class="topbar-hint topbar-auth" href="/login" hidden>登录</a>
|
||||
<span class="topbar-hint topbar-preview" hidden>预览</span>
|
||||
</header>
|
||||
<div class="preview-banner" id="preview-banner" hidden>预览数据 · 不影响真实数据库</div>
|
||||
<main class="main-shell">
|
||||
<div id="view"></div>
|
||||
</main>
|
||||
<nav class="bottom-nav" id="bottom-nav" aria-label="主导航"></nav>
|
||||
<nav class="bottom-nav" id="bottom-nav" aria-label="主导航">
|
||||
<div class="sidebar-topbar">
|
||||
<a class="brand" href="/home">
|
||||
<span class="brand-mark">满</span>
|
||||
<span class="brand-text">
|
||||
<strong>Reaction 看板</strong>
|
||||
<small class="brand-subtitle">最新动态</small>
|
||||
</span>
|
||||
</a>
|
||||
<a class="topbar-hint topbar-auth" href="/login" hidden>登录</a>
|
||||
<span class="topbar-hint topbar-preview" hidden>预览</span>
|
||||
</div>
|
||||
<div class="nav-items" id="nav-items"></div>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
||||
<script src="/static/vendor/echarts.min.js"></script>
|
||||
<script type="module" src="/static/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -123,6 +123,16 @@ h1, h2, h3, p { margin: 0; }
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
.sidebar-topbar {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 0 4px 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.nav-items { display: flex; flex-direction: row; width: 100%; height: 100%; }
|
||||
|
||||
.preview-banner {
|
||||
position: sticky;
|
||||
@@ -160,6 +170,7 @@ h1, h2, h3, p { margin: 0; }
|
||||
}
|
||||
.page-head h1 { font-size: 24px; letter-spacing: -0.4px; }
|
||||
.page-sub { margin-top: 4px; color: var(--muted); font-size: 12.5px; }
|
||||
.page-head h1 .page-sub { margin-top: 0; margin-left: 8px; font-weight: 400; }
|
||||
.home-head { align-items: center; }
|
||||
.back-link { color: var(--muted); font-size: 13px; }
|
||||
.back-link:hover { color: var(--accent); }
|
||||
@@ -825,17 +836,12 @@ h1, h2, h3, p { margin: 0; }
|
||||
}
|
||||
|
||||
@media (min-width: 900px) {
|
||||
.topbar {
|
||||
position: fixed;
|
||||
left: var(--nav-width);
|
||||
right: 0;
|
||||
height: 64px;
|
||||
padding: 0 28px;
|
||||
}
|
||||
.preview-banner { top: 64px; margin-left: var(--nav-width); }
|
||||
.mobile-topbar { display: none; }
|
||||
.sidebar-topbar { display: flex; }
|
||||
.preview-banner { top: 0; margin-left: var(--nav-width); }
|
||||
.main-shell {
|
||||
margin-left: var(--nav-width);
|
||||
padding: 82px 28px 40px;
|
||||
padding: 16px 28px 40px;
|
||||
}
|
||||
.bottom-nav {
|
||||
top: 0;
|
||||
@@ -845,11 +851,15 @@ h1, h2, h3, p { margin: 0; }
|
||||
height: 100vh;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
gap: 4px;
|
||||
padding: 82px 12px 20px;
|
||||
gap: 10px;
|
||||
padding: 18px 12px 20px;
|
||||
border-top: 0;
|
||||
border-right: 1px solid var(--border);
|
||||
}
|
||||
.nav-items {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.nav-item {
|
||||
flex: 0 0 auto;
|
||||
flex-direction: row;
|
||||
|
||||
@@ -126,10 +126,7 @@ export async function renderRankings(root, { query }) {
|
||||
|
||||
root.innerHTML = `<section class="page rankings-page">
|
||||
<header class="page-head">
|
||||
<div>
|
||||
<h1>榜单</h1>
|
||||
<p class="page-sub">共 ${formatCount(data.total)} ${scope === "up" ? "个 UP" : "条视频"}</p>
|
||||
</div>
|
||||
<h1>榜单 <span class="page-sub">共 ${formatCount(data.total)} ${scope === "up" ? "个 UP" : "条视频"}</span></h1>
|
||||
</header>
|
||||
${controls({ scope, sort, within, growthDays, q, folder, minVideos, folders })}
|
||||
${data.available === false ? `<div class="notice-row warning">
|
||||
|
||||
Reference in new issue
Block a user