diff --git a/src/lib/cms.ts b/src/lib/cms.ts index f255349..36e5ede 100644 --- a/src/lib/cms.ts +++ b/src/lib/cms.ts @@ -693,3 +693,34 @@ export async function getCalendarItems( return data.items ?? []; }); } + +/** + * Bulk-fetches approved comment counts for many page_ids in one request. + * Hits the comment plugin's `/api/comments/counts` endpoint. Returns a map + * `{ pageId → count }` with `0` for any id missing in the response. + * + * The comment-plugin endpoints live behind a different env (`PUBLIC_CMS_URL`) + * and may be on a different origin in dev. SSR-side calls always hit it + * directly; the cookie sync of the visitor session does NOT happen here + * (counts are public anyway). + */ +export async function getCommentCounts( + pageIds: string[], + options?: { environment?: string }, +): Promise> { + if (pageIds.length === 0) return {}; + const base = getBaseUrl(); + const url = new URL(`${base}/api/comments/counts`); + url.searchParams.set("ids", pageIds.join(",")); + if (options?.environment) { + url.searchParams.set("_environment", options.environment); + } + try { + const res = await fetch(url.toString()); + if (!res.ok) return {}; + const data = (await res.json()) as { counts?: Record }; + return data.counts ?? {}; + } catch { + return {}; + } +} diff --git a/src/lib/components/BlogOverview.svelte b/src/lib/components/BlogOverview.svelte index c888ae4..42f6fef 100644 --- a/src/lib/components/BlogOverview.svelte +++ b/src/lib/components/BlogOverview.svelte @@ -8,6 +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"; interface TagEntry { _slug?: string; @@ -113,6 +114,45 @@ } 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>({}); + + function postSlug(p: PostEntry): string { + const raw = (p as { slug?: string; _slug?: string }).slug ?? p._slug ?? ""; + return raw.replace(/^\//, "").trim(); + } + + $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; + }) + .catch(() => { + if (!cancelled) commentCounts = {}; + }); + return () => { + cancelled = true; + }; + });
@@ -258,6 +298,7 @@ post={post} href={postHref(post)} tagBase={`${basePath}/tag`} + commentCount={commentCounts[postSlug(post)] ?? null} /> {/each}
diff --git a/src/lib/components/PostCard.svelte b/src/lib/components/PostCard.svelte index c12c6e9..32a08eb 100644 --- a/src/lib/components/PostCard.svelte +++ b/src/lib/components/PostCard.svelte @@ -1,4 +1,6 @@