Skip to content

Feature: Threaded Comments (1-level) + @mentions with Realtime Notifications #24

@hoangsonww

Description

@hoangsonww

Summary
Add first-level threaded comments on posts and support @username mentions. When a user is mentioned or receives a reply, send a realtime in-app notification (and optional web-push later). Keep the scope tight: 1 nesting level (comment → reply), no arbitrary deep threads.

Motivation
Replies and mentions are core social behaviors. This improves conversation quality, boosts engagement, and creates clear notification events without overhauling the feed.


Scope

  • Comment on a post.

  • Reply to a comment (single level).

  • Parse and store @mentions in comments/replies.

  • Realtime in-app notifications for:

    • replies to my post
    • replies to my comment
    • direct @mention in a comment/reply
  • Pagination + optimistic UI.

  • RLS-safe, index-backed queries.

Out of scope (follow-ups)

  • Deeply nested threads (>1 level).
  • Web-push and email digests.
  • Moderation/reporting.

Data Model (Supabase / Postgres)

New table comments:

id uuid primary key default gen_random_uuid(),
post_id uuid not null references posts(id) on delete cascade,
author_id uuid not null references profiles(id) on delete cascade,
parent_comment_id uuid null references comments(id) on delete cascade,
content text not null check (length(trim(content)) > 0),
mentions text[] not null default '{}', -- parsed @handles → normalized usernames
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()

Indexes:

create index on comments (post_id, created_at desc);
create index on comments (parent_comment_id, created_at asc);
create index on comments using gin (mentions); -- for mention lookups

Notifications table notifications:

id uuid primary key default gen_random_uuid(),
user_id uuid not null references profiles(id) on delete cascade, -- recipient
type text not null check (type in ('reply_to_post','reply_to_comment','mention')),
actor_id uuid not null references profiles(id) on delete cascade, -- who triggered
post_id uuid not null references posts(id) on delete cascade,
comment_id uuid not null references comments(id) on delete cascade,
read_at timestamptz null,
created_at timestamptz not null default now()

RLS (sketch):

  • comments:

    • select for authenticated (public comments)
    • insert: author_id = auth.uid()
    • update/delete: author_id = auth.uid()
  • notifications:

    • select: user_id = auth.uid()
    • update (mark read): user_id = auth.uid()

API/Queries (client utils)

  • getComments(postId) → top-level comments (no parent), paginated.
  • getReplies(parentCommentId) → replies, oldest-first.
  • addComment({ postId, content })
  • addReply({ postId, parentCommentId, content })
  • listNotifications({ unreadOnly?, limit, cursor })
  • markNotificationRead(id) / markAllRead()

Realtime:

  • Subscribe to comments by post_id.
  • Subscribe to notifications by user_id.

UI/UX

  • Under each post:

    • Comment composer (placeholder: “Write a comment…”).

    • List top-level comments (newest first), each with:

      • author, timestamp, content (with linkified mentions)
      • “Reply” action → inline reply composer
      • “View replies (n)” toggle → expands replies (oldest first)
  • Mentions autocomplete: type @ to fetch usernames (debounced query).

  • Notifications bell in header:

    • badge count (unread)

    • dropdown list with items:

      • “X replied to your post”
      • “X replied to your comment”
      • “X mentioned you”
    • click → route to post/[id]#comment-{commentId}

  • Optimistic posting; fallback toast on error.


Mention Parsing (client)

  • On submit: extract @(\w{2,30}) (match your username rules).
  • Resolve to canonical usernames (and store into mentions[]).
  • If resolution fails, drop unknown mention(s) silently.

Performance & Limits

  • Page comments in chunks (e.g., 20).
  • Cap comment length (e.g., 1,000 chars).
  • Prevent N+1: fetch authors via single in query.
  • Add basic rate-limit (client cooldown + server constraint idea: one comment per 2s per author via trigger or defer to future).

Tasks

  • DB: create comments, indexes, RLS.
  • DB: create notifications, indexes, RLS.
  • Client: comment + reply composers with optimistic insert.
  • Client: comments list (top-level) with pagination; replies expand/collapse.
  • Client: mentions autocomplete + parsing + storage of mentions[].
  • Client: notifications bell, list, mark-read.
  • Realtime: subscribe comments by post_id; notifications by current user_id.
  • Linkification of @username/profile/[id or handle].
  • Tests: unit (parsing, reducers), integration (posting flow), e2e happy path.
  • Docs: README section + migration notes.

Acceptance Criteria

  • Can post a top-level comment and a single-level reply.
  • @mention in a comment/reply creates a notification for mentioned users.
  • Replying to my post/comment notifies me once per action.
  • Notifications are visible in the bell menu and can be marked read.
  • Realtime: new comments/replies and notifications appear without reload.
  • All operations succeed with RLS enabled; unauthenticated users cannot post.

Rollout

  • Ship behind feature.comments_v1 config flag (default: ON).
  • Safe migration with rollback SQL.
  • Seed data script for local dev (a few posts/comments to demo UI).

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdocumentationImprovements or additions to documentationenhancementNew feature or requestgood first issueGood for newcomershelp wantedExtra attention is neededquestionFurther information is requested

Projects

Status

Backlog

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions