Skip to main content

What It Does

Text comments on recipes and posts with @mention support. Mentioned users receive notifications. Comments can be swiped to delete.

User Flow

1

Open Comments

Tap comment icon → CommentModal opens (bottom sheet).
2

Write Comment

Type comment, can @mention users via MentionInput with UserSearchPopup.
3

Submit

supabase.rpc('create_recipe_comment', {...}) or supabase.rpc('create_post_comment', {...}). The RPC function extracts @mentions from the content, inserts mention records, and triggers fire to create notifications.
4

Delete (Own Comments)

Swipe-to-delete via SwipeableCommentItem.

Components

ComponentPurpose
CommentModalBottom sheet modal
CommentSectionInline comment list
SwipeableCommentItemSwipeable comment with delete action
MentionTextRenders @mentions as tappable links
MentionInputText input with @mention autocomplete
UserSearchPopupDropdown for @mention user search

Hooks

useComments (src/hooks/api/useComments.ts) — provides useComments (infinite query), useAddComment (optimistic create), useDeleteComment (cache-aware delete). Also useCommentModal, useMentionInput.

Data Access Pattern

The mobile app calls Supabase directly for comments — supabase.rpc('create_recipe_comment'), supabase.rpc('get_comments'), etc. The web API routes exist for the web client (Phase 2).

Cross-Screen Comment Count Sync

When a comment is added via useAddComment, the comment count must update everywhere — not just the comment list, but also every feed card and detail view showing that content.

How It Works

  1. onMutate: Optimistic comment inserted into comment list cache with a temp ID (temp-{timestamp})
  2. onSuccess:
    • Temp comment replaced with real server response (real ID, server timestamp)
    • Comment count incremented in ALL list view caches — walks every page of every infinite query matching queryKeys.posts.all or queryKeys.recipes.all
    • Comment count incremented in detail view cache (commentCount + 1)
  3. onError: Rollback comment list to previous snapshot

Delete Comment (useDeleteComment)

On success:
  1. Comment removed from comment list cache
  2. Comment count decremented in ALL list view caches (walks every page of every infinite query)
  3. Comment count decremented in detail view cache (commentCount - 1, floored at 0)

Web API Routes (Phase 2)

MethodRoutePurpose
POST/api/recipes/[id]/commentsCreate recipe comment
GET/api/recipes/[id]/commentsList recipe comments
DELETE/api/recipes/[id]/comments/[commentId]Delete recipe comment
POST/api/posts/[id]/commentsCreate post comment
GET/api/posts/[id]/commentsList post comments
DELETE/api/posts/[id]/comments/[commentId]Delete post comment

RPC Functions

FunctionParametersReturns
create_recipe_commentp_recipe_id, p_user_id, p_contentTABLE(out_comment_id, out_content, out_created_at, out_user_id, out_username, out_avatar_url)
create_post_commentp_post_id, p_user_id, p_contentTABLE(out_comment_id, out_content, out_created_at, out_user_id, out_username, out_avatar_url)
get_commentsp_content_type (‘recipe’/‘post’), p_content_id, p_limit (20), p_offset (0)TABLE(id, content, created_at, user_id, username, avatar_url, total_count)
delete_recipe_commentp_comment_id, p_user_idboolean
delete_post_commentp_comment_id, p_user_idboolean

Mention Flow

The create_post_comment and create_recipe_comment RPC functions handle @mention extraction entirely in the database:
  1. Comment is inserted into the comments table
  2. The RPC function extracts @mentions from the content using regexp_matches(p_content, '@([a-zA-Z0-9_]+)', 'g')
  3. Mentioned usernames are resolved to user IDs via JOIN to profiles
  4. Self-mentions are excluded (WHERE p.id != p_user_id)
  5. Mention records are inserted into *_comment_mentions table (with ON CONFLICT DO NOTHING for safety)
  6. Database trigger fires on the mention INSERT → creates notification (type: mention_recipe_comment or mention_post_comment)
The mobile app calls supabase.rpc('create_post_comment') or supabase.rpc('create_recipe_comment') directly. The mention extraction, validation, and notification creation all happen inside the RPC function and its associated triggers — no application-level mention parsing is needed for the database write.

Database

TableKey Columns
recipe_commentsid, recipe_id (FK→recipes), user_id (FK→profiles), content, created_at
post_commentsid, post_id (FK→posts), user_id (FK→profiles), content, created_at
recipe_comment_mentionscomment_id (FK→recipe_comments), mentioned_user_id (FK→profiles) — Composite PK
post_comment_mentionscomment_id (FK→post_comments), mentioned_user_id (FK→profiles) — Composite PK

RLS Policies

  • Comments: SELECT filtered by parent not deleted, INSERT auth.uid() = user_id, DELETE auth.uid() = user_id
  • Mentions: SELECT public, INSERT/DELETE by comment author (via JOIN to comments table)

Triggers

TriggerTableEventsFunction
trigger_update_recipe_comment_countrecipe_commentsINSERT, DELETEupdate_recipe_comment_count()
trigger_update_post_comment_countpost_commentsINSERT, DELETEupdate_post_comment_count()
trigger_recipe_comment_mention_notificationrecipe_comment_mentionsINSERTcreate_recipe_comment_mention_notification()
trigger_post_comment_mention_notificationpost_comment_mentionsINSERTcreate_post_comment_mention_notification()