From 0017a1562c5daf8704c0a6e789c03a84e29ee998 Mon Sep 17 00:00:00 2001 From: miruka Date: Sun, 28 Feb 2021 09:52:51 -0400 Subject: [PATCH] Room context menu: notification setting checkmark Indicate which notification override setting is currently active --- src/backend/matrix_client.py | 55 ++++++++++++++++++++++++------- src/backend/models/items.py | 19 +++++++++++ src/backend/nio_callbacks.py | 21 ++++++++++-- src/gui/MainPane/RoomDelegate.qml | 4 +++ 4 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index 61ef6fd7..e357fdbb 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -41,8 +41,8 @@ from .errors import ( from .html_markdown import HTML_PROCESSOR as HTML from .media_cache import Media, Thumbnail from .models.items import ( - ZERO_DATE, Account, Event, Member, PushRule, Room, Transfer, - TransferStatus, TypeSpecifier, + ZERO_DATE, Account, Event, Member, PushRule, Room, + RoomNotificationOverride, Transfer, TransferStatus, TypeSpecifier, ) from .models.model_store import ModelStore from .nio_callbacks import NioCallbacks @@ -1919,13 +1919,25 @@ class MatrixClient(nio.AsyncClient): await self.delete_pushrule("global", kind, rule_id) + def _rule_overrides_room(self, rule: PushRule) -> Optional[str]: + override = rule.kind is nio.PushRuleKind.override + one_cnd = len(rule.conditions) == 1 + + if not one_cnd: + return None + + cnd = nio.PushCondition.from_dict(rule.conditions[0]) + ev_match = isinstance(cnd, nio.PushEventMatch) + + if override and ev_match and cnd.key == "room_id": + return cnd.pattern + + return None + + async def _remove_room_override_rule(self, room_id: str) -> None: for rule in self.models[self.user_id, "pushrules"].values(): - override_kind = rule.kind is nio.PushRuleKind.override - this_room_cnd = nio.PushEventMatch("room_id", room_id).as_value - - if (override_kind and rule.conditions == [this_room_cnd]): - print(rule) + if self._rule_overrides_room(rule) == room_id: await self.remove_pushrule(rule.kind, rule.rule_id) @@ -2015,7 +2027,6 @@ class MatrixClient(nio.AsyncClient): ) -> None: """Register/update a `nio.MatrixRoom` as a `models.items.Room`.""" - # Add room inviter = getattr(room, "inviter", "") or "" levels = room.power_levels can_send_state = partial(levels.can_user_send_state, self.user_id) @@ -2044,7 +2055,28 @@ class MatrixClient(nio.AsyncClient): ) unverified_devices = registered.unverified_devices + notification_setting = RoomNotificationOverride.UseDefaultSettings + + for rule in self.models[self.user_id, "pushrules"].values(): + overrides = self._rule_overrides_room(rule) == room.room_id + is_room_kind = rule.kind is nio.PushRuleKind.room + room_kind_match = is_room_kind and rule.rule_id == room.room_id + + if overrides and not rule.actions: + notification_setting = RoomNotificationOverride.IgnoreEvents + break + elif overrides: + notification_setting = RoomNotificationOverride.AllEvents + break + elif room_kind_match and not rule.actions: + notification_setting = RoomNotificationOverride.HighlightsOnly + break + elif room_kind_match: + notification_setting = RoomNotificationOverride.AllEvents + break + pinned = self.backend.settings.RoomList.Pinned + room_item = Room( id = room.room_id, for_account = self.user_id, @@ -2085,9 +2117,10 @@ class MatrixClient(nio.AsyncClient): last_event_date = last_event_date, - unreads = room.unread_notifications, - highlights = room.unread_highlights, - local_unreads = local_unreads, + unreads = room.unread_notifications, + highlights = room.unread_highlights, + local_unreads = local_unreads, + notification_setting = notification_setting, lexical_sorting = self.backend.settings.RoomList.lexical_sort, pinned = room.room_id in pinned.get(self.user_id, []), diff --git a/src/backend/models/items.py b/src/backend/models/items.py index 450914e2..5b379465 100644 --- a/src/backend/models/items.py +++ b/src/backend/models/items.py @@ -38,6 +38,16 @@ class PingStatus(AutoStrEnum): Failed = auto() +class RoomNotificationOverride(AutoStrEnum): + """Possible per-room notification override settings, as displayed in the + left sidepane's context menu when right-clicking a room. + """ + UseDefaultSettings = auto() + AllEvents = auto() + HighlightsOnly = auto() + IgnoreEvents = auto() + + @dataclass(eq=False) class Homeserver(ModelItem): """A homeserver we can connect to. The `id` field is the server's URL.""" @@ -170,6 +180,9 @@ class Room(ModelItem): highlights: int = 0 local_unreads: bool = False + notification_setting: RoomNotificationOverride = \ + RoomNotificationOverride.UseDefaultSettings + lexical_sorting: bool = False pinned: bool = False @@ -229,6 +242,12 @@ class Room(ModelItem): @dataclass(eq=False) class AccountOrRoom(Account, Room): + """The left sidepane in the GUI lists a mixture of accounts and rooms + giving a tree view illusion. Since all items in a QML ListView must have + the same available properties, this class inherits both + `Account` and `Room` to fulfill that purpose. + """ + type: Union[Type[Account], Type[Room]] = Account account_order: int = -1 diff --git a/src/backend/nio_callbacks.py b/src/backend/nio_callbacks.py index 89432581..ba01f994 100644 --- a/src/backend/nio_callbacks.py +++ b/src/backend/nio_callbacks.py @@ -4,7 +4,7 @@ import asyncio import json import logging as log -from dataclasses import asdict, dataclass, field +from dataclasses import dataclass, field from datetime import datetime, timedelta from html import escape from pathlib import Path @@ -783,6 +783,18 @@ class NioCallbacks: # Account data callbacks async def onPushRulesEvent(self, ev: nio.PushRulesEvent) -> None: + async def update_affected_room(rule: PushRule) -> None: + affects_room: Optional[str] + + if rule.kind == nio.PushRuleKind.room: + affects_room = rule.rule_id + else: + affects_room = self.client._rule_overrides_room(rule) + + if affects_room in self.client.rooms: + nio_room = self.client.rooms[affects_room] + await self.client.register_nio_room(nio_room) + model = self.models[self.user_id, "pushrules"] kinds: Dict[nio.PushRuleKind, List[nio.PushRule]] = { @@ -800,9 +812,10 @@ class NioCallbacks: new_keys.add((kind.value, rule.id)) with model.batch_remove(): - for key in tuple(model): + for key, rule in list(model.items()): if key not in new_keys: del model[key] + await update_affected_room(rule) # Then, add new rules/modify changed existing ones @@ -825,7 +838,7 @@ class NioCallbacks: sound = str(tweaks.get("sound") or "") hint = tweaks.get("urgency_hint", bool(sound)) is not False - model[kind.value, rule.id] = PushRule( + rule_item = PushRule( id = (kind.value, rule.id), kind = kind, rule_id = rule.id, @@ -841,6 +854,8 @@ class NioCallbacks: sound = sound, urgency_hint = hint, ) + model[kind.value, rule.id] = rule_item + await update_affected_room(rule_item) self.client.push_rules = ev diff --git a/src/gui/MainPane/RoomDelegate.qml b/src/gui/MainPane/RoomDelegate.qml index d588ce8e..9948ca07 100644 --- a/src/gui/MainPane/RoomDelegate.qml +++ b/src/gui/MainPane/RoomDelegate.qml @@ -152,6 +152,7 @@ HTile { HMenuItem { text: qsTr("Use default account settings") + checked: model.notification_setting === "UseDefaultSettings" onTriggered: py.callClientCoro( model.for_account, "room_pushrule_use_default", [model.id], ) @@ -159,6 +160,7 @@ HTile { HMenuItem { text: qsTr("All new messages") + checked: model.notification_setting === "AllEvents" onTriggered: py.callClientCoro( model.for_account, "room_pushrule_all_events", [model.id], ) @@ -166,6 +168,7 @@ HTile { HMenuItem { text: qsTr("Highlights only (replies, keywords...)") + checked: model.notification_setting === "HighlightsOnly" onTriggered: py.callClientCoro( model.for_account, "room_pushrule_highlights_only", @@ -175,6 +178,7 @@ HTile { HMenuItem { text: qsTr("Ignore new messages") + checked: model.notification_setting === "IgnoreEvents" onTriggered: py.callClientCoro( model.for_account, "room_pushrule_ignore_all", [model.id], )