diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py
index b681d4fb..849cf2d6 100644
--- a/src/backend/matrix_client.py
+++ b/src/backend/matrix_client.py
@@ -1142,6 +1142,18 @@ class MatrixClient(nio.AsyncClient):
response = await super().join(string)
return response.room_id
+ async def toggle_bookmark(self, room_id: str) -> None:
+ room = self.models[self.user_id, "rooms"][room_id]
+ room.bookmarked = not room.bookmarked
+
+ settings = self.backend.ui_settings
+ bookmarks = settings["roomBookmarkIDs"].setdefault(self.user_id, [])
+ if room.bookmarked and room_id not in bookmarks:
+ bookmarks.append(room_id)
+ elif not room.bookmarked and room_id in bookmarks:
+ bookmarks.remove(room_id)
+
+ await self.backend.ui_settings.write(self.backend.ui_settings._data)
async def room_forget(self, room_id: str) -> None:
"""Leave a joined room (or decline an invite) and forget its history.
@@ -1854,6 +1866,7 @@ class MatrixClient(nio.AsyncClient):
)
unverified_devices = registered.unverified_devices
+ bookmarks = self.backend.ui_settings["roomBookmarkIDs"]
room_item = Room(
id = room.room_id,
for_account = self.user_id,
@@ -1900,6 +1913,7 @@ class MatrixClient(nio.AsyncClient):
local_highlights = local_highlights,
lexical_sorting = self.backend.ui_settings["lexicalRoomSorting"],
+ bookmarked = room.room_id in bookmarks.get(self.user_id, {}),
)
self.models[self.user_id, "rooms"][room.room_id] = room_item
diff --git a/src/backend/models/items.py b/src/backend/models/items.py
index efefa0e2..123ef63f 100644
--- a/src/backend/models/items.py
+++ b/src/backend/models/items.py
@@ -132,6 +132,7 @@ class Room(ModelItem):
local_highlights: bool = False
lexical_sorting: bool = False
+ bookmarked: bool = False
def __lt__(self, other: "Room") -> bool:
"""Sort by membership, highlights/unread events, last event date, name.
@@ -146,11 +147,13 @@ class Room(ModelItem):
if self.lexical_sorting:
return (
self.for_account,
+ other.bookmarked,
self.left,
other.inviter_id,
(self.display_name or self.id).lower(),
) < (
other.for_account,
+ self.bookmarked,
other.left,
self.inviter_id,
(other.display_name or other.id).lower(),
@@ -159,6 +162,7 @@ class Room(ModelItem):
# Left rooms may still have an inviter_id, so check left first.
return (
self.for_account,
+ other.bookmarked,
self.left,
other.inviter_id,
bool(other.highlights),
@@ -170,6 +174,7 @@ class Room(ModelItem):
) < (
other.for_account,
+ self.bookmarked,
other.left,
self.inviter_id,
bool(self.highlights),
@@ -192,6 +197,7 @@ class AccountOrRoom(Account, Room):
self.account_order,
self.id if self.type is Account else self.for_account,
other.type is Account,
+ other.bookmarked,
self.left,
other.inviter_id,
(self.display_name or self.id).lower(),
@@ -199,6 +205,7 @@ class AccountOrRoom(Account, Room):
other.account_order,
other.id if other.type is Account else other.for_account,
self.type is Account,
+ self.bookmarked,
other.left,
self.inviter_id,
(other.display_name or other.id).lower(),
@@ -208,6 +215,7 @@ class AccountOrRoom(Account, Room):
self.account_order,
self.id if self.type is Account else self.for_account,
other.type is Account,
+ other.bookmarked,
self.left,
other.inviter_id,
bool(other.highlights),
@@ -221,6 +229,7 @@ class AccountOrRoom(Account, Room):
other.account_order,
other.id if other.type is Account else other.for_account,
self.type is Account,
+ self.bookmarked,
other.left,
self.inviter_id,
bool(self.highlights),
diff --git a/src/backend/user_files.py b/src/backend/user_files.py
index c76b5612..6799de28 100644
--- a/src/backend/user_files.py
+++ b/src/backend/user_files.py
@@ -291,6 +291,7 @@ class UISettings(JSONDataFile):
"theme": "Midnight.qpl",
"writeAliases": {},
"zoom": 1.0,
+ "roomBookmarkIDs": {},
"media": {
"autoLoad": True,
diff --git a/src/gui/MainPane/RoomDelegate.qml b/src/gui/MainPane/RoomDelegate.qml
index c2a7d764..730db110 100644
--- a/src/gui/MainPane/RoomDelegate.qml
+++ b/src/gui/MainPane/RoomDelegate.qml
@@ -63,7 +63,9 @@ HTile {
spacing: room.spacing
TitleLabel {
- text: model.display_name || qsTr("Empty room")
+ text:
+ (model.bookmarked ? "\u2665 " : "") +
+ (model.display_name || qsTr("Empty room"))
color:
model.local_unreads ?
theme.mainPane.listView.room.unreadName :
@@ -150,6 +152,14 @@ HTile {
})
}
+ HMenuItem {
+ icon.name: model.bookmarked ? "bookmark-remove": "bookmark-add"
+ text: model.bookmarked ? qsTr("Remove bookmark"): qsTr("Bookmark")
+ onTriggered: py.callClientCoro(
+ model.for_account, "toggle_bookmark", [model.id]
+ )
+ }
+
HMenuItem {
icon.name: "copy-room-id"
text: qsTr("Copy room ID")
diff --git a/src/icons/thin/bookmark-add.svg b/src/icons/thin/bookmark-add.svg
new file mode 100644
index 00000000..81bfb36d
--- /dev/null
+++ b/src/icons/thin/bookmark-add.svg
@@ -0,0 +1 @@
+
diff --git a/src/icons/thin/bookmark-remove.svg b/src/icons/thin/bookmark-remove.svg
new file mode 100644
index 00000000..f6d44c35
--- /dev/null
+++ b/src/icons/thin/bookmark-remove.svg
@@ -0,0 +1 @@
+