feat: @mentions in comments

Parse @username in comment content, notify mentioned users (dedup
against recipe-author/parent-author who are already notified via
comment/reply), and render @username as a link to their profile.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Arnaud
2026-07-03 22:16:06 +02:00
parent 73a71d28cd
commit a51ba85253
5 changed files with 52 additions and 5 deletions
@@ -1,6 +1,7 @@
"use client";
import { useState, useEffect, useCallback, useMemo } from "react";
import { useState, useEffect, useCallback, useMemo, Fragment } from "react";
import Link from "next/link";
import { MessageCircle, Reply, Trash2 } from "lucide-react";
import { toast } from "sonner";
import { useTranslations } from "next-intl";
@@ -24,6 +25,25 @@ type Comment = {
};
const MAX_VISUAL_INDENT = 4;
const MENTION_REGEX = /@([a-z0-9_-]{3,30})/gi;
function renderContentWithMentions(content: string) {
const parts = content.split(MENTION_REGEX);
// split() with a capturing group interleaves [text, username, text, username, ...text]
return parts.map((part, i) =>
i % 2 === 1 ? (
<Link
key={i}
href={`/u/${part.toLowerCase()}`}
className="text-primary font-medium hover:underline"
>
@{part}
</Link>
) : (
<Fragment key={i}>{part}</Fragment>
)
);
}
function timeAgo(dateStr: string) {
const diff = Date.now() - new Date(dateStr).getTime();
@@ -126,7 +146,7 @@ function CommentItem({
<span className="font-medium text-sm">{comment.userName}</span>
<span className="text-xs text-muted-foreground">{timeAgo(comment.createdAt)}</span>
</div>
<p className="text-sm leading-relaxed whitespace-pre-wrap">{comment.content}</p>
<p className="text-sm leading-relaxed whitespace-pre-wrap">{renderContentWithMentions(comment.content)}</p>
<CommentReactions recipeId={recipeId} commentId={comment.id} initialCounts={{}} initialUserReactions={[]} />
<div className="flex items-center gap-2">
{currentUserId && (