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:
join,leave,typing(client→server);chat:typing,chat:message,chat:created(server→client)
API Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/chats | List user conversations |
POST | /api/chats | Create conversation |
POST | /api/chats/direct/:userId | Create or get direct conversation |
POST | /api/chats/task/:taskId | Create or get task conversation |
GET | /api/chats/:id/messages | List messages (cursor pagination: ?before=&limit=) |
POST | /api/chats/:id/messages | Send message |
POST | /api/chats/:id/messages/attachments | Upload attachments (up to 10 files) |
POST | /api/chats/:id/read | Mark conversation as read |
Socket.IO Events
Client → Server
| Event | Payload | Description |
|---|---|---|
join | conversationId: string | Join conversation room |
leave | conversationId: string | Leave conversation room |
typing | conversationId: string | Typing indicator |
Server → Client
| Event | Payload | Description |
|---|---|---|
chat:typing | { conversationId, userId } | Another user is typing |
Messages are sent via REST (POST /api/chats/:id/messages), real-time delivery is handled by sendToConversation() on the server.
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 display inline on task detail pages)
- Right panel: Message stream with real-time updates
Voice Messages
Chat supports voice message recording and playback:
VoiceRecorder.vue— records audio via the MediaRecorder API, uploads as a file attachmentAudioPlayer.vue— custom audio player for voice message playback with waveform display- Voice messages are uploaded via
POST /api/chats/:id/messages/attachmentswith avoiceflag - Duration metadata is stored alongside the audio file
Key Implementation Details
- Task conversations are created via
POST /api/chats/task/:taskId— a separate endpoint from direct conversations - The
taskpath segment is static, avoiding route conflicts with/direct/:userId - Creating a task conversation automatically adds the requesting user as a participant
- Message delivery is real-time via socket events — no polling
- Typing indicators show when another user is typing
- The main chat page filters out task-type conversations (they display inline on task detail pages)