refactor: fold comment count into PostActions toolbar
CommentCountBadge was a separate component rendered next to PostActions — two adjacent strips of buttons that should have been one toolbar. Merges the count fetch + click-to-scroll behaviour into PostActions behind `commentPageId` / `commentTargetId` props. The standalone CommentCountBadge.svelte is now redundant; deleted. Layout under the post hero is now a single row: [💬 3] [↗ Teilen] [📋 Link] [🖨️] [⏱ 4 min] Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,65 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import Icon from "@iconify/svelte";
|
|
||||||
import "$lib/iconify-offline";
|
|
||||||
import { env } from "$env/dynamic/public";
|
|
||||||
import { useTranslate, T } from "$lib/translations";
|
|
||||||
|
|
||||||
const t = useTranslate();
|
|
||||||
|
|
||||||
let {
|
|
||||||
pageId,
|
|
||||||
environment,
|
|
||||||
targetId = "comments-section",
|
|
||||||
class: cls = "",
|
|
||||||
}: {
|
|
||||||
pageId: string;
|
|
||||||
environment?: string;
|
|
||||||
targetId?: string;
|
|
||||||
class?: string;
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
let count = $state<number | null>(null);
|
|
||||||
|
|
||||||
const cmsBase = (
|
|
||||||
env.PUBLIC_CMS_URL ||
|
|
||||||
import.meta.env.PUBLIC_CMS_URL ||
|
|
||||||
"http://localhost:3000"
|
|
||||||
).replace(/\/$/, "");
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
const url = `${cmsBase}/api/comments/${encodeURIComponent(pageId)}/count${
|
|
||||||
environment ? `?_environment=${encodeURIComponent(environment)}` : ""
|
|
||||||
}`;
|
|
||||||
let cancelled = false;
|
|
||||||
fetch(url)
|
|
||||||
.then((r) => r.json())
|
|
||||||
.then((d) => {
|
|
||||||
if (cancelled) return;
|
|
||||||
count = typeof d?.count === "number" ? d.count : 0;
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
if (!cancelled) count = 0;
|
|
||||||
});
|
|
||||||
return () => {
|
|
||||||
cancelled = true;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
function onClick(e: MouseEvent) {
|
|
||||||
const el = document.getElementById(targetId) as HTMLDetailsElement | null;
|
|
||||||
if (!el) return;
|
|
||||||
e.preventDefault();
|
|
||||||
el.open = true;
|
|
||||||
el.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<a
|
|
||||||
href="#{targetId}"
|
|
||||||
onclick={onClick}
|
|
||||||
aria-label={t(T.comments_jump_to)}
|
|
||||||
class="no-underline! inline-flex items-center gap-1 text-xs font-medium transition-colors {cls}"
|
|
||||||
>
|
|
||||||
<Icon icon="mdi:comment-outline" class="size-4" />
|
|
||||||
<span class="tabular-nums">{count ?? "…"}</span>
|
|
||||||
</a>
|
|
||||||
@@ -1,12 +1,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from "@iconify/svelte";
|
import Icon from "@iconify/svelte";
|
||||||
import "$lib/iconify-offline";
|
import "$lib/iconify-offline";
|
||||||
|
import { env } from "$env/dynamic/public";
|
||||||
import { useTranslate, T } from "$lib/translations";
|
import { useTranslate, T } from "$lib/translations";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
url,
|
url,
|
||||||
title,
|
title,
|
||||||
readingTimeMinutes = null,
|
readingTimeMinutes = null,
|
||||||
|
commentPageId = null,
|
||||||
|
commentEnvironment,
|
||||||
|
commentTargetId = "comments-section",
|
||||||
class: cls = "",
|
class: cls = "",
|
||||||
}: {
|
}: {
|
||||||
/** Absolute URL of the page being shared. */
|
/** Absolute URL of the page being shared. */
|
||||||
@@ -15,11 +19,58 @@
|
|||||||
title: string;
|
title: string;
|
||||||
/** Estimated reading time. `null` hides the badge. */
|
/** Estimated reading time. `null` hides the badge. */
|
||||||
readingTimeMinutes?: number | null;
|
readingTimeMinutes?: number | null;
|
||||||
|
/** Page id for the comment plugin. `null` hides the count badge. */
|
||||||
|
commentPageId?: string | null;
|
||||||
|
/** Optional space override for the comment count fetch. */
|
||||||
|
commentEnvironment?: string;
|
||||||
|
/** DOM id of the comment accordion (used by the count-badge click). */
|
||||||
|
commentTargetId?: string;
|
||||||
class?: string;
|
class?: string;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
const t = useTranslate();
|
const t = useTranslate();
|
||||||
|
|
||||||
|
// ── Comment count fetch (only when commentPageId is set) ────────────────
|
||||||
|
let commentCount = $state<number | null>(null);
|
||||||
|
const cmsBase = (
|
||||||
|
env.PUBLIC_CMS_URL ||
|
||||||
|
import.meta.env.PUBLIC_CMS_URL ||
|
||||||
|
"http://localhost:3000"
|
||||||
|
).replace(/\/$/, "");
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (!commentPageId) {
|
||||||
|
commentCount = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fetchUrl = `${cmsBase}/api/comments/${encodeURIComponent(commentPageId)}/count${
|
||||||
|
commentEnvironment
|
||||||
|
? `?_environment=${encodeURIComponent(commentEnvironment)}`
|
||||||
|
: ""
|
||||||
|
}`;
|
||||||
|
let cancelled = false;
|
||||||
|
fetch(fetchUrl)
|
||||||
|
.then((r) => r.json())
|
||||||
|
.then((d) => {
|
||||||
|
if (cancelled) return;
|
||||||
|
commentCount = typeof d?.count === "number" ? d.count : 0;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
if (!cancelled) commentCount = 0;
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function onCommentClick(e: MouseEvent) {
|
||||||
|
const el = document.getElementById(commentTargetId) as HTMLDetailsElement | null;
|
||||||
|
if (!el) return;
|
||||||
|
e.preventDefault();
|
||||||
|
el.open = true;
|
||||||
|
el.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
}
|
||||||
|
|
||||||
let copied = $state(false);
|
let copied = $state(false);
|
||||||
let shared = $state(false);
|
let shared = $state(false);
|
||||||
let copyTimer: ReturnType<typeof setTimeout> | null = null;
|
let copyTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
@@ -77,7 +128,19 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="inline-flex flex-wrap items-center gap-x-2 gap-y-1 {cls}">
|
<div class="inline-flex flex-wrap items-center gap-x-3 gap-y-1 {cls}">
|
||||||
|
{#if commentPageId}
|
||||||
|
<a
|
||||||
|
href="#{commentTargetId}"
|
||||||
|
onclick={onCommentClick}
|
||||||
|
aria-label={t(T.comments_jump_to)}
|
||||||
|
class="no-underline! inline-flex items-center gap-1 text-xs font-medium text-zinc-600 hover:text-zinc-900 transition-colors"
|
||||||
|
>
|
||||||
|
<Icon icon="mdi:comment-outline" class="size-4" />
|
||||||
|
<span class="tabular-nums">{commentCount ?? "…"}</span>
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={share}
|
onclick={share}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ const TRANSLATION_KEYS = [
|
|||||||
"post_map_activate",
|
"post_map_activate",
|
||||||
"post_map_open_osm",
|
"post_map_open_osm",
|
||||||
"post_comments",
|
"post_comments",
|
||||||
// Comment plugin (Comments.svelte / PostCard count pill / CommentCountBadge).
|
// Comment plugin (Comments.svelte / PostCard count pill / PostActions toolbar).
|
||||||
"comments_loading",
|
"comments_loading",
|
||||||
"comments_load_error_title",
|
"comments_load_error_title",
|
||||||
"comments_retry",
|
"comments_retry",
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
import EventBadges from "$lib/components/EventBadges.svelte";
|
import EventBadges from "$lib/components/EventBadges.svelte";
|
||||||
import EventMap from "$lib/components/EventMap.svelte";
|
import EventMap from "$lib/components/EventMap.svelte";
|
||||||
import Accordion from "$lib/components/Accordion.svelte";
|
import Accordion from "$lib/components/Accordion.svelte";
|
||||||
import CommentCountBadge from "$lib/components/CommentCountBadge.svelte";
|
|
||||||
import Comments from "$lib/components/Comments.svelte";
|
import Comments from "$lib/components/Comments.svelte";
|
||||||
import PostActions from "$lib/components/PostActions.svelte";
|
import PostActions from "$lib/components/PostActions.svelte";
|
||||||
import RustyImage from "$lib/components/RustyImage.svelte";
|
import RustyImage from "$lib/components/RustyImage.svelte";
|
||||||
@@ -78,16 +77,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-zinc-600">
|
<div class="flex flex-wrap items-center gap-x-4 gap-y-2 text-sm text-zinc-600">
|
||||||
{#if data.commentPageId}
|
|
||||||
<CommentCountBadge
|
|
||||||
pageId={data.commentPageId}
|
|
||||||
targetId="comments-section"
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
<PostActions
|
<PostActions
|
||||||
url={data.canonicalUrl}
|
url={data.canonicalUrl}
|
||||||
title={data.seoTitle}
|
title={data.seoTitle}
|
||||||
readingTimeMinutes={data.readingTimeMinutes}
|
readingTimeMinutes={data.readingTimeMinutes}
|
||||||
|
commentPageId={data.commentPageId}
|
||||||
|
commentTargetId="comments-section"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user