Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 956263c

Browse files
committed
Store unread messages in the database
1 parent d2b0721 commit 956263c

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

changelog.d/7736.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add unread messages count to sync responses.

synapse/storage/data_stores/main/events.py

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from canonicaljson import json
2626
from prometheus_client import Counter
2727

28+
from twisted.enterprise.adbapi import Connection
2829
from twisted.internet import defer
2930

3031
import synapse.metrics
@@ -61,6 +62,12 @@
6162
["type", "origin_type", "origin_entity"],
6263
)
6364

65+
STATE_EVENT_TYPES_TO_MARK_UNREAD = [
66+
EventTypes.PowerLevels,
67+
EventTypes.Topic,
68+
EventTypes.Name,
69+
]
70+
6471

6572
def encode_json(json_object):
6673
"""
@@ -977,7 +984,7 @@ def _update_metadata_tables_txn(
977984
txn, events=[event for event, _ in events_and_contexts]
978985
)
979986

980-
for event, _ in events_and_contexts:
987+
for event, context in events_and_contexts:
981988
if event.type == EventTypes.Name:
982989
# Insert into the event_search table.
983990
self._store_room_name_txn(txn, event)
@@ -1009,6 +1016,8 @@ def _update_metadata_tables_txn(
10091016
if isinstance(expiry_ts, int) and not event.is_state():
10101017
self._insert_event_expiry_txn(txn, event.event_id, expiry_ts)
10111018

1019+
self._maybe_insert_unread_event_txn(txn, event, context)
1020+
10121021
# Insert into the room_memberships table.
10131022
self._store_room_members_txn(
10141023
txn,
@@ -1614,3 +1623,72 @@ def f(txn, stream_ordering):
16141623
await self.db.runInteraction("locally_reject_invite", f, stream_ordering)
16151624

16161625
return stream_ordering
1626+
1627+
def _maybe_insert_unread_event_txn(
1628+
self, txn: Connection, event: EventBase, context: EventContext,
1629+
):
1630+
"""Mark the event as unread for every current member of the room if it passes the
1631+
conditions for that.
1632+
1633+
These conditions are: the event must either have a body, be an encrypted message,
1634+
or be either a power levels event, a room name event or a room topic event, and
1635+
must be neither rejected or soft-failed nor an edit or a notice.
1636+
1637+
Args:
1638+
txn: The transaction to use to retrieve room members and to mark the event
1639+
as unread.
1640+
event: The event to evaluate and maybe mark as unread.
1641+
context: The context in which the event was sent (used to figure out whether
1642+
the event has been rejected).
1643+
"""
1644+
content = event.content
1645+
1646+
is_edit = (
1647+
content.get("m.relates_to", {}).get("rel_type") == RelationTypes.REPLACE
1648+
)
1649+
is_notice = not event.is_state() and content.get("msgtype") == "m.notice"
1650+
1651+
# We don't want rejected or soft-failed events, edits or notices to be marked
1652+
# unread.
1653+
if (
1654+
context.rejected
1655+
or is_edit
1656+
or is_notice
1657+
or event.internal_metadata.is_soft_failed()
1658+
):
1659+
return
1660+
1661+
body_exists = content.get("body") is not None
1662+
is_state_event_to_mark_unread = (
1663+
event.is_state() and event.type in STATE_EVENT_TYPES_TO_MARK_UNREAD
1664+
)
1665+
is_encrypted_message = (
1666+
not event.is_state() and event.type == EventTypes.Encrypted
1667+
)
1668+
1669+
# We want to mark unread messages with a body, some state events (power levels,
1670+
# room name, room topic) and encrypted messages.
1671+
if not (body_exists or is_state_event_to_mark_unread or is_encrypted_message):
1672+
return
1673+
1674+
# Get the list of users that are currently joined to the room.
1675+
users_in_room = self.db.simple_select_onecol_txn(
1676+
txn=txn,
1677+
table="room_memberships",
1678+
keyvalues={"membership": Membership.JOIN, "room_id": event.room_id},
1679+
retcol="user_id",
1680+
)
1681+
1682+
# Mark the message as unread for every user currently in the room.
1683+
self.db.simple_insert_many_txn(
1684+
txn=txn,
1685+
table="unread_messages",
1686+
values=[
1687+
{
1688+
"user_id": user_id,
1689+
"stream_ordering": event.internal_metadata.stream_ordering,
1690+
"room_id": event.room_id,
1691+
}
1692+
for user_id in users_in_room
1693+
],
1694+
)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/* Copyright 2020 The Matrix.org Foundation C.I.C
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
CREATE TABLE IF NOT EXISTS unread_messages(
17+
user_id TEXT NOT NULL, -- The user for which the message is unread.
18+
stream_ordering BIGINT NOT NULL, -- The position of the message in the event stream.
19+
room_id TEXT NOT NULL,
20+
UNIQUE (user_id, stream_ordering)
21+
);

0 commit comments

Comments
 (0)