news-digest: live-recompute source + desk counts; hide empty sections
The server-rendered .source-count / .desk-count badges were correct at render time but went stale the moment the user hid anything — "r/HOMELAB (4)" stayed at 4 even after all 4 items were hidden. Worse, the entire source header still rendered with a (0) badge once every item underneath was gone. app.js gains a refreshCounts() pass that walks every .source and .desk, recomputes the visible (non-.is-hidden) child count, updates the badge text, and toggles an .is-empty class. CSS rule for .source.is-empty and .desk.is-empty sets display:none so empty groups collapse out entirely. Hooked into hideItem, restoreItem, and the initial-paint hidden-set application.
This commit is contained in:
@@ -61,6 +61,28 @@
|
||||
return r.json();
|
||||
}
|
||||
|
||||
/* ── live counts ─────────────────────────────────────────────────
|
||||
The server-rendered .source-count and .desk-count badges are
|
||||
accurate at render time, but they fall stale as soon as the user
|
||||
hides anything. Recompute from the DOM whenever the visible set
|
||||
changes. Empty sources / desks get an .is-empty class that hides
|
||||
them entirely (no point showing "r/homelab (0)"). */
|
||||
|
||||
function refreshCounts() {
|
||||
document.querySelectorAll(".source").forEach((src) => {
|
||||
const visible = src.querySelectorAll(".item:not(.is-hidden)").length;
|
||||
const badge = src.querySelector(".source-count");
|
||||
if (badge) badge.textContent = String(visible);
|
||||
src.classList.toggle("is-empty", visible === 0);
|
||||
});
|
||||
document.querySelectorAll(".desk").forEach((desk) => {
|
||||
const visibleItems = desk.querySelectorAll(".item:not(.is-hidden)").length;
|
||||
const badge = desk.querySelector(".desk-count");
|
||||
if (badge) badge.textContent = `${visibleItems} items`;
|
||||
desk.classList.toggle("is-empty", visibleItems === 0);
|
||||
});
|
||||
}
|
||||
|
||||
/* ── tray rendering ─────────────────────────────────────────────── */
|
||||
|
||||
function refreshTrayVisibility() {
|
||||
@@ -122,6 +144,7 @@
|
||||
// Optimistic — flip class first, talk to server next.
|
||||
el.classList.add(HIDE_CLASS);
|
||||
addTrayRow(id, titleOf(el));
|
||||
refreshCounts();
|
||||
|
||||
try {
|
||||
await apiHide(id, true);
|
||||
@@ -129,6 +152,7 @@
|
||||
console.warn("[digest] hide failed, rolling back:", e);
|
||||
el.classList.remove(HIDE_CLASS);
|
||||
removeTrayRow(id);
|
||||
refreshCounts();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,12 +168,14 @@
|
||||
}
|
||||
el.classList.remove(HIDE_CLASS);
|
||||
removeTrayRow(id);
|
||||
refreshCounts();
|
||||
try {
|
||||
await apiHide(id, false);
|
||||
} catch (e) {
|
||||
console.warn("[digest] restore failed, re-hiding:", e);
|
||||
el.classList.add(HIDE_CLASS);
|
||||
addTrayRow(id, titleOf(el));
|
||||
refreshCounts();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,5 +213,6 @@
|
||||
}
|
||||
});
|
||||
if (trayHadAdditions) refreshTrayVisibility();
|
||||
refreshCounts();
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -632,6 +632,14 @@ a:hover { color: var(--accent); }
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* When every item in a .source (or every source in a .desk) is hidden,
|
||||
collapse the whole header out — no point rendering "r/homelab (0)".
|
||||
app.js applies .is-empty when the visible-count drops to zero. */
|
||||
.source.is-empty,
|
||||
.desk.is-empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hidden-tray {
|
||||
margin-top: 24px;
|
||||
padding: 14px 18px;
|
||||
|
||||
Reference in New Issue
Block a user