Implement a non-functional push rule control UI
This commit is contained in:
		@@ -4,15 +4,20 @@
 | 
			
		||||
- PCN error handling
 | 
			
		||||
- Change docs linking to dev branch back to master 
 | 
			
		||||
 | 
			
		||||
- fix DeviceSection padding
 | 
			
		||||
- Implement fallback QML notifications, usable if dbus isn't available
 | 
			
		||||
- annoying tooltips when menu open
 | 
			
		||||
- profiles missing in notifications
 | 
			
		||||
 | 
			
		||||
- add http_proxy support
 | 
			
		||||
- image viewer: can't expand image in reduced window layout
 | 
			
		||||
- Encrypted rooms don't show invites in member list after Mirage restart
 | 
			
		||||
- Room display name not updated when someone removes theirs
 | 
			
		||||
- Fix right margin of own `<image url>\n<image url>` messages
 | 
			
		||||
- option to use plaintext notifications
 | 
			
		||||
- warn on ambiguously activated shortcut
 | 
			
		||||
 | 
			
		||||
- SSO device delete?
 | 
			
		||||
- filter > enter > room list is always scrolled to top
 | 
			
		||||
- session list: prevent tab-focusing the delegates
 | 
			
		||||
- refresh server list button
 | 
			
		||||
 
 | 
			
		||||
@@ -66,6 +66,9 @@ class Backend:
 | 
			
		||||
 | 
			
		||||
            - `"accounts"`: logged-in accounts;
 | 
			
		||||
 | 
			
		||||
            - `("<user_id>", "pushrules")`: push rules configured for our
 | 
			
		||||
              account `user_id`.
 | 
			
		||||
 | 
			
		||||
            - `("<user_id>", "rooms")`: rooms our account `user_id` is part of;
 | 
			
		||||
 | 
			
		||||
            - `("<user_id>", "transfers")`: ongoing or failed file
 | 
			
		||||
 
 | 
			
		||||
@@ -85,7 +85,51 @@ class Account(ModelItem):
 | 
			
		||||
        return (self.order, self.id) < (other.order, other.id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PushRuleKind(AutoStrEnum):
 | 
			
		||||
    Override  = auto()
 | 
			
		||||
    Content   = auto()
 | 
			
		||||
    Room      = auto()
 | 
			
		||||
    Sender    = auto()
 | 
			
		||||
    Underride = auto()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass(eq=False)
 | 
			
		||||
class PushRule(ModelItem):
 | 
			
		||||
    """A push rule configured for one of our account."""
 | 
			
		||||
 | 
			
		||||
    id:           str          = field()
 | 
			
		||||
    kind:         PushRuleKind = field()
 | 
			
		||||
    order:        int          = field()
 | 
			
		||||
    default:      bool         = field()
 | 
			
		||||
    enabled:      bool         = True
 | 
			
		||||
    pattern:      str          = ""
 | 
			
		||||
    notify:       bool         = False
 | 
			
		||||
    highlight:    bool         = False
 | 
			
		||||
    bubble:       bool         = False
 | 
			
		||||
    sound:        bool         = False
 | 
			
		||||
    urgency_hint: bool         = False
 | 
			
		||||
 | 
			
		||||
    def __lt__(self, other: "PushRule") -> bool:
 | 
			
		||||
        """Sort by `kind`, then `order`."""
 | 
			
		||||
 | 
			
		||||
        return (
 | 
			
		||||
            self.kind is PushRuleKind.Underride,
 | 
			
		||||
            self.kind is PushRuleKind.Sender,
 | 
			
		||||
            self.kind is PushRuleKind.Room,
 | 
			
		||||
            self.kind is PushRuleKind.Content,
 | 
			
		||||
            self.kind is PushRuleKind.Override,
 | 
			
		||||
            self.order,
 | 
			
		||||
        ) < (
 | 
			
		||||
            other.kind is PushRuleKind.Underride,
 | 
			
		||||
            other.kind is PushRuleKind.Sender,
 | 
			
		||||
            other.kind is PushRuleKind.Room,
 | 
			
		||||
            other.kind is PushRuleKind.Content,
 | 
			
		||||
            other.kind is PushRuleKind.Override,
 | 
			
		||||
            other.order,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class Room(ModelItem):
 | 
			
		||||
    """A matrix room we are invited to, are or were member of."""
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ import nio
 | 
			
		||||
 | 
			
		||||
from .html_markdown import HTML_PROCESSOR
 | 
			
		||||
from .media_cache import Media
 | 
			
		||||
from .models.items import TypeSpecifier
 | 
			
		||||
from .models.items import PushRule, PushRuleKind, TypeSpecifier
 | 
			
		||||
from .presence import Presence
 | 
			
		||||
from .pyotherside_events import DevicesUpdated
 | 
			
		||||
from .utils import classes_defined_in, plain2html
 | 
			
		||||
@@ -53,22 +53,23 @@ class NioCallbacks:
 | 
			
		||||
            if method:
 | 
			
		||||
                self.client.add_response_callback(method, response_class)
 | 
			
		||||
 | 
			
		||||
        for name, event_class in classes_defined_in(nio.events).items():
 | 
			
		||||
        for name, ev_class in classes_defined_in(nio.events).items():
 | 
			
		||||
            method = getattr(self, f"on{name}", None)
 | 
			
		||||
 | 
			
		||||
            if not method:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if issubclass(event_class, nio.EphemeralEvent):
 | 
			
		||||
                self.client.add_ephemeral_callback(method, event_class)
 | 
			
		||||
            elif issubclass(event_class, nio.ToDeviceEvent):
 | 
			
		||||
                self.client.add_to_device_callback(method, event_class)
 | 
			
		||||
            elif issubclass(event_class, nio.AccountDataEvent):
 | 
			
		||||
                self.client.add_room_account_data_callback(method, event_class)
 | 
			
		||||
            elif issubclass(event_class, nio.PresenceEvent):
 | 
			
		||||
                self.client.add_presence_callback(method, event_class)
 | 
			
		||||
            if issubclass(ev_class, nio.EphemeralEvent):
 | 
			
		||||
                self.client.add_ephemeral_callback(method, ev_class)
 | 
			
		||||
            elif issubclass(ev_class, nio.ToDeviceEvent):
 | 
			
		||||
                self.client.add_to_device_callback(method, ev_class)
 | 
			
		||||
            elif issubclass(ev_class, nio.AccountDataEvent):
 | 
			
		||||
                self.client.add_global_account_data_callback(method, ev_class)
 | 
			
		||||
                self.client.add_room_account_data_callback(method, ev_class)
 | 
			
		||||
            elif issubclass(ev_class, nio.PresenceEvent):
 | 
			
		||||
                self.client.add_presence_callback(method, ev_class)
 | 
			
		||||
            else:
 | 
			
		||||
                self.client.add_event_callback(method, event_class)
 | 
			
		||||
                self.client.add_event_callback(method, ev_class)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
@@ -779,6 +780,54 @@ class NioCallbacks:
 | 
			
		||||
            ev.read_by_count = len(ev.last_read_by)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Account data callbacks
 | 
			
		||||
 | 
			
		||||
    async def onPushRulesEvent(self, ev: nio.PushRulesEvent) -> None:
 | 
			
		||||
        model = self.models[self.user_id, "pushrules"]
 | 
			
		||||
        model.clear()
 | 
			
		||||
 | 
			
		||||
        kinds: Dict[PushRuleKind, List[nio.PushRule]] = {
 | 
			
		||||
            PushRuleKind.Override:  ev.global_rules.override,
 | 
			
		||||
            PushRuleKind.Content:   ev.global_rules.content,
 | 
			
		||||
            PushRuleKind.Room:      ev.global_rules.room,
 | 
			
		||||
            PushRuleKind.Sender:    ev.global_rules.sender,
 | 
			
		||||
            PushRuleKind.Underride: ev.global_rules.underride,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for kind, rules in kinds.items():
 | 
			
		||||
            for order, rule in enumerate(rules):
 | 
			
		||||
                tweaks = {
 | 
			
		||||
                    action.tweak: action.value for action in rule.actions
 | 
			
		||||
                    if isinstance(action, nio.PushSetTweak)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                # Note: The `dont_notify` action does nothing.
 | 
			
		||||
                # As of now (sept 2020), `coalesce` is just a `notify` synonym.
 | 
			
		||||
                notify = any(
 | 
			
		||||
                    isinstance(action, (nio.PushNotify, nio.PushCoalesce))
 | 
			
		||||
                    for action in rule.actions
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                high   = tweaks.get("highlight", False) is not False
 | 
			
		||||
                bubble = tweaks.get("bubble", notify) is not False
 | 
			
		||||
                sound  = tweaks.get("sound", False) is not False
 | 
			
		||||
                hint   = tweaks.get("urgency_hint", high) is not False
 | 
			
		||||
 | 
			
		||||
                model[rule.id] = PushRule(
 | 
			
		||||
                    id           = rule.id,
 | 
			
		||||
                    kind         = kind,
 | 
			
		||||
                    order        = order,
 | 
			
		||||
                    default      = rule.default,
 | 
			
		||||
                    enabled      = rule.enabled,
 | 
			
		||||
                    pattern      = rule.pattern,
 | 
			
		||||
                    notify       = notify,
 | 
			
		||||
                    highlight    = high,
 | 
			
		||||
                    bubble       = bubble,
 | 
			
		||||
                    sound        = sound,
 | 
			
		||||
                    urgency_hint = hint,
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # Presence event callbacks
 | 
			
		||||
 | 
			
		||||
    async def onPresenceEvent(self, ev: nio.PresenceEvent) -> None:
 | 
			
		||||
 
 | 
			
		||||
@@ -18,11 +18,14 @@ HPage {
 | 
			
		||||
        height: Math.min(implicitHeight, page.availableHeight)
 | 
			
		||||
 | 
			
		||||
        header: HTabBar {
 | 
			
		||||
            currentIndex: 1  // XXX
 | 
			
		||||
            HTabButton { text: qsTr("Account") }
 | 
			
		||||
            HTabButton { text: qsTr("Notifications") }
 | 
			
		||||
            HTabButton { text: qsTr("Security") }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Account { userId: page.userId }
 | 
			
		||||
        Notifications { userId: page.userId }
 | 
			
		||||
        Security { userId: page.userId }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								src/gui/Pages/AccountSettings/NotificationRuleButton.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/gui/Pages/AccountSettings/NotificationRuleButton.qml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import QtQuick 2.12
 | 
			
		||||
import "../../Base"
 | 
			
		||||
 | 
			
		||||
HButton {
 | 
			
		||||
    property bool on: true
 | 
			
		||||
 | 
			
		||||
    opacity: on ? 1 : theme.disabledElementsOpacity
 | 
			
		||||
    hoverEnabled: true
 | 
			
		||||
    backgroundColor: "transparent"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										157
									
								
								src/gui/Pages/AccountSettings/NotificationRuleDelegate.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/gui/Pages/AccountSettings/NotificationRuleDelegate.qml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,157 @@
 | 
			
		||||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import QtQuick 2.12
 | 
			
		||||
import QtQuick.Controls 2.12
 | 
			
		||||
import QtQuick.Layouts 1.12
 | 
			
		||||
import "../.."
 | 
			
		||||
import "../../Base"
 | 
			
		||||
import "../../Base/Buttons"
 | 
			
		||||
import "../../Base/HTile"
 | 
			
		||||
import "../../MainPane"
 | 
			
		||||
import "../../PythonBridge"
 | 
			
		||||
import "../../ShortcutBundles"
 | 
			
		||||
 | 
			
		||||
HTile {
 | 
			
		||||
    id: root
 | 
			
		||||
 | 
			
		||||
    property string userId
 | 
			
		||||
 | 
			
		||||
    readonly property QtObject matchingRoom:
 | 
			
		||||
        model.kind === "Room" ?
 | 
			
		||||
        ModelStore.get(userId, "rooms").find(model.id) :
 | 
			
		||||
        null
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    contentOpacity: model.enabled ? 1 : theme.disabledElementsOpacity
 | 
			
		||||
    hoverEnabled: false
 | 
			
		||||
 | 
			
		||||
    contentItem: HColumnLayout {
 | 
			
		||||
        spacing: root.spacing / 2
 | 
			
		||||
 | 
			
		||||
        TitleLabel {
 | 
			
		||||
            opacity: model.enabled ? 1 : theme.disabledElementsOpacity
 | 
			
		||||
            elide: Text.ElideNone
 | 
			
		||||
            wrapMode: HLabel.Wrap
 | 
			
		||||
 | 
			
		||||
            textFormat:
 | 
			
		||||
                model.id === ".m.rule.contains_user_name" ||
 | 
			
		||||
                model.id === ".m.rule.roomnotif" ||
 | 
			
		||||
                model.kind === "Sender" ?
 | 
			
		||||
                HLabel.StyledText :
 | 
			
		||||
                HLabel.PlainText
 | 
			
		||||
 | 
			
		||||
            text:
 | 
			
		||||
                model.id === ".m.rule.master" ?
 | 
			
		||||
                qsTr("Any message") :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.suppress_notices" ?
 | 
			
		||||
                qsTr("Messages sent by bots") :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.invite_for_me" ?
 | 
			
		||||
                qsTr("Received room invites") :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.member_event" ?
 | 
			
		||||
                qsTr("Membership, name & avatar changes") :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.contains_display_name" ?
 | 
			
		||||
                qsTr("Messages containing my display name") :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.tombstone" ?
 | 
			
		||||
                qsTr("Room migration alerts") :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.reaction" ?
 | 
			
		||||
                qsTr("Emoji reactions") :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.roomnotif" ?
 | 
			
		||||
                qsTr("Messages containing %1").arg(
 | 
			
		||||
                    utils.htmlColorize("@room", theme.colors.accentText),
 | 
			
		||||
                ) :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.contains_user_name" ?
 | 
			
		||||
                qsTr("Contains %1").arg(utils.coloredNameHtml(
 | 
			
		||||
                    "", userId, userId.split(":")[0].substring(1),
 | 
			
		||||
                )):
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.call" ?
 | 
			
		||||
                qsTr("Incoming audio calls") :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.encrypted_room_one_to_one" ?
 | 
			
		||||
                qsTr("Encrypted 1-to-1 messages") :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.room_one_to_one" ?
 | 
			
		||||
                qsTr("Unencrypted 1-to-1 messages") :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.message" ?
 | 
			
		||||
                qsTr("Unencrypted group messages") :
 | 
			
		||||
 | 
			
		||||
                model.id === ".m.rule.encrypted" ?
 | 
			
		||||
                qsTr("Encrypted group messages") :
 | 
			
		||||
 | 
			
		||||
                model.kind === "Content" ?
 | 
			
		||||
                qsTr('Contains "%1"').arg(model.pattern) :
 | 
			
		||||
 | 
			
		||||
                model.kind === "Sender" ?
 | 
			
		||||
                utils.coloredNameHtml("", model.id) :
 | 
			
		||||
 | 
			
		||||
                matchingRoom && matchingRoom.display_name ?
 | 
			
		||||
                matchingRoom.display_name :
 | 
			
		||||
 | 
			
		||||
                model.id
 | 
			
		||||
 | 
			
		||||
            Layout.fillWidth: true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        HRowLayout {
 | 
			
		||||
            NotificationRuleButton {
 | 
			
		||||
                on: model.notify
 | 
			
		||||
 | 
			
		||||
                contentItem: MessageIndicator {
 | 
			
		||||
                    indicatorTheme:
 | 
			
		||||
                        theme.mainPane.listView.room.unreadIndicator
 | 
			
		||||
                    unreads: 1
 | 
			
		||||
                    text: "+1"
 | 
			
		||||
                    font.pixelSize: theme.fontSize.normal
 | 
			
		||||
                    topPadding: leftPadding / 3
 | 
			
		||||
                    bottomPadding: topPadding
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            NotificationRuleButton {
 | 
			
		||||
                on: model.highlight
 | 
			
		||||
 | 
			
		||||
                contentItem: MessageIndicator {
 | 
			
		||||
                    indicatorTheme:
 | 
			
		||||
                        theme.mainPane.listView.room.unreadIndicator
 | 
			
		||||
 | 
			
		||||
                    unreads: 1
 | 
			
		||||
                    highlights: 1
 | 
			
		||||
                    text: "+1"
 | 
			
		||||
                    font.pixelSize: theme.fontSize.normal
 | 
			
		||||
                    topPadding: leftPadding / 3
 | 
			
		||||
                    bottomPadding: topPadding
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            NotificationRuleButton {
 | 
			
		||||
                icon.name: "pushrule-action-bubble"
 | 
			
		||||
                on: model.bubble
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            NotificationRuleButton {
 | 
			
		||||
                icon.name: "pushrule-action-sound"
 | 
			
		||||
                on: model.sound
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            NotificationRuleButton {
 | 
			
		||||
                icon.name: "pushrule-action-urgency-hint"
 | 
			
		||||
                on: model.urgency_hint
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            HSpacer {}
 | 
			
		||||
 | 
			
		||||
            NotificationRuleButton {
 | 
			
		||||
                icon.name: "pushrule-edit"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								src/gui/Pages/AccountSettings/Notifications.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/gui/Pages/AccountSettings/Notifications.qml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
// SPDX-License-Identifier: LGPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
import QtQuick 2.12
 | 
			
		||||
import QtQuick.Controls 2.12
 | 
			
		||||
import QtQuick.Layouts 1.12
 | 
			
		||||
import "../.."
 | 
			
		||||
import "../../Base"
 | 
			
		||||
import "../../Base/Buttons"
 | 
			
		||||
import "../../PythonBridge"
 | 
			
		||||
import "../../ShortcutBundles"
 | 
			
		||||
 | 
			
		||||
HListView {
 | 
			
		||||
    id: root
 | 
			
		||||
 | 
			
		||||
    property string userId
 | 
			
		||||
 | 
			
		||||
    property bool enableFlickShortcuts:
 | 
			
		||||
        SwipeView ? SwipeView.isCurrentItem : true
 | 
			
		||||
 | 
			
		||||
    function takeFocus() {
 | 
			
		||||
        // deviceList.headerItem.exportButton.forceActiveFocus()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    clip: true
 | 
			
		||||
    model: ModelStore.get(userId, "pushrules")
 | 
			
		||||
    bottomMargin: theme.spacing
 | 
			
		||||
    implicitHeight: Math.min(window.height, contentHeight + bottomMargin)
 | 
			
		||||
 | 
			
		||||
    section.property: "kind"
 | 
			
		||||
    section.delegate: HLabel {
 | 
			
		||||
        width: root.width
 | 
			
		||||
        topPadding: padding * (section === "Override" ? 1 : 1.5)
 | 
			
		||||
        padding: theme.spacing
 | 
			
		||||
        font.pixelSize: theme.fontSize.big
 | 
			
		||||
        text:
 | 
			
		||||
            section === "Override" ? qsTr("High-priority general rules") :
 | 
			
		||||
            section === "Content" ? qsTr("Message text rules") :
 | 
			
		||||
            section === "Room" ? qsTr("Room rules") :
 | 
			
		||||
            section === "Sender" ? qsTr("Sender rules") :
 | 
			
		||||
            qsTr("General rules")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    delegate: NotificationRuleDelegate {
 | 
			
		||||
        userId: root.userId
 | 
			
		||||
        width: root.width
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Layout.fillWidth: true
 | 
			
		||||
    Layout.fillHeight: true
 | 
			
		||||
 | 
			
		||||
    FlickShortcuts {
 | 
			
		||||
        flickable: root
 | 
			
		||||
        active: ! mainUI.debugConsole.visible && root.enableFlickShortcuts
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								src/icons/thin/pushrule-action-bubble.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/icons/thin/pushrule-action-bubble.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
    <path d="m22 3v13h-6.961l-3.039 3.798-3.039-3.798h-6.961v-13zm2-2h-24v17h8l4 5 4-5h8z"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 184 B  | 
							
								
								
									
										11
									
								
								src/icons/thin/pushrule-action-sound.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/icons/thin/pushrule-action-sound.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
    <path d="m14.509208 8.343398c1.202865.9346638 1.945334 2.226588 1.94209 3.651856-.0032 1.425268-.748954 2.714677-1.953439 3.648082l.978322 1.538485c1.713516-1.324631 2.775344-3.158736 2.780206-5.184051.0032-2.0265727-1.05048-3.8594193-2.759132-5.1890826z" stroke-width="1.428038"/>
 | 
			
		||||
    <path d="m18.318701 4.9920248c2.307024 1.7957761 3.72921 4.2712232 3.723838 7.0065862-.0054 2.735361-1.440099 5.212198-3.754288 7.003804l.980846 1.74296c2.887361-2.234991 4.68032-5.328952 4.685693-8.743986.009-3.4150319-1.766089-6.5062134-4.648077-8.7467639z" stroke-width="1.57784"/>
 | 
			
		||||
    <g transform="matrix(.6875 0 0 1.01875 0 -.225)">
 | 
			
		||||
        <path d="m11 1.8125-9.1191406 5.09375v10.1875l9.1191406 5.09375zm-1.6875 2.9941406v14.3867184l-7.4324287-4.041727v-6.1767663z" transform="matrix(1.4545455 0 0 .98159509 0 .220859)"/>
 | 
			
		||||
        <path d="m2.7346492 8.9958856h-2.7346492v6.1896554h2.7346492z" stroke-width=".786744"/>
 | 
			
		||||
        <path d="m7 7c-4.7095572 2.7190641-5.0350591 2.9069927 0 0z" fill="none"/>
 | 
			
		||||
        <path d="m2.7357957 7-2.7357957 1.0208759v.9750097h2.7346492z"/>
 | 
			
		||||
        <path d="m2.7357956 17-2.73579573-1.020876v-.97501h2.73464923z"/>
 | 
			
		||||
    </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.2 KiB  | 
							
								
								
									
										3
									
								
								src/icons/thin/pushrule-action-urgency-hint.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/icons/thin/pushrule-action-urgency-hint.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
    <path d="m22.909091 9.2836364v1.4967276h-2.768727c.02182-.243273.04145-.488728.04145-.742909 0-.2585459-.01854-.5061824-.03818-.7527277h2.765455zm-11.616-7.1487273v-2.1349091h1.495636v2.1425455c-.264-.024-.528-.038183-.792-.038183-.234545 0-.469091.010909-.703636.030547zm6.022909 1.6843636 1.712727-1.9014545 1.111637 1.0014544-1.748728 1.9385456c-.272727-.324-.646909-.733091-1.075636-1.0385455zm-11.7141819 1.0385455-1.7487273-1.9385456 1.1116364-1.0014544 1.7127273 1.9014545c-.4276365.3054545-.8029091.7145455-1.0756364 1.0385455zm-1.7421818 5.9225458h-2.7687273v-1.4967276h2.7654545c-.019636.2465453-.038182.4952727-.038182.7538186 0 .254181.019636.499636.041455.742909zm10.3221817 9.946909h-4.363636c-.301091 0-.545455.244363-.545455.545454s.244364.545455.545455.545455h4.363636c.301091 0 .545455-.244364.545455-.545455s-.244364-.545454-.545455-.545454zm0 2.181818h-4.363636c-.301091 0-.545455.244364-.545455.545454 0 .301091.244364.545455.545455.545455h4.363636c.301091 0 .545455-.244364.545455-.545455 0-.30109-.244364-.545454-.545455-.545454zm4.363636-12.871636c0 3.893453-3.506181 6.526909-3.506181 9.598909h-2.169819c-.0033-2.026909.949091-3.697091 1.877455-5.309455.830182-1.445454 1.616727-2.811273 1.616727-4.289454 0-2.8276368-2.263636-4.1149096-4.366909-4.1149096-2.1 0-4.3603635 1.2872728-4.3603635 4.1149096 0 1.478181.7865455 2.844 1.6167275 4.289454.928363 1.612364 1.881818 3.282546 1.876363 5.309455h-2.1687268c0-3.072-3.5061818-5.705456-3.5061818-9.598909 0-4.0614551 3.2705454-6.2967278 6.5421816-6.2967278 3.273818 0 6.548727 2.2374545 6.548727 6.2967278z" stroke-width="1.09091"/>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 1.7 KiB  | 
							
								
								
									
										7
									
								
								src/icons/thin/pushrule-edit.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/icons/thin/pushrule-edit.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
    <g stroke-width=".853763" transform="translate(9.811836)">
 | 
			
		||||
        <path d="m12 0c1.208596 0 2.188164.97956763 2.188164 2.1881632 0 1.2085956-.979568 2.1881633-2.188164 2.1881633s-2.1881636-.9795677-2.1881636-2.1881633c0-1.20859557.9795676-2.1881632 2.1881636-2.1881632z"/>
 | 
			
		||||
        <path d="m12 9.8118368c1.208596 0 2.188164.9795672 2.188164 2.1881632s-.979568 2.188163-2.188164 2.188163-2.1881636-.979567-2.1881636-2.188163.9795676-2.1881632 2.1881636-2.1881632z"/>
 | 
			
		||||
        <path d="m12 19.623674c1.208596 0 2.188164.979567 2.188164 2.188163s-.979568 2.188163-2.188164 2.188163-2.1881636-.979567-2.1881636-2.188163.9795676-2.188163 2.1881636-2.188163z"/>
 | 
			
		||||
    </g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 760 B  | 
		Reference in New Issue
	
	Block a user