Feature: Message Threads #21
Labels
No labels
area:api
area:core
area:docs
area:infra
area:ux
dependencies
documentation
duplicate
good first issue
help wanted
invalid
question
rust
status:complete
status:partial
status:planned
type:bug
type:design
type:feature
type:infra
type:refactor
type:research
type:ux
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
icub3d/decentcom#21
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Migrated from GitHub issue icub3d/decentcom#21
Original Author: @icub3d
Original Date: 2026-04-15T14:15:47Z
Feature: Message Threads
Overview
Allow users to start threaded conversations on any message. Threads provide focused sub-conversations without cluttering the main channel timeline. Thread replies are visible in a dedicated thread view panel, with unread tracking and notifications so users can follow threads they care about.
Background
The server model (
docs/design/server-model.md) definesmessage_threadsas a feature flag (enabled by default). The existing message model supports flat channel-based messaging; threads add a parent-child relationship between messages. The gateway (docs/design/architecture.md) already handles real-time message delivery — thread messages are delivered as events scoped to the thread. Unread tracking builds on the message delivery infrastructure.Requirements
thread_idmessage_threadsfeature flag gates thread creation; when disabled, thread endpoints return 403Design
API / Interface Changes
New REST endpoints:
POST /api/v1/channels/{channel_id}/messages/{message_id}/threads201 Createdwith{ thread_id, parent_message_id, channel_id, created_at, reply_count, follower_count }GET /api/v1/threads/{thread_id}/messageslimit,before,after(cursor-based).200 OKwith{ messages: [...], cursor }POST /api/v1/threads/{thread_id}/messagescontent,attachment_ids).201 Createdwith the message object.PUT /api/v1/threads/{thread_id}/follow200 OKDELETE /api/v1/threads/{thread_id}/follow204 No ContentGET /api/v1/threads/{thread_id}200 OKModified endpoints:
GET /api/v1/channels/{channel_id}/messages— Message objects now include optional thread metadata:New gateway events:
THREAD_MESSAGE_CREATEis sent to users who are subscribed to the thread (have it open or are following it).THREAD_UPDATEis sent to all users in the parent channel so the reply count indicator stays current.Data Model Changes
New table:
threadsNew table:
thread_followersModified table:
messagesAdd a nullable
thread_idcolumn:Messages with a non-null
thread_idare thread replies. Messages with a nullthread_idare regular channel messages.Component Changes
Server (
server/):server/src/models/thread.rs— New module:Threadstruct,ThreadFollowerstruct, CRUD operations, reply count updates, follower managementserver/src/routes/threads.rs— New module: thread creation, message listing, message posting, follow/unfollow, thread metadata endpointsserver/src/routes/messages.rs— Modify message serialization to include thread summary (reply count, last reply) when a thread existsserver/src/gateway/events.rs— AddTHREAD_CREATE,THREAD_MESSAGE_CREATE,THREAD_UPDATEevent typesserver/src/gateway/handler.rs— Manage thread subscriptions: users who open a thread subscribe to its events; thread followers receiveTHREAD_MESSAGE_CREATEeven if they don't have the thread panel openserver/src/storage/sqlite/migrations/— Migrations forthreads,thread_followerstables andmessages.thread_idcolumnClient (
client/):client/src/components/ThreadPanel.tsx— New component: slide-in panel showing the parent message at the top, thread replies below, message input at the bottomclient/src/components/ThreadIndicator.tsx— New component: shows "N replies" badge on messages that have threads, with participant avatars and last reply timeclient/src/components/Message.tsx— AddThreadIndicatorrendering, "Reply in thread" button on message hoverclient/src/stores/threadStore.ts— New store: active thread state, thread message list, follow state, unread counts per threadclient/src/api/threads.ts— API client functions for thread CRUD, follow/unfollow, message fetchingclient/src/gateway/handlers.ts— Add handlers forTHREAD_CREATE,THREAD_MESSAGE_CREATE,THREAD_UPDATEeventsclient/src/components/ChannelView.tsx— Layout adjustment: when a thread is open, renderThreadPanelalongside the main message list (split view)Task List
Server
threadsandthread_followerstables andmessages.thread_idcolumn to SQLite migrations (server/migrations/010_threads.sql)Thread,ThreadFollower,ThreadSummarymodels inserver/src/storage/models.rsThreadStoretrait and SQLite impl inserver/src/storage/sqlite/threads.rsPOST /api/v1/channels/{channel_id}/messages/{message_id}/threads— create thread, idempotent, broadcastsTHREAD_CREATEGET /api/v1/threads/{thread_id}/messageswith cursor-based pagination (oldest-first)POST /api/v1/threads/{thread_id}/messages— create reply, increments reply_count, auto-follows sender, broadcastsTHREAD_MESSAGE_CREATEandTHREAD_UPDATEPUT/DELETE /api/v1/threads/{thread_id}/followfor follow/unfollowGET /api/v1/threads/{thread_id}for thread metadataTHREAD_CREATE,THREAD_MESSAGE_CREATE,THREAD_UPDATEadded to gateway Op enumTHREAD_UPDATEbroadcast to channel subscribers when reply count changesenrich_messagesmessage_threadsfeature flagClient
client/src/api/threads.tsthreadStore.tswith active thread ID, thread messages, follow state, and per-thread unread countsTHREAD_CREATE,THREAD_MESSAGE_CREATE,THREAD_UPDATEThreadIndicator.tsx— reply count badge with participant avatars, clickable to open threadThreadPanel.tsx— parent message header, scrollable reply list, message input, follow/unfollow toggleMessage.tsxThreadIndicatorintoMessage.tsxfor messages with threadsChannelView.tsxlayout for side-by-side thread panel (resizable split)last_read_aton thread follower record when user views the threadTest List
last_reply_atget_thread_summariesreturns summaries for parent message IDsroutes/thread_tests.rs)message_threadsfeature flag is disabledTHREAD_CREATEevent is broadcast to channel subscribersTHREAD_MESSAGE_CREATEevent is sent to thread followersOpen Questions