feat(threads): Slack-style thread panel for rooms and DMs #36
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/threads"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
messages.parent_id(migration 0011); top-level messages keepparent_id IS NULL, replies hang off a single parent (one level deep, enforced at the route layer).GET /room/:rid/thread/:midopens a side panel with the parent + replies + composer;POST /room/:rid/thread/:mid/messagesposts a reply;DELETE /thread-panelcloses the drawer. The same handlers cover DMs since access is gated byis_room_accessible.N replies) renders under each top-level message; click reopens the panel. Pill updates live via OOB swap when a reply lands.ChatEvent::ThreadReply { parent_id, message }fans out to room recipients; per-recipient renderer emits the reply fragment (only takes effect if the panel is open for that parent) plus an OOB reply-count update for the main feed.(room_id, parent_id, user_id)map in the hub, plumbed through newThreadTyping/ThreadStoppedTypingevents and athread_typingclient control frame, so room-composer typing and thread-composer typing never bleed into each other's UI.Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- Add `messages.parent_id` (migration 0011); top-level messages keep `parent_id IS NULL`, replies hang off a single parent (one level deep, enforced at the route layer). - Scope main feed loaders, prior/next message lookups, and unread counts to top-level messages so thread replies do not bleed into the room timeline or sidebar badges. - New routes: `GET /room/:rid/thread/:mid` opens a side panel with the parent + replies + composer; `POST /room/:rid/thread/:mid/messages` posts a reply; `DELETE /thread-panel` closes the drawer. The same handlers cover DMs since access is gated by `is_room_accessible`. - Reply pill (`N replies`) renders under each top-level message; click reopens the panel. Pill updates live via OOB swap when a reply lands. - Realtime: `ChatEvent::ThreadReply { parent_id, message }` fans out to room recipients; per-recipient renderer emits the reply fragment (only takes effect if the panel is open for that parent) plus an OOB reply-count update for the main feed. - Typing indicators are scoped per-thread via a separate `(room_id, parent_id, user_id)` map in the hub, plumbed through new `ThreadTyping`/`ThreadStoppedTyping` events and a `thread_typing` client control frame, so room-composer typing and thread-composer typing never bleed into each other's UI. - Stop typing on send mirrors the room composer behavior added in #35. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>