articles: discussion threads + Related Forum Posts sidebar
ArticleComments component renders under every news + articles detail page. Anonymous visitors see all existing comments and a Sign-in CTA; signed-in members get a composer, threaded replies, and up/down votes. AI personas are tagged with a small AI chip so readers know what they're interacting with — same persona TZ rules from #63 apply. Schema extensions on bb.article_comments: parent_comment_id self-FK for one-level threading, denormalized author fields, vote counters, is_ai_seeded flag, status enum. user_id relaxed to nullable so AI rows can exist without a real user_profile; CHECK constraint enforces 'real user OR is_ai_seeded' so anonymous comments can never sneak through. forum_votes.target_type check expanded to include 'comment' — same polymorphic vote table powers thread/reply/comment votes. New Related Forum Posts sidebar on both /articles/[slug] and /news/[slug]. getRelatedForumThreads() does title-keyword ILIKE OR against forum_threads, ranked by reply_count + recency, with recently- active fallback so the box is never empty. 6 threads per article. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
getLegacyArticleBySlug,
|
||||
getLegacyRecentSlugs,
|
||||
getLegacyRelatedArticles,
|
||||
getRelatedForumThreads, type RelatedForumThread,
|
||||
} from "@/lib/articles/legacy-source";
|
||||
|
||||
export const revalidate = 3600;
|
||||
@@ -33,11 +34,15 @@ function absoluteImage(src: string | undefined | null): string {
|
||||
async function loadArticle(slug: string): Promise<{
|
||||
article: Article | null;
|
||||
related: Article[];
|
||||
relatedForumThreads: RelatedForumThread[];
|
||||
}> {
|
||||
const imported = await getLegacyArticleBySlug(slug);
|
||||
if (!imported) return { article: null, related: [] };
|
||||
const related = await getLegacyRelatedArticles(slug, imported.category, 3);
|
||||
return { article: imported, related };
|
||||
if (!imported) return { article: null, related: [], relatedForumThreads: [] };
|
||||
const [related, relatedForumThreads] = await Promise.all([
|
||||
getLegacyRelatedArticles(slug, imported.category, 3),
|
||||
getRelatedForumThreads(imported.title, 6),
|
||||
]);
|
||||
return { article: imported, related, relatedForumThreads };
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
|
||||
@@ -77,7 +82,7 @@ export async function generateStaticParams() {
|
||||
|
||||
export default async function ArticlePage({ params }: PageProps) {
|
||||
const { slug } = await params;
|
||||
const { article, related } = await loadArticle(slug);
|
||||
const { article, related, relatedForumThreads } = await loadArticle(slug);
|
||||
if (!article) {
|
||||
// Slug isn't in any of our article tables — send the reader to the news
|
||||
// index so a click from the homepage/ticker doesn't dead-end on 404.
|
||||
@@ -107,7 +112,7 @@ export default async function ArticlePage({ params }: PageProps) {
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleSchema) }}
|
||||
/>
|
||||
<ArticleDetailPage article={article} relatedArticles={related} />
|
||||
<ArticleDetailPage article={article} relatedArticles={related} relatedForumThreads={relatedForumThreads} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user