Skip to content

Chat / DM Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use compose:subagent (recommended) or compose:execute to implement this plan task-by-task.

Goal: Implement direct messaging and chat rooms with real-time-like polling for the Circle.so clone.

Architecture: Chat rooms identified by UUID. Messages use rich_text_body (TipTap JSON). DMs are chat rooms with kind: "direct" and 2 participants. Cursor-based pagination for messages. Polling for new messages (no WebSocket in backend spec).

Tech Stack: React 19, TanStack Router, TanStack React Query, TipTap (existing), shadcn/ui


File Structure

src/
├── lib/api/
│   └── chat.ts                          # Chat API hooks (queries + mutations)
├── components/chat/
│   ├── ChatList.tsx                     # Sidebar list of chat rooms
│   ├── ChatWindow.tsx                   # Main chat message view
│   ├── ChatMessage.tsx                  # Single message bubble
│   ├── ChatInput.tsx                    # Message input with TipTap
│   ├── ChatThread.tsx                   # Thread view (replies)
│   └── NewChatDialog.tsx               # Start new DM/group chat
├── routes/
│   └── chat.tsx                         # Chat page (layout with list + window)

Task 1: Chat API Hooks

Files:

  • Create: src/lib/api/chat.ts

  • [ ] Step 1: Create chat API layer

Create React Query hooks for:

  • useChatRooms() — GET /api/headless/v1/messages (list rooms)

  • useChatMessages(uuid) — GET /api/headless/v1/messages/{uuid}/chat_room_messages (cursor pagination)

  • useSendMessage(uuid) — POST /api/headless/v1/messages/{uuid}/chat_room_messages

  • useCreateChatRoom() — POST /api/headless/v1/messages (create DM/group)

  • useMarkChatRead(uuid) — POST /api/headless/v1/messages/{uuid}/mark_all_as_read

  • useUnreadChatRooms() — GET /api/headless/v1/messages/unread_chat_rooms

  • useChatThreads() — GET /api/headless/v1/chat_threads

  • useChatThread(id) — GET /api/headless/v1/chat_threads/

  • [ ] Step 2: Commit


Task 2: ChatMessage Component

Files:

  • Create: src/components/chat/ChatMessage.tsx

  • [ ] Step 1: Create message bubble component

Renders a single chat message with:

  • Sender avatar + name

  • Message body (render TipTap JSON as HTML using existing jsonToHtml)

  • Timestamp

  • Reactions display

  • Thread indicator (replies_count)

  • Hover actions (reply, react)

  • [ ] Step 2: Commit


Task 3: ChatInput Component

Files:

  • Create: src/components/chat/ChatInput.tsx

  • [ ] Step 1: Create message input component

Uses existing TipTapEditor in minimal mode. On submit:

  • Serializes content to rich_text_body format

  • Calls useSendMessage mutation

  • Clears editor on success

  • [ ] Step 2: Commit


Task 4: ChatWindow Component

Files:

  • Create: src/components/chat/ChatWindow.tsx

  • [ ] Step 1: Create main chat view

  • Fetches messages via useChatMessages(uuid)

  • Renders message list with auto-scroll to bottom

  • Cursor-based "load more" for older messages

  • Shows ChatInput at bottom

  • Empty state when no room selected

  • [ ] Step 2: Commit


Task 5: ChatList Component

Files:

  • Create: src/components/chat/ChatList.tsx

  • [ ] Step 1: Create chat room list sidebar

  • Fetches rooms via useChatRooms()

  • Shows room name, last message preview, timestamp

  • Unread badge per room

  • Click to select room

  • "New chat" button at top

  • [ ] Step 2: Commit


Task 6: NewChatDialog Component

Files:

  • Create: src/components/chat/NewChatDialog.tsx

  • [ ] Step 1: Create new chat dialog

  • Search members (reuse useSearchMembers from existing API)

  • Select one or more members

  • Create DM (2 members) or group chat (3+ members)

  • Navigate to the new chat room

  • [ ] Step 2: Commit


Task 7: Chat Page Route

Files:

  • Create: src/routes/chat.tsx

  • [ ] Step 1: Create chat page

Layout: sidebar (ChatList) + main area (ChatWindow)

  • Two-panel layout on desktop

  • Mobile: show list, tap to show messages

  • Selected room state via URL param or local state

  • [ ] Step 2: Commit


Task 8: Verify Build

  • [ ] Step 1: Run typecheck
  • [ ] Step 2: Run build
  • [ ] Step 3: Fix any issues

Strum — Documentação.