I recently wrapped up a project on improving notifications in threads for Matrix. This is adapted from my research notes to understand the status quo before adapting the Matrix protocol for threads (in MSC3771 and MSC3773). Hopefully others find the information useful!
Note
These notes are true as of the v1.3 of the Matrix spec and also cover some Matrix spec changes which may or may not have been merged since. It is known to be out of date with the changes from MSC2285, MSC3771, and MSC3773.
Matrix uses “receipts” to note which part of a room has been read by a user. It considers the history for a room to be split into three sections1:
The fully read marker is between 1 & 2 while the read receipt is between 2 & 3. Note that fully read markers are not shared with other users while read receipts are.
Another way to consider this is2:
They are stored in the room account data for the user (but modified via a separate API).
The API is:
POST /_matrix/client/v3/rooms/{roomId}/read_markers
The read receipt can optionally be updated at the same time.
In Element Web your fully read marker is displayed as the green line across the conversation.
Only m.read
is defined at the moment, but it is meant to be generic infrastructure.
Updating a read receipt updates a “marker” which causes any notifications prior to and including the event to be marked as read.3 A user has a single read receipt “marker” per room.
Passed to clients as an m.receipt
event under the ephemeral
array for each
room in the /sync
response. The event includes a map of event ID -> receipt type
-> user ID -> data (currently just a timestamp). Note that the event is a delta
from previous events. An example:
{
"content": {
"$1435641916114394fHBLK:matrix.org": {
"m.read": {
"@rikj:jki.re": {
"ts": 1436451550453
}
}
}
},
"room_id": "!jEsUZKDJdhlrceRyVU:example.org",
"type": "m.receipt"
}
The API is:
POST /_matrix/client/v3/rooms/{roomId}/receipt/{receiptType}/{eventId}
In Element Web read receipts are the small avatars on the right hand side of the conversation. Note that your own read receipt is not shown.
A new receipt type (m.read.hidden
) to set a read receipt without sharing it with
other users. It also modifies the /read_markers
API to accept the new receipt type
and modifies the /receipts
API to accept the fully read marker.
This is useful to synchronize notifications across devices while keeping read status private.
A user’s push rules determine one or more user-specific actions on each event. They are customizable, but the homeserver provides default rules. They can result in an action to:
notify
action), which can have additional actions (“tweaks”):highlight
action)sound
action)By default, all new m.room.message
and m.room.encrypted
events generate a
notification, events with a user’s display name or username in them or @room
generate highlights, etc.
Augments push rules to allow applying them to the target of an event relationship and defines a default push rule for replies (not using the reply fallback).
A proposed replacement for push rules, the results are essentially the same
actions (and presumedly would not change the data returned in /sync
, see below).
/sync
The number of notification events and highlight events since the user’s last read
receipt are both returned on a per room basis as part of a /sync
response (for
joined room).
Notification and highlight events are any messages where the push rules resulted
in an action of notify
or highlight
, respectively. (Note that a highlight
action must be a notify
action, thus highlight_count <= notification_count
.)
An example:
{
"account_data": [...],
"ephemeral": [...],
"state": [...],
"summary": {...},
"timeline": {...},
"unread_notifications": {
"highlight_count": 0,
"notification_count": 0
}
}
A new field is added under the unread_notifications
field (unread_count
) which
is the total number of events matching particular criteria since the user’s last
read receipt.
This replaces MSC2625, which adds a new push rule action (mark_unread
) to
perform the same task. In this rendition, notify
implies mark_unread
and thus
highlight_count <= notification_count <= unread_count
.
Push notifications receive either the number of unread messages (across all rooms)
or the number of rooms with unread messages (depending on the value of
push.group_unread_count_by_room
in the Synapse configuration). Unread messages
are any messages where the push rules resulted in an action of notify
.
This information is sent from the homeserver to the push gateway as part of every notification:
{
"notifications": {
"counts": {
"unread": 1,
...
},
...
}
}
From the fully read marker specification. ↩
See a discussion on MSC2285. ↩