Skip to content

Real-Time Chat

Planner includes a built-in real-time messaging system powered by Socket.IO WebSockets.

Architecture

  • Socket.IO namespace: /chat
  • Authentication: Token-based via handshake.auth.token
  • Events: chat:created, chat:message, chat:typing

API Endpoints

MethodPathDescription
GET/api/chats/conversationsList user's conversations
POST/api/chats/direct/:userIdGet or create direct conversation
POST/api/chats/task/:taskIdGet or create task conversation
GET/api/chats/conversations/:convId/messagesList messages
POST/api/chats/conversations/:convId/messagesSend message

Socket.IO Events

Client → Server

EventPayloadDescription
chat:message{ conversationId, body }Send a message
chat:typing{ conversationId, isTyping }Typing indicator

Server → Client

EventPayloadDescription
chat:createdConversationNew conversation created
chat:messageMessageNew message in conversation

Frontend

Chat Store

typescript
const store = useChatStore()
const { conversations, messages } = storeToRefs(store)

await store.fetchConversations()
await store.createConversation(userId)  // direct
await store.createTaskConversation(taskId)  // task chat
await store.fetchMessages(conversationId)
await store.sendMessage(conversationId, body)

UI Structure

The chat interface has a two-panel layout:

  • Left panel: Conversation list (filtered by type — task chats are shown separately on task pages)
  • Right panel: Message thread with real-time updates

Key Implementation Details

  • Task conversations are created via POST /api/chats/task/:taskId — this is a separate endpoint from direct conversations
  • The task path segment is static, avoiding route conflicts with /direct/:userId
  • Creating a task conversation auto-adds the requesting user as a participant
  • Message delivery is real-time via socket events — no polling
  • Typing indicators show when the other participant is typing
  • The main chat page filters out task-type conversations (shown inline on task detail page instead)