feat: print-friendly layout + comment count on every post overview
- Hides Header / HeaderOverlay / Footer / TopBanner / Breadcrumbs and the comment accordion when printing. Action toolbar already had `print:hidden`. - Adds @media print rules in app.css: 1.5cm page margins, 11pt body, forced black-on-white, expand link URLs after anchor text inside articles, keep images and headings together at page breaks. - PostCard now renders the count pill whenever a value is known (incl. zero); previously hidden for `count === 0`, so posts whose comments are still pending always looked empty. - Extracts the bulk-fetch helper to `lib/comment-counts.ts` (`fetchCommentCounts` + `postSlugForComments`) and uses it from both `BlogOverview` and `PostOverviewBlock`. Homepage / any page with a post_overview block now shows the badges too — previously only the paginated /posts/* routes did. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
import { postHref } from "$lib/blog-utils";
|
||||
import { t as tStatic, T } from "$lib/translations";
|
||||
import type { Translations } from "$lib/translations";
|
||||
import { env } from "$env/dynamic/public";
|
||||
import { fetchCommentCounts, postSlugForComments, type CommentCountsMap } from "$lib/comment-counts";
|
||||
|
||||
interface TagEntry {
|
||||
_slug?: string;
|
||||
@@ -115,43 +115,15 @@
|
||||
|
||||
const totalCount = $derived(totalPosts);
|
||||
|
||||
// Bulk-fetch comment counts for the visible posts. Uses a single
|
||||
// `/api/comments/counts` request keyed on the post slug. Failures keep the
|
||||
// map empty so cards just don't render the badge.
|
||||
let commentCounts = $state<Record<string, number>>({});
|
||||
|
||||
function postSlug(p: PostEntry): string {
|
||||
const raw = (p as { slug?: string; _slug?: string }).slug ?? p._slug ?? "";
|
||||
return raw.replace(/^\//, "").trim();
|
||||
}
|
||||
|
||||
// Bulk-fetch comment counts for the visible posts (single request per render).
|
||||
let commentCounts = $state<CommentCountsMap>({});
|
||||
$effect(() => {
|
||||
const slugs = posts.map(postSlug).filter(Boolean);
|
||||
if (slugs.length === 0) {
|
||||
commentCounts = {};
|
||||
return;
|
||||
}
|
||||
const cmsBase = (
|
||||
env.PUBLIC_CMS_URL ||
|
||||
import.meta.env.PUBLIC_CMS_URL ||
|
||||
""
|
||||
).replace(/\/$/, "");
|
||||
if (!cmsBase) return;
|
||||
const url = new URL(`${cmsBase}/api/comments/counts`);
|
||||
url.searchParams.set("ids", slugs.join(","));
|
||||
let cancelled = false;
|
||||
fetch(url.toString())
|
||||
.then((r) => (r.ok ? r.json() : { counts: {} }))
|
||||
.then((d) => {
|
||||
if (cancelled) return;
|
||||
commentCounts = (d?.counts ?? {}) as Record<string, number>;
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) commentCounts = {};
|
||||
const ctrl = new AbortController();
|
||||
fetchCommentCounts(posts.map(postSlugForComments), { signal: ctrl.signal })
|
||||
.then((map) => {
|
||||
commentCounts = map;
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
return () => ctrl.abort();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -298,7 +270,7 @@
|
||||
post={post}
|
||||
href={postHref(post)}
|
||||
tagBase={`${basePath}/tag`}
|
||||
commentCount={commentCounts[postSlug(post)] ?? null}
|
||||
commentCount={commentCounts[postSlugForComments(post)] ?? null}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user