-
Notifications
You must be signed in to change notification settings - Fork 413
MSC3440: Threading via m.thread relation
#3440
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
73 commits
Select commit
Hold shift + click to select a range
beda89e
Threading via relation
germain-gg 2fa27ac
Add explainer on how to handle m.in_reply_to
germain-gg d8a0a94
Clarify wording on threading MSC
germain-gg 7d39887
Mention MSC3051 in the alternative section of MSC3440
germain-gg 6e37911
Clarify updates to MSC2675 for MSC3440
germain-gg c142b17
Line wrap the MSC
germain-gg c578f75
More line wrapping for MSC3440
germain-gg 33acdf4
Clarify single-layer event aggregation section
germain-gg 7102165
Update thread-as-rooms advantages
germain-gg f84f949
Clarify backwards compatibility and incremental support
germain-gg f02dc8d
Clarify wording and correct typos
germain-gg 3e46728
Splitting Cerulean and MSC2836 in alternatives section
germain-gg 44e967f
Add dependencies for threads MSC
germain-gg 65d0d55
Clarify intro to threads as rooms
germain-gg 2b76a6e
Add currentUserParticipated flag
germain-gg 99c5b2e
snake_case over camelCase
germain-gg 4ee42b1
Adding dependency to MSC3567
fc81bbd
Add threads capability
91e6ec7
Merge branch 'main' into gsouquet/threading-via-relations
germain-gg eaeef00
Fix typo
germain-gg 26fb5f2
Update syntax highlighting to use jsonc
germain-gg a23c795
Add limitations when fetching thread content by relation type
germain-gg 6b1a368
Add reply chain fallback via m.in_reply_to
germain-gg f227592
Clarity in wording and fix typo
b493f21
Cosmetic changes based on pull request feedback
germain-gg 46e1e9b
Add note to allow clients to omit fallback for rich replies
germain-gg e40efa0
fix typo
germain-gg 23928e7
Clarify wording to not confuse thread answers with quote-replies
germain-gg 0880a86
move relations justification to alternatives section
germain-gg 1bbb021
Clarify handling of m.in_reply_to missing rel_type:m.thread
germain-gg 3c977f7
Fix typo
germain-gg 0140454
Fix typo
germain-gg 700464c
Declare MSC2781 as a dependency
germain-gg 0035202
Use rich reply over quote reply
germain-gg e3cb699
Depend on MSC3676 rather than MSC2781
ara4n 847f468
Remove full stop typo
germain-gg c8ffa62
Clarify new filtering parameters.
clokep a7cbf8d
Fix typo.
clokep 5896d69
Update wording for client side considerations
germain-gg ee5df80
Add m.in_reply_to mixin to thread fallback
germain-gg 00daf64
Add guidance for clients and servers for thread invalid relations
germain-gg a5d8aab
update thread root wording
germain-gg d7ed3c4
Add better definition to reply target event
germain-gg 5c04906
Add note regarding forward compatibility
germain-gg d667a0b
link to MSC2674
richvdh b157dfd
Update proposals/3440-threading-via-relations.md
germain-gg 3162bea
Clarification on responsibilities for the reply fallback
germain-gg fa232f4
Update `/messages` API endpoint version on example
germain-gg 68d9c42
Apply wording suggestions from code review
germain-gg 5bbb015
Add notes on server-side invalid relation filtering
germain-gg 707af2b
Fix typo
germain-gg b28a365
reword paragraph about forwarding m.thread relation
germain-gg 8f82dfa
Add unstable prefix for capability endpoint
germain-gg 8f8be64
Re-order alternatives to match intro paragraph
germain-gg b6d8076
rework relation_senders and relation_types definition
germain-gg cd671ef
Apply wording suggestions from code review
germain-gg a61c01e
Clarify fallback mechanism
germain-gg 362e661
Rename filter property names
germain-gg b831fb3
Change m.render_in to m.display_reply_fallback
germain-gg e2dde8e
Clarify what endpoints support the new filter
germain-gg e640f6b
Switch from /capabilities to /versions
germain-gg bda3a1e
remove references to Cerulean
germain-gg 89c4b5e
Update latest_event description
germain-gg 61bb518
Clarity in wording and fix typo
germain-gg 9159a5a
rename m.display_reply_fallback to hide_reply
germain-gg a97307a
remove redundant paragraph about forward compat
germain-gg f541dab
Improve bundled relationship example
germain-gg 82b4c62
Explain context on why a thread-unaware client might want to send m.t…
germain-gg 75f4cb2
Clarify `hide_reply`
germain-gg 54ce185
Rename hide_reply to show_reply
germain-gg 6d6baa2
rename show_reply to is_falling_back
germain-gg 893cf1f
Add note about stable support.
clokep 641e326
Update proposals/3440-threading-via-relations.md
germain-gg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,162 @@ | ||
| # MSC3440 Threading via `m.thread` relation | ||
|
|
||
| ## Problem | ||
|
|
||
| Threading is a great way to create alternative timelines to group messages related to each other. This is particularly useful in high traffic rooms where multiple conversations can happen in parallel or when a single discussion might stretch over a very long period of time. | ||
|
|
||
| The main goal when implementing threads is to create conversations that are easier to follow and smoother to read. | ||
|
|
||
| There have been several experiments in threading for Matrix... | ||
|
|
||
| - [MSC2326](https://github.com/matrix-org/matrix-doc/pull/2326): Label based filtering | ||
| - [MSC2836](https://github.com/matrix-org/matrix-doc/pull/2836): Threading by serverside traversal of relationships | ||
| - "Threads as rooms" | ||
|
|
||
| ...but none have yet been widely adopted due to the upheaval of implementing an entirely new Client-Server API construct in the core message sending/display path. | ||
|
|
||
| Meanwhile, threading is very clearly a core requirement for any modern messaging solution, and Matrix uptake is suffering due to the lack of progress. | ||
|
|
||
| ## Proposal | ||
|
|
||
| ### Event format | ||
|
|
||
| A new relation would be used to express that an event belongs to a thread. | ||
|
|
||
| ```json | ||
| "m.relates_to": { | ||
| "rel_type": "m.thread", | ||
| "event_id": "$thread_root" | ||
| } | ||
| ``` | ||
| Where $thread_root is the event ID of the root message in the thread. | ||
|
|
||
| A big advantage of relations over quote replies is that they can be server-side aggregated. It means that a client is not bound to download the entire history of a room to have a comprehensive list of events being part of a thread. | ||
|
|
||
| It will have a custom aggregation which is a summary of the thread: the latest message, a list of participants and the total count of messages. I.e. in places which include bundled relations (per [MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675)), the thread root would include additional information in the `unsigned` field: | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ```json | ||
| { | ||
| "latest_event": { | ||
| "content": { ... }, | ||
| ... | ||
| }, | ||
| "senders": ["@john:example.com", ...], | ||
| "count": 7 | ||
| } | ||
| ``` | ||
|
|
||
| #### Quote replies in a thread | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| No recommendation to modifying quote replies is made, this would still be handled via the `m.in_reply_to` field of `m.relates_to`. Thus you could quote a reply in a thread: | ||
|
|
||
| ```json | ||
| "m.relates_to": { | ||
| "rel_type": "m.thread", | ||
| "event_id": "$thread_root", | ||
| "m.in_reply_to": { | ||
| "event_id": "$event_target" | ||
| } | ||
| } | ||
| ``` | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Fetch all replies to a thread | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| To fetch an entire thread, the `/relations` API can be used as defined in [MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675) | ||
|
|
||
| ``` | ||
| GET /_matrix/client/unstable/rooms/!room_id:domain/relations/$thread_root/m.thread | ||
| ``` | ||
novocaine marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| Where `$thread_root` is the event ID of the root message in the thread. | ||
|
|
||
| [MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675) would need to be clarified to ensure that returned messages include any bundled relations necessary for displaying the thread, e.g. reactions to the threaded events. | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Fetch all threads in a room | ||
|
|
||
| To fetch all threads in a room it is proposed to use the [`/messages`](https://matrix.org/docs/spec/client_server/latest#get-matrix-client-r0-rooms-roomid-messages) API and expand the room event filtering to include relations. The `RoomEventFilter` will take additional parameters: | ||
|
|
||
| * `relation_types`: A list of relation types which must be bundled with the event to include it. If this list is absent then no filtering is done on relation types. | ||
| * `relation_senders`: A list of senders of relations... | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| This can also be combined with the `sender` field to search for threads which a user has participated in (or not participated in). | ||
|
|
||
| ``` | ||
| GET /_matrix/client/unstable/rooms/!room_id:domain/messages/filter=... | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ``` | ||
|
|
||
| Where filter would be JSON and URL-encoded string include the above new fields: | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ```json | ||
| { | ||
| "types": ["m.room.message"], | ||
| "relation_senders": [...], | ||
| "relation_types": ["m.thread"] | ||
| } | ||
| ``` | ||
|
|
||
| ### Limitations | ||
germain-gg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| #### Read receipts | ||
|
|
||
| Read receipts and read markers assume a single chronological timeline. Threading changes that assumption making the current API not very practical. | ||
|
|
||
| Clients can synthetize read receipts but it is possible that some notification get lost upon a fresh start where the clients have to start off the `m.read` information received from the homeserver. | ||
|
|
||
| Synchronising the synthesized notification count across devices will present its own challenges and is probably undesirable at this stage. The preferred route would be to create another MSC to make read receipts support multiple timelines in a single room. | ||
|
|
||
| #### Single-layer event aggration | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Bundling only includes relations a single-layer deep, so it might return an event with `m.thread` relation, the `m.annotation` to that event and the `m.reference` relations to that event, but not the `m.annotation` to the `m.reference`. | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Client considerations | ||
|
|
||
| #### Display "m.thread" as "m.in_reply_to" | ||
|
|
||
| It is possible for clients to provide a backwards compatible experience for users by treating the new relation `m.thread` the same way they would treat a `m.in_reply_to` event. | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Failing to do the above will still render the event in the room. It might create a disjointed experience as events might lack the original context for correct understanding. | ||
|
|
||
| #### Sending `m.thread` before fully implementing threads | ||
richvdh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| To ensure maximum compability and continuity in the conversation it is recommend for clients who do not fully support threads yet to adapt the `m.relates_to` body to use the `m.thread` relation type when replying to a threaded event. | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| This will help threads enabled clients to render the event in the place that gives the maximum context to the users. | ||
|
|
||
| As a fallback, it is recommended that clients make clicking on a quote reply related to a threaded event open that thread and highlight the corresponding event | ||
|
|
||
germain-gg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ## Alternatives | ||
germain-gg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| [MSC2836](https://github.com/matrix-org/matrix-doc/pull/2836), "Threading as rooms", building on `m.in_reply_to` are the main alternatives here. The first two are non-overlapping with this MSC. | ||
|
|
||
| ### Threads as rooms | ||
|
|
||
| The provides full server-side APIs for navigating trees of events, and could be considered an extension of this MSC for scenarios which require that capability (e.g. Twitter-style microblogging as per [Cerulean](https://matrix.org/blog/2020/12/18/introducing-cerulean), or building an NNTP or IMAP or Reddit style threaded UI) | ||
|
|
||
| "Threads as rooms" is the idea that each thread could just get its own Matrix room in parallel with the one which spawned the thread. | ||
|
|
||
| Advantages to "Threads as rooms" include: | ||
| * May be simpler for client implementations. | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| * Also requires minimal Client-Server API changes | ||
germain-gg marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Disadvantages include: | ||
| * Access control, membership, history visibility, room versions etc needs to be synced between the thread-room and the parent room | ||
| * Harder to control lifetime of threads in the context of the parent room if they're completely split off | ||
| * Clients which aren't aware of them are going to fill up with a lot of rooms. | ||
| * Bridging to non-threaded chat systems is trickier as you may have to splice together rooms | ||
| * The sheer number of rooms involved probably makes it dependent on `/sync` v3 landing (the to-be-specced next generation of `/sync` which is constant-time complexity with your room count). | ||
|
|
||
| ### Threads via m.in_reply_to | ||
|
|
||
| The rationale for using a new relation type instead of building on `m.in_reply_to` is to re-use the event relationship APIs provided by [MSC2675](https://github.com/matrix-org/matrix-doc/pull/2675). The MSC3267 definition of `m.reference` relationships could be updated to mention threads (perhaps by using the key field from [MSC2677](https://github.com/matrix-org/matrix-doc/pull/2677) as the thread ID), but it is clearer to define a new relation type. It is unclear what impact this would have on [MSC3267](https://github.com/matrix-org/matrix-doc/pull/3267), but that is unimplemented by clients. | ||
|
|
||
| ## Security considerations | ||
germain-gg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| None | ||
|
|
||
| ## Unstable prefix | ||
|
|
||
| Clients and servers should use list of unstable prefixes listed below while this MSC has not been included in a spec release. | ||
|
|
||
| * `io.element.thread` should be used in place of `m.thread` as relation type | ||
| * `io.element.relation_senders` should be used in place of `relation_senders` in the `RoomEventFilter` | ||
| * `io.element.relation_types` should be used in place of `relation_types` in the `RoomEventFilter` | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.