Skip to content

git commit -m "feat(chat): add end-to-end message reactions and action#13

Merged
BuckyMcYolo merged 1 commit intomainfrom
dev
Mar 5, 2026
Merged

git commit -m "feat(chat): add end-to-end message reactions and action#13
BuckyMcYolo merged 1 commit intomainfrom
dev

Conversation

@BuckyMcYolo
Copy link
Copy Markdown
Owner

bar polish"
-m "Add message_reaction persistence, API/realtime reaction payloads, and reaction toggle handling across backend services." \ -m "Update web chat to render and optimistically update reactions, extract reusable message send/reaction hooks, and sync with websocket events."
-m "Polish chat UI: stabilize action-bar overlay behavior, add owner-only Edit/Delete menu entries, improve reaction pill hover/cursor states, and increase date divider spacing."

bar polish" \
-m "Add message_reaction persistence, API/realtime reaction payloads,
and reaction toggle handling across backend services." \
-m "Update web chat to render and optimistically update reactions,
extract reusable message send/reaction hooks, and sync with websocket
events." \
-m "Polish chat UI: stabilize action-bar overlay behavior, add
owner-only Edit/Delete menu entries, improve reaction pill hover/cursor
states, and increase date divider spacing."
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

📝 Walkthrough

Walkthrough

This PR introduces message reaction functionality by adding database schema for storing reactions, extending API queries to fetch reaction data with current user context, implementing realtime socket events for toggling reactions with optimistic UI updates, and updating React components and hooks to display and manage message reactions with real-time synchronization.

Changes

Cohort / File(s) Summary
Database Schema
packages/db/src/schemas/message-reactions.ts, packages/db/src/schemas/index.ts
Adds new message_reaction table with columns for messageId, userId, emoji, and enforces unique constraint on (messageId, userId, emoji); exports schema and relations.
Realtime Types
packages/realtime-types/src/events.ts
Introduces RealtimeMessageReaction, RealtimeMessageReactionUpdated, ToggleMessageReactionPayload, and ToggleMessageReactionAck types; extends RealtimeMessage with reactions array and updates client/server event interfaces for reaction toggle and update events.
API Message Schema
apps/api/src/lib/helpers/openapi/message-schemas.ts
Adds messageReactionSchema with emoji, count, and reactedByCurrentUser fields; extends messageWithAuthorSchema to include reactions array.
API Message Queries & Handlers
apps/api/src/lib/queries/messages.ts, apps/api/src/routes/v1/channels/handlers.ts, apps/api/src/routes/v1/dms/handlers.ts
Extends fetchMessagePage to accept currentUserId, fetches per-message reactions from database, aggregates counts per emoji, flags whether current user reacted, and includes reactions in response; handler methods pass current user ID to query.
Realtime Service
apps/realtime/src/services/messages.ts
Implements toggleMessageReaction function to transactionally toggle user reactions (insert or delete), calculates updated reaction counts, and returns reaction update with actor context; extends createMessage to initialize reactions array.
Realtime Socket Handler
apps/realtime/src/index.ts
Adds socket event handler for message:reaction:toggle that parses payload, invokes toggle service, broadcasts message:reaction:updated to channel, and sends ack with result or error.
Web Hooks for Real-time Sync
apps/web/src/hooks/use-message-reactions.ts, apps/web/src/hooks/use-message-sending.ts
Introduces useMessageReactions hook for optimistic reaction toggling with server sync and cache updates, and useMessageSending hook for sending messages with optimistic updates; both handle real-time events and maintain React Query cache.
Web Utilities for Optimistic Updates
apps/web/src/lib/realtime-adapter.ts
Adds applyReactionUpdateToMessage to apply server-sent reaction updates to cached messages and toggleReactionOptimistically for client-side optimistic toggling; updates createOptimisticMessage to include empty reactions array.
Web UI Components
apps/web/src/components/chat/emoji-reaction-picker.tsx, apps/web/src/components/chat/message-action-bar.tsx, apps/web/src/components/chat/message-item.tsx, apps/web/src/components/chat/message-list.tsx, apps/web/src/components/chat/date-divider.tsx
Replaces extended emoji grid with full emoji picker component; adds edit/delete/overlay state management to action bar; adds reaction display and interaction to message item; threads currentUserId and onReact handlers through component hierarchy; increases padding in date divider.
Web Route Integration
apps/web/src/routes/_authenticated/$guildSlug/$channelId.tsx, apps/web/src/routes/_authenticated/dms/$dmId.tsx
Refactors routes to use useMessageReactions and useMessageSending hooks instead of inline real-time handling; removes manual socket listeners and optimistic state management; passes currentUserId and reaction callbacks to child components.
Web Dependencies
apps/web/package.json
Adds emoji-picker-react@^4.18.0 dependency.

Sequence Diagram

sequenceDiagram
    actor User
    participant Client as Client (Browser)
    participant RealtimeServer as Realtime Server
    participant Database
    participant OtherClients as Other Clients in Channel

    User->>Client: Click reaction emoji
    Client->>Client: Apply optimistic update to cache
    Client->>Client: Render updated reaction count<br/>(reactedByCurrentUser = true)
    Client->>RealtimeServer: Emit message:reaction:toggle<br/>(messageId, emoji)
    RealtimeServer->>Database: Check channel access<br/>Fetch message & existing reaction
    alt Reaction exists
        Database-->>RealtimeServer: Return existing reaction
        RealtimeServer->>Database: Delete reaction (transactional)
    else Reaction does not exist
        Database-->>RealtimeServer: No reaction found
        RealtimeServer->>Database: Insert new reaction (transactional)
    end
    Database-->>RealtimeServer: Confirm toggle
    RealtimeServer->>RealtimeServer: Calculate updated reaction count
    RealtimeServer->>RealtimeServer: Build RealtimeMessageReactionUpdated update
    RealtimeServer-->>Client: Ack with { ok: true, update }
    Client->>Client: Apply server update to cache<br/>(sync optimistic with server)
    RealtimeServer->>OtherClients: Broadcast message:reaction:updated<br/>(emoji, count, actorUserId, reactedByActor)
    OtherClients->>OtherClients: Receive update & apply to cache<br/>Render updated reaction
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • PR #8: Establishes the realtime server foundation and socket infrastructure that this PR extends with new reaction event handlers and service logic.
  • PR #9: Introduces the @repo/realtime-types package that this PR augments with new reaction-specific event types and payloads.

