diff --git a/app/(app)/notifications/page.tsx b/app/(app)/notifications/page.tsx index f890f278..bab81e83 100644 --- a/app/(app)/notifications/page.tsx +++ b/app/(app)/notifications/page.tsx @@ -145,7 +145,9 @@ function NotificationRow({ ? `/u/${selfHandle}/posts#post-${n.post_id}` : n.kind === "post_mention" && n.post_id && n.actor_handle ? `/u/${n.actor_handle}/posts#post-${n.post_id}` - : (n.kind === "post_comment" || n.kind === "comment_reply") && + : (n.kind === "post_comment" || + n.kind === "comment_reply" || + n.kind === "comment_mention") && n.post_id && n.post_author_handle ? `/u/${n.post_author_handle}/posts#post-${n.post_id}` @@ -190,6 +192,8 @@ function messageFor(n: AppNotification): string { return "commented on your post."; case "comment_reply": return "replied to your comment."; + case "comment_mention": + return "mentioned you in a comment."; default: return "sent you a notification."; } diff --git a/components/notifications/notifications-provider.tsx b/components/notifications/notifications-provider.tsx index a3ed8e08..d80ca185 100644 --- a/components/notifications/notifications-provider.tsx +++ b/components/notifications/notifications-provider.tsx @@ -67,6 +67,8 @@ function messageFor(e: IncomingEvent): string { return `${who} commented on your post`; case "comment_reply": return `${who} replied to your comment`; + case "comment_mention": + return `${who} mentioned you in a comment`; default: return "You have a new notification"; } diff --git a/components/social/comment/comment-row.tsx b/components/social/comment/comment-row.tsx index 689910a6..f5efddfc 100644 --- a/components/social/comment/comment-row.tsx +++ b/components/social/comment/comment-row.tsx @@ -11,6 +11,7 @@ */ import { useEffect, useRef, useState } from "react"; +import Link from "next/link"; import { HiHeart, HiOutlineHeart } from "react-icons/hi2"; import { Pin } from "lucide-react"; import { toast } from "sonner"; @@ -20,6 +21,26 @@ import { UserAvatar } from "@/components/ui/user-avatar"; import { timeAgo } from "@/lib/time"; import { CommentMenu } from "./comment-menu"; +// Split a comment body into text + clickable @handle links. The capturing +// group keeps the delimiters so they can be turned into profile links. +const MENTION_TOKEN = /(@[A-Za-z0-9_]+)/g; +function renderBody(text: string) { + return text.split(MENTION_TOKEN).map((part, i) => { + if (part.length > 1 && part[0] === "@") { + return ( + + {part} + + ); + } + return part; + }); +} + export function CommentRow({ comment, viewerId, @@ -151,7 +172,7 @@ export function CommentRow({ @{comment.reply_to_handle}{" "} ) : null} - {comment.body} + {renderBody(comment.body)}

{/* Inline action row */} diff --git a/components/social/comment/post-comments-drawer.tsx b/components/social/comment/post-comments-drawer.tsx index edd0ca7d..d9eb5a75 100644 --- a/components/social/comment/post-comments-drawer.tsx +++ b/components/social/comment/post-comments-drawer.tsx @@ -37,6 +37,7 @@ import { useMediaQuery } from "@/components/selection-toolbar/shared"; import { Drawer, DrawerContent } from "@/components/ui/drawer"; import { ConfirmModal } from "@/components/ui/confirm-modal"; import { EmojiPicker } from "@/components/ui/emoji-picker"; +import { MentionTextarea } from "@/components/social/mention-textarea"; import { CommentRow } from "./comment-row"; import { ReportCommentModal } from "./comment-report-modal"; @@ -324,20 +325,15 @@ export function PostCommentsDrawer({ ) : null}
-