You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of status-im/status-app#21257
Implements a first-class thread model for community chat channels. Threads are anchored to an existing parent message and inherit the channel's permissions and encryption. The feature is gated behind a `Threads` feature flag (off by default).
---
### Schema & protocol
- **`chat_message.proto`** — added optional `thread_id` field (field 21) to
`ChatMessage`. Old clients silently ignore the field; new clients write to
and read from it transparently.
- **`communities.proto`** — added `create_thread_all_members_enabled` boolean
to `CommunityAdminSettings`, controlling who may start a new thread.
- **DB migrations**
- `1764000001` — `ALTER TABLE user_messages ADD COLUMN thread_id TEXT` with
indexes on `(local_chat_id, thread_id)` and `(thread_id)`.
- `1764000002` — `CREATE TABLE threads (thread_id, chat_id,
parent_message_id, name)` with an index on `(chat_id, name)`.
---
### Persistence (`protocol/message_persistence.go`)
- New `Thread` struct with JSON tags (`threadId`, `chatId`,
`parentMessageId`, `name`).
- `SaveMessages` now auto-creates or updates thread metadata whenever a
message with a non-empty `thread_id` is saved. If the parent message is
not yet known the thread name is stored as an empty string; when the
parent arrives, `updateThreadNameFromParentIfNeeded` back-fills the name.
- Thread name is normalised to a 40-character trimmed excerpt of the parent
message text. Unknown/placeholder names are **empty string** — the client
controls the display label.
- New persistence helpers: `UpsertThread`, `ThreadByID`, `ThreadsByChatID`,
`MessagesByThreadID`, `threadNameFromParentByID`.
---
### Community & request layer
- `AllowsAllMembersToCreateThread()` helper on `Community`.
- `CreateCommunityRequest` / `EditCommunityRequest` now forward
`CreateThreadAllMembersEnabled`.
---
### Messenger (`protocol/messenger.go`)
- `sendChatMessage` enforces the community thread-creation permission gate:
only admins may start a new thread unless `AllowsAllMembersToCreateThread`
is set.
- **`CreateThread(chatID, parentMessageID)`** — explicit thread creation API.
Requires the parent message to already exist locally; returns
`"parent message not found"` otherwise. Applies the same permission gate
as `sendChatMessage`.
- **`ThreadsByChatID(chatID)`** — lists all threads for a channel.
- **`MessagesByThreadID(chatID, threadID, cursor, limit)`** — paginated
thread message history.
- `addThreadsToResponse` enriches every `MessengerResponse` (send path and
retrieve/signal path) with the thread metadata for any thread-tagged
messages in the response.
---
### MessengerResponse (`protocol/messenger_response.go`)
- New private `threads map[string]*Thread` field.
- `AddThread` / `AddThreads` / `Threads()` accessors.
- `threads` included in `MarshalJSON` output (`"threads"` key), `IsEmpty`,
and `Merge`.
---
### RPC API (`services/ext/api.go`)
Three new `PublicAPI` methods exposed over JSON-RPC under the `wakuext`
namespace:
| Method | Description |
|--------|-------------|
| `wakuext_createThread` | Create thread from an existing parent message |
| `wakuext_chatThreads` | List all threads for a chat |
`wakuext_sendChatMessage` now accepts an optional `threadId` field.
---
### Tests added
- `TestSaveMessagesCreatesThreadWithEmptyNameWhenParentMissing`
- `TestSaveMessagesUpdatesEmptyThreadNameWhenParentArrives`
- `TestMessagesByThreadID`
- `TestDeleteParentMessageKeepsThreadMetadata`
- `TestCreateThreadFromExistingParent`
- `TestCreateThreadFailsWhenParentMessageMissing`
- `TestMessengerResponseMergeThreads`
- `TestMessengerResponseMarshalJSONIncludesThreads`
---
### API reference
`cmd/status-backend/API_REFERENCE.md` updated with request/response examples
and error tables for all three new RPC methods.
Copy file name to clipboardExpand all lines: cmd/status-backend/API_REFERENCE.md
+93-1Lines changed: 93 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -319,10 +319,15 @@ Send a text message to a chat/channel.
319
319
[{
320
320
"chatId": "<chatId>",
321
321
"text": "Hello!",
322
-
"contentType": 1
322
+
"contentType": 1,
323
+
"threadId": "<parentMessageId-optional>"
323
324
}]
324
325
```
325
326
327
+
-`threadId` is optional.
328
+
- Use `threadId` to send a message into an existing thread.
329
+
- If `threadId` points to a parent that is not yet known locally, the backend still creates thread metadata and backfills the name when the parent arrives.
330
+
326
331
**Chat ID formats:**
327
332
328
333
-**Community channel:** concatenation of community ID and chat UUID without separator:
@@ -346,6 +351,93 @@ Read message history for a chat.
346
351
347
352
---
348
353
354
+
### wakuext_createThread
355
+
356
+
Explicitly create a thread from an existing parent message. The parent message must already be stored locally (i.e. it was previously received or sent).
357
+
358
+
For community chats, the caller must be a privileged member (admin/owner) unless the community has "create threads for all members" enabled.
359
+
360
+
**Params:**`[chatId, parentMessageId]`
361
+
-`chatId`: string — channel/chat identifier.
362
+
-`parentMessageId`: string — ID of the message that becomes the thread root.
363
+
364
+
**Result:**
365
+
```json
366
+
{
367
+
"threads": [
368
+
{
369
+
"threadId": "0x-parent-message-id",
370
+
"chatId": "0xcommunity...<chatUUID>",
371
+
"parentMessageId": "0x-parent-message-id",
372
+
"name": "First 40 chars of parent text (or empty)"
373
+
}
374
+
]
375
+
}
376
+
```
377
+
378
+
**Errors:**
379
+
-`"threads feature is disabled"` — feature flag is off.
380
+
-`"chatID and parentMessageID are required"` — missing params.
381
+
-`"thread already exists for this message"` — thread already created for this parent.
382
+
-`"parent message not found"` — parent message doesn't exist locally.
383
+
-`"only admins can create threads in this community"` — permission denied.
384
+
385
+
---
386
+
387
+
### wakuext_chatThreads
388
+
389
+
List threads for a chat.
390
+
391
+
**Params:**`[chatId]`
392
+
-`chatId`: string (communityId + chatUUID)
393
+
394
+
**Result:**
395
+
```json
396
+
{
397
+
"threads": [
398
+
{
399
+
"threadId": "0x-parent-message-id",
400
+
"chatId": "0xcommunity...<chatUUID>",
401
+
"parentMessageId": "0x-parent-message-id",
402
+
"name": "Parent message text (or empty until known)"
403
+
}
404
+
]
405
+
}
406
+
```
407
+
408
+
Notes:
409
+
-`name` may be empty when the parent message has not been persisted yet.
410
+
- Unknown/placeholder names are intentionally client-defined; backend returns empty string until resolved.
411
+
412
+
---
413
+
414
+
### wakuext_threadMessages
415
+
416
+
Read message history for a single thread.
417
+
418
+
**Params:**`[chatId, threadId, cursor, limit]`
419
+
-`chatId`: string (communityId + chatUUID)
420
+
-`threadId`: string (thread identifier; currently parent message ID)
0 commit comments