Poem

🐰 A hop, a click, reactions bloom,
Emojis dance in the chatroom,
Optimistic hops sync with the server's care,
Real-time updates float through the air—
Message reactions, hopping everywhere! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title describes the main changes: adding end-to-end message reactions feature and action bar UI polish, which align with the changeset's core objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/lib/helpers/openapi/message-schemas.ts`:
- Line 22: The new required "reactions" field in message-schemas.ts is
incompatible with existing message payloads; change the reactions schema (the
`reactions` property that currently references `messageReactionSchema`) to be
optional (or provide a safe default like an empty array) so responses from
apps/api/src/lib/queries/messages.ts and the handlers in
apps/api/src/routes/v1/channels/handlers.ts and
apps/api/src/routes/v1/dms/handlers.ts remain valid until you update the message
assembly to include reactions.

In `@apps/realtime/src/services/messages.ts`:
- Around line 168-178: The reaction count query (reactionCount) is executed
outside the transaction, allowing race conditions vs. reactedByActor updates;
move the db.select(...) that computes the count on schema.messageReaction
(currently assigned to reactionCount) into the same transaction scope that
updates/reads reactedByActor so it uses the transaction handle (trx) instead of
the global db connection and runs before committing, ensuring the broadcasted
count is consistent and atomic with the transaction's updates.

In `@apps/web/src/components/chat/emoji-reaction-picker.tsx`:
- Around line 32-36: The handleSelect function closes the picker locally but
doesn't notify the parent—update handleSelect (the handler that calls onSelect,
setOpen and setShowFullPicker) to also invoke onOpenChange(false) (guarded by
optional chaining or a null check) so parent components receive the closed state
when an emoji is selected; ensure this call mirrors the existing close logic
used elsewhere to keep overlay state in sync.

In `@apps/web/src/hooks/use-message-reactions.ts`:
- Around line 89-92: The failure rollback currently calls
toggleReactionLocal(messageId, emoji) inside the result.ok false branch which is
concurrency-unsafe; instead, on failure invalidate or refetch the authoritative
message data so the client state is reconciled with the server. Replace the
inverse local toggle in the error path of the send/ack block (where result.ok is
checked) with a call to the message data resync mechanism used by this hook
(e.g., trigger the existing refetch/invalidate function for the message or emit
a request to reload the message by messageId) so authoritative state is applied
rather than a second local toggle.

In `@apps/web/src/hooks/use-message-sending.ts`:
- Around line 112-130: When emitting with socket.emit("message:send", {
channelId, content, nonce }, ...) add a timeout fallback so optimistic messages
and pendingNonces don't get stuck: start a setTimeout when sending (store timers
keyed by nonce, e.g., pendingTimers.current[nonce] or similar), and on timeout
remove the nonce from pendingNonces.current and updateMessagesInCache to remove
or mark the optimistic message (id === nonce); also clear the timeout when the
callback succeeds or fails (both branches inside the socket callback must clear
the timer) and ensure the timeout is cleared on any early returns to avoid
leaks; use realtimeMessageToMessage only when callback returns result.message.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9596507e-593b-4cc9-9f95-47467b221d87

📥 Commits

Reviewing files that changed from the base of the PR and between 89faa7c and 500db3e.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (20)
  • apps/api/src/lib/helpers/openapi/message-schemas.ts
  • apps/api/src/lib/queries/messages.ts
  • apps/api/src/routes/v1/channels/handlers.ts
  • apps/api/src/routes/v1/dms/handlers.ts
  • apps/realtime/src/index.ts
  • apps/realtime/src/services/messages.ts
  • apps/web/package.json
  • apps/web/src/components/chat/date-divider.tsx
  • apps/web/src/components/chat/emoji-reaction-picker.tsx
  • apps/web/src/components/chat/message-action-bar.tsx
  • apps/web/src/components/chat/message-item.tsx
  • apps/web/src/components/chat/message-list.tsx
  • apps/web/src/hooks/use-message-reactions.ts
  • apps/web/src/hooks/use-message-sending.ts
  • apps/web/src/lib/realtime-adapter.ts
  • apps/web/src/routes/_authenticated/$guildSlug/$channelId.tsx
  • apps/web/src/routes/_authenticated/dms/$dmId.tsx
  • packages/db/src/schemas/index.ts
  • packages/db/src/schemas/message-reactions.ts
  • packages/realtime-types/src/events.ts

Comment thread apps/api/src/lib/helpers/openapi/message-schemas.ts
Comment thread apps/realtime/src/services/messages.ts
Comment thread apps/web/src/components/chat/emoji-reaction-picker.tsx
Comment thread apps/web/src/hooks/use-message-reactions.ts
Comment thread apps/web/src/hooks/use-message-sending.ts
@BuckyMcYolo BuckyMcYolo merged commit 44b1d14 into main Mar 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant