Add popup to edit push rules
This commit is contained in:
parent
71cd509a9d
commit
765ce46aeb
16
docs/TODO.md
16
docs/TODO.md
|
@ -1,5 +1,21 @@
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
- push popup cancel & remove
|
||||||
|
- right click on rule
|
||||||
|
- combo box custom item
|
||||||
|
- explain pattern
|
||||||
|
- fix spinbox buttons
|
||||||
|
- way to add new rule
|
||||||
|
- quick room & sender rule changes
|
||||||
|
- config & keybind for global rule disabling
|
||||||
|
- quick settings
|
||||||
|
- import/export/json edit rules?
|
||||||
|
- add missing license headers to qml files
|
||||||
|
- fix flickable popups can't be flicked by keyboard
|
||||||
|
- room selector for room rules
|
||||||
|
- validate json for unknown action/condition
|
||||||
|
- seen tooltips can't be shown on image hover
|
||||||
|
|
||||||
- PCN docstrings
|
- PCN docstrings
|
||||||
- PCN error handling
|
- PCN error handling
|
||||||
- Change docs linking to dev branch back to master
|
- Change docs linking to dev branch back to master
|
||||||
|
|
|
@ -41,8 +41,8 @@ from .errors import (
|
||||||
from .html_markdown import HTML_PROCESSOR as HTML
|
from .html_markdown import HTML_PROCESSOR as HTML
|
||||||
from .media_cache import Media, Thumbnail
|
from .media_cache import Media, Thumbnail
|
||||||
from .models.items import (
|
from .models.items import (
|
||||||
ZERO_DATE, Account, Event, Member, PushRule, Room,
|
ZERO_DATE, Account, Event, Member, PushRule, Room, Transfer,
|
||||||
Transfer, TransferStatus, TypeSpecifier,
|
TransferStatus, TypeSpecifier,
|
||||||
)
|
)
|
||||||
from .models.model_store import ModelStore
|
from .models.model_store import ModelStore
|
||||||
from .nio_callbacks import NioCallbacks
|
from .nio_callbacks import NioCallbacks
|
||||||
|
@ -54,6 +54,8 @@ from .pyotherside_events import (
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .backend import Backend
|
from .backend import Backend
|
||||||
|
|
||||||
|
PushAction = Union[Dict[str, Any], nio.PushAction]
|
||||||
|
PushCondition = Union[Dict[str, Any], nio.PushCondition]
|
||||||
CryptDict = Dict[str, Any]
|
CryptDict = Dict[str, Any]
|
||||||
PathCallable = Union[
|
PathCallable = Union[
|
||||||
str, Path, Callable[[], Coroutine[None, None, Union[str, Path]]],
|
str, Path, Callable[[], Coroutine[None, None, Union[str, Path]]],
|
||||||
|
@ -1780,6 +1782,79 @@ class MatrixClient(nio.AsyncClient):
|
||||||
raise MatrixUnauthorized()
|
raise MatrixUnauthorized()
|
||||||
|
|
||||||
|
|
||||||
|
async def edit_pushrule(
|
||||||
|
self,
|
||||||
|
kind: Union[nio.PushRuleKind, str],
|
||||||
|
rule_id: str,
|
||||||
|
old_kind: Union[None, nio.PushRuleKind, str] = None,
|
||||||
|
old_rule_id: Optional[str] = None,
|
||||||
|
move_before_rule_id: Optional[str] = None,
|
||||||
|
move_after_rule_id: Optional[str] = None,
|
||||||
|
enable: Optional[bool] = None,
|
||||||
|
conditions: Optional[List[PushCondition]] = None,
|
||||||
|
pattern: Optional[str] = None,
|
||||||
|
actions: Optional[List[PushAction]] = None,
|
||||||
|
) -> None:
|
||||||
|
"""Create or edit an existing non-builtin pushrule."""
|
||||||
|
|
||||||
|
# Convert arguments that were passed as basic types (usually from QML)
|
||||||
|
|
||||||
|
if isinstance(old_kind, str):
|
||||||
|
old_kind = nio.PushRuleKind[old_kind]
|
||||||
|
|
||||||
|
kind = nio.PushRuleKind[kind] if isinstance(kind, str) else kind
|
||||||
|
|
||||||
|
conditions = [
|
||||||
|
nio.PushCondition.from_dict(c) if isinstance(c, dict) else c
|
||||||
|
for c in conditions
|
||||||
|
] if isinstance(conditions, list) else None
|
||||||
|
|
||||||
|
actions = [
|
||||||
|
nio.PushAction.from_dict(a) if isinstance(a, dict) else a
|
||||||
|
for a in actions
|
||||||
|
] if isinstance(actions, list) else None
|
||||||
|
|
||||||
|
# Now edit the rule
|
||||||
|
|
||||||
|
old: Optional[PushRule] = None
|
||||||
|
key = (old_kind.value, old_rule_id)
|
||||||
|
|
||||||
|
if None not in key:
|
||||||
|
old = self.models[self.user_id, "pushrules"].get(key)
|
||||||
|
|
||||||
|
kind_change = old_kind is not None and old_kind != kind
|
||||||
|
rule_id_change = old_rule_id is not None and old_rule_id != rule_id
|
||||||
|
explicit_move = move_before_rule_id or move_after_rule_id
|
||||||
|
|
||||||
|
if old and not kind_change and not explicit_move:
|
||||||
|
# If user edits a rule without specifying a new position,
|
||||||
|
# the server would move it to the first position
|
||||||
|
move_after_rule_id = old.rule_id
|
||||||
|
|
||||||
|
if old and actions is None:
|
||||||
|
# Matrix API forces us to always pass a non-null actions paramater
|
||||||
|
actions = [nio.PushAction.from_dict(a) for a in old.actions]
|
||||||
|
|
||||||
|
await self.set_pushrule(
|
||||||
|
scope = "global",
|
||||||
|
kind = kind,
|
||||||
|
rule_id = rule_id,
|
||||||
|
before = move_before_rule_id,
|
||||||
|
after = move_after_rule_id,
|
||||||
|
actions = actions,
|
||||||
|
conditions = conditions,
|
||||||
|
pattern = pattern,
|
||||||
|
)
|
||||||
|
|
||||||
|
# If we're editing an existing rule but its kind or ID is changed,
|
||||||
|
# set_pushrule creates a new rule, thus we must delete the old one
|
||||||
|
if kind_change or rule_id_change:
|
||||||
|
await self.delete_pushrule("global", old_kind, old_rule_id)
|
||||||
|
|
||||||
|
if enable is not None and (old.enabled if old else True) != enable:
|
||||||
|
await self.enable_pushrule("global", kind, rule_id, enable)
|
||||||
|
|
||||||
|
|
||||||
async def tweak_pushrule(
|
async def tweak_pushrule(
|
||||||
self,
|
self,
|
||||||
kind: Union[nio.PushRuleKind, str],
|
kind: Union[nio.PushRuleKind, str],
|
||||||
|
@ -1790,6 +1865,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
sound: Optional[str] = None,
|
sound: Optional[str] = None,
|
||||||
urgency_hint: Optional[bool] = None,
|
urgency_hint: Optional[bool] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
"""Set an existing pushrule's actions. Works for builtin rules."""
|
||||||
|
|
||||||
kind = nio.PushRuleKind[kind] if isinstance(kind, str) else kind
|
kind = nio.PushRuleKind[kind] if isinstance(kind, str) else kind
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,9 @@ class PushRule(ModelItem):
|
||||||
order: int = field()
|
order: int = field()
|
||||||
default: bool = field()
|
default: bool = field()
|
||||||
enabled: bool = True
|
enabled: bool = True
|
||||||
|
conditions: List[Dict[str, Any]] = field(default_factory=list)
|
||||||
pattern: str = ""
|
pattern: str = ""
|
||||||
|
actions: List[Dict[str, Any]] = field(default_factory=list)
|
||||||
notify: bool = False
|
notify: bool = False
|
||||||
highlight: bool = False
|
highlight: bool = False
|
||||||
bubble: bool = False
|
bubble: bool = False
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import logging as log
|
import logging as log
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import asdict, dataclass, field
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from html import escape
|
from html import escape
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -786,7 +786,8 @@ class NioCallbacks:
|
||||||
model = self.models[self.user_id, "pushrules"]
|
model = self.models[self.user_id, "pushrules"]
|
||||||
|
|
||||||
kinds: Dict[nio.PushRuleKind, List[nio.PushRule]] = {
|
kinds: Dict[nio.PushRuleKind, List[nio.PushRule]] = {
|
||||||
kind: getattr(ev.global_rules, kind.value) for kind in nio.PushRuleKind
|
kind: getattr(ev.global_rules, kind.value)
|
||||||
|
for kind in nio.PushRuleKind
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove from model rules that are now deleted.
|
# Remove from model rules that are now deleted.
|
||||||
|
@ -831,7 +832,9 @@ class NioCallbacks:
|
||||||
order = order,
|
order = order,
|
||||||
default = rule.default,
|
default = rule.default,
|
||||||
enabled = rule.enabled,
|
enabled = rule.enabled,
|
||||||
|
conditions = [c.as_value for c in rule.conditions],
|
||||||
pattern = rule.pattern,
|
pattern = rule.pattern,
|
||||||
|
actions = [a.as_value for a in rule.actions],
|
||||||
notify = notify,
|
notify = notify,
|
||||||
highlight = high,
|
highlight = high,
|
||||||
bubble = bubble,
|
bubble = bubble,
|
||||||
|
|
71
src/gui/Base/HSpinBox.qml
Normal file
71
src/gui/Base/HSpinBox.qml
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
|
||||||
|
SpinBox {
|
||||||
|
id: box
|
||||||
|
|
||||||
|
property var defaultValue: null
|
||||||
|
readonly property bool changed: value !== (defaultValue || 0)
|
||||||
|
|
||||||
|
function reset() { value = Qt.binding(() => defaultValue || 0) }
|
||||||
|
|
||||||
|
|
||||||
|
// XXX TODO: default binding break
|
||||||
|
value: defaultValue || 0
|
||||||
|
implicitHeight: theme.baseElementsHeight
|
||||||
|
padding: 0
|
||||||
|
editable: true
|
||||||
|
|
||||||
|
background: null
|
||||||
|
|
||||||
|
contentItem: HTextField {
|
||||||
|
id: textField
|
||||||
|
height: parent.height
|
||||||
|
implicitWidth: 90 * theme.uiScale
|
||||||
|
radius: 0
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
verticalAlignment: Qt.AlignVCenter
|
||||||
|
// FIXME
|
||||||
|
text: box.textFromValue(box.value, box.locale)
|
||||||
|
|
||||||
|
readOnly: ! box.editable
|
||||||
|
validator: box.validator
|
||||||
|
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
||||||
|
|
||||||
|
onTextChanged: if (text && text !== "-") box.value = text
|
||||||
|
}
|
||||||
|
|
||||||
|
down.indicator: HButton {
|
||||||
|
x: box.mirrored ? parent.width - width : 0
|
||||||
|
height: parent.height
|
||||||
|
font.pixelSize: theme.fontSize.biggest
|
||||||
|
text: qsTr("-")
|
||||||
|
autoRepeat: true
|
||||||
|
autoRepeatInterval: 50
|
||||||
|
|
||||||
|
onPressed: box.decrease()
|
||||||
|
}
|
||||||
|
|
||||||
|
up.indicator: HButton {
|
||||||
|
x: box.mirrored ? 0 : parent.width - width
|
||||||
|
height: parent.height
|
||||||
|
font.pixelSize: theme.fontSize.biggest
|
||||||
|
text: qsTr("+")
|
||||||
|
autoRepeat: true
|
||||||
|
autoRepeatInterval: 50
|
||||||
|
|
||||||
|
onPressed: box.increase()
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
cursorShape: textField.hovered ? Qt.IBeamCursor : Qt.ArrowCursor
|
||||||
|
onWheel: wheel => {
|
||||||
|
wheel.angleDelta.y < 0 ? box.decrease() : box.increase()
|
||||||
|
wheel.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
import QtQuick 2.12
|
import QtQuick 2.12
|
||||||
|
@ -5,19 +6,15 @@ import QtQuick.Layouts 1.12
|
||||||
import "../.."
|
import "../.."
|
||||||
import "../../Base"
|
import "../../Base"
|
||||||
import "../../Base/HTile"
|
import "../../Base/HTile"
|
||||||
|
import "../../Base/Buttons"
|
||||||
import "../../MainPane"
|
import "../../MainPane"
|
||||||
|
import "../../Popups"
|
||||||
|
|
||||||
HTile {
|
HTile {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property Item page
|
property Item page
|
||||||
|
|
||||||
readonly property QtObject matchingRoom:
|
|
||||||
model.kind === "room" ?
|
|
||||||
ModelStore.get(page.userId, "rooms").find(model.rule_id) :
|
|
||||||
null
|
|
||||||
|
|
||||||
|
|
||||||
contentOpacity: model.enabled ? 1 : theme.disabledElementsOpacity
|
contentOpacity: model.enabled ? 1 : theme.disabledElementsOpacity
|
||||||
hoverEnabled: false
|
hoverEnabled: false
|
||||||
leftPadding: theme.spacing / 4
|
leftPadding: theme.spacing / 4
|
||||||
|
@ -30,71 +27,8 @@ HTile {
|
||||||
opacity: model.enabled ? 1 : theme.disabledElementsOpacity
|
opacity: model.enabled ? 1 : theme.disabledElementsOpacity
|
||||||
elide: Text.ElideNone
|
elide: Text.ElideNone
|
||||||
wrapMode: HLabel.Wrap
|
wrapMode: HLabel.Wrap
|
||||||
|
textFormat: HLabel.StyledText
|
||||||
textFormat:
|
text: utils.formatPushRuleName(page.userId, model)
|
||||||
model.rule_id === ".m.rule.contains_user_name" ||
|
|
||||||
model.rule_id === ".m.rule.roomnotif" ||
|
|
||||||
model.kind === "sender" ?
|
|
||||||
HLabel.StyledText :
|
|
||||||
HLabel.PlainText
|
|
||||||
|
|
||||||
text:
|
|
||||||
model.rule_id === ".m.rule.master" ?
|
|
||||||
qsTr("Any message") :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.suppress_notices" ?
|
|
||||||
qsTr("Messages sent by bots") :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.invite_for_me" ?
|
|
||||||
qsTr("Received room invites") :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.member_event" ?
|
|
||||||
qsTr("Membership, name & avatar changes") :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.contains_display_name" ?
|
|
||||||
qsTr("Messages containing my display name") :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.tombstone" ?
|
|
||||||
qsTr("Room migration alerts") :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.reaction" ?
|
|
||||||
qsTr("Emoji reactions") :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.roomnotif" ?
|
|
||||||
qsTr("Messages containing %1").arg(
|
|
||||||
utils.htmlColorize("@room", theme.colors.accentText),
|
|
||||||
) :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.contains_user_name" ?
|
|
||||||
qsTr("Contains %1").arg(utils.coloredNameHtml(
|
|
||||||
"", page.userId, page.userId.split(":")[0].substring(1),
|
|
||||||
)):
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.call" ?
|
|
||||||
qsTr("Incoming audio calls") :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.encrypted_room_one_to_one" ?
|
|
||||||
qsTr("Encrypted 1-to-1 messages") :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.room_one_to_one" ?
|
|
||||||
qsTr("Unencrypted 1-to-1 messages") :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.message" ?
|
|
||||||
qsTr("Unencrypted group messages") :
|
|
||||||
|
|
||||||
model.rule_id === ".m.rule.encrypted" ?
|
|
||||||
qsTr("Encrypted group messages") :
|
|
||||||
|
|
||||||
model.kind === "content" ?
|
|
||||||
qsTr('Contains "%1"').arg(model.pattern) :
|
|
||||||
|
|
||||||
model.kind === "sender" ?
|
|
||||||
utils.coloredNameHtml("", model.rule_id) :
|
|
||||||
|
|
||||||
matchingRoom && matchingRoom.display_name ?
|
|
||||||
matchingRoom.display_name :
|
|
||||||
|
|
||||||
model.rule_id
|
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.leftMargin: theme.spacing
|
Layout.leftMargin: theme.spacing
|
||||||
|
@ -161,6 +95,10 @@ HTile {
|
||||||
|
|
||||||
NotificationRuleButton {
|
NotificationRuleButton {
|
||||||
icon.name: "pushrule-edit"
|
icon.name: "pushrule-edit"
|
||||||
|
onClicked: window.makePopup(
|
||||||
|
"Popups/PushRuleSettingsPopup/PushRuleSettingsPopup.qml",
|
||||||
|
{userId: page.userId, rule: model},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,11 +73,11 @@ HListView {
|
||||||
padding: theme.spacing
|
padding: theme.spacing
|
||||||
font.pixelSize: theme.fontSize.big
|
font.pixelSize: theme.fontSize.big
|
||||||
text:
|
text:
|
||||||
section === "override" ? qsTr("High-priority general rules") :
|
section === "override" ? qsTr("High priority general rules") :
|
||||||
section === "content" ? qsTr("Message text rules") :
|
section === "content" ? qsTr("Message content rules") :
|
||||||
section === "room" ? qsTr("Room rules") :
|
section === "room" ? qsTr("Room rules") :
|
||||||
section === "sender" ? qsTr("Sender rules") :
|
section === "sender" ? qsTr("Sender rules") :
|
||||||
qsTr("General rules")
|
qsTr("Low priority general rules")
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: NotificationRuleDelegate {
|
delegate: NotificationRuleDelegate {
|
||||||
|
|
20
src/gui/Popups/PushRuleSettingsPopup/ContentRule.qml
Normal file
20
src/gui/Popups/PushRuleSettingsPopup/ContentRule.qml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import "../../Base"
|
||||||
|
import "../../Base/Buttons"
|
||||||
|
|
||||||
|
HColumnLayout {
|
||||||
|
readonly property alias idField: idField
|
||||||
|
|
||||||
|
HLabeledItem {
|
||||||
|
// TODO: globbing explanation & do space works?
|
||||||
|
label.text: qsTr("Word:")
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
HTextField {
|
||||||
|
id: idField
|
||||||
|
width: parent.width
|
||||||
|
defaultText: rule.kind === "content" ? rule.pattern : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
src/gui/Popups/PushRuleSettingsPopup/CustomLabel.qml
Normal file
14
src/gui/Popups/PushRuleSettingsPopup/CustomLabel.qml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import "../../Base"
|
||||||
|
|
||||||
|
HLabel {
|
||||||
|
opacity: enabled ? 1 : theme.disabledElementsOpacity
|
||||||
|
wrapMode: HLabel.Wrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Behavior on opacity { HNumberAnimation {} }
|
||||||
|
}
|
119
src/gui/Popups/PushRuleSettingsPopup/GeneralRule.qml
Normal file
119
src/gui/Popups/PushRuleSettingsPopup/GeneralRule.qml
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import "../../Base"
|
||||||
|
import "../../Base/Buttons"
|
||||||
|
|
||||||
|
HColumnLayout {
|
||||||
|
readonly property alias idField: idField
|
||||||
|
|
||||||
|
readonly property var matrixConditions: {
|
||||||
|
const results = []
|
||||||
|
|
||||||
|
for (let i = 0; i < conditionRepeater.count; i++) {
|
||||||
|
results.push(conditionRepeater.itemAt(i).control.matrixObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing: theme.spacing / 2
|
||||||
|
|
||||||
|
HLabeledItem {
|
||||||
|
label.text: rule.default ? qsTr("Rule ID:") : qsTr("Rule name:")
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
HTextField {
|
||||||
|
id: idField
|
||||||
|
width: parent.width
|
||||||
|
defaultText: rule.rule_id
|
||||||
|
// TODO: minimum length, check no dupe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HRowLayout {
|
||||||
|
Layout.topMargin: theme.spacing / 2
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
text: qsTr("Conditions for a message to trigger this rule:")
|
||||||
|
}
|
||||||
|
|
||||||
|
PositiveButton {
|
||||||
|
icon.name: "pushrule-condition-add"
|
||||||
|
iconItem.small: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: false
|
||||||
|
onClicked: addConditionMenu.open()
|
||||||
|
|
||||||
|
HMenu {
|
||||||
|
id: addConditionMenu
|
||||||
|
x: -width + parent.width
|
||||||
|
y: parent.height
|
||||||
|
|
||||||
|
HMenuItem {
|
||||||
|
text: qsTr("Room has a certain number of members")
|
||||||
|
}
|
||||||
|
HMenuItem {
|
||||||
|
text: qsTr("Message property matches value")
|
||||||
|
}
|
||||||
|
HMenuItem {
|
||||||
|
text: qsTr("Message contains my display name")
|
||||||
|
}
|
||||||
|
HMenuItem {
|
||||||
|
text: qsTr(
|
||||||
|
"Sender has permission to trigger special notification"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HMenuItem {
|
||||||
|
text: qsTr("Custom JSON condition")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
text: qsTr("No conditions added, all messages will match")
|
||||||
|
color: theme.colors.dimText
|
||||||
|
visible: Layout.preferredHeight > 0
|
||||||
|
Layout.preferredHeight: conditionRepeater.count ? 0 : implicitHeight
|
||||||
|
|
||||||
|
Behavior on Layout.preferredHeight { HNumberAnimation {} }
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: conditionRepeater
|
||||||
|
model: JSON.parse(rule.conditions)
|
||||||
|
|
||||||
|
HRowLayout {
|
||||||
|
readonly property Item control: loader.item
|
||||||
|
|
||||||
|
spacing: theme.spacing
|
||||||
|
|
||||||
|
HLoader {
|
||||||
|
id: loader
|
||||||
|
|
||||||
|
readonly property var condition: modelData
|
||||||
|
readonly property string filename:
|
||||||
|
modelData.kind === "event_match" ?
|
||||||
|
"PushEventMatch" :
|
||||||
|
modelData.kind === "contains_display_name" ?
|
||||||
|
"PushContainsDisplayName" :
|
||||||
|
modelData.kind === "room_member_count" ?
|
||||||
|
"PushRoomMemberCount" :
|
||||||
|
modelData.kind === "sender_notification_permission" ?
|
||||||
|
"PushSenderNotificationPermission" :
|
||||||
|
"PushUnknownCondition"
|
||||||
|
|
||||||
|
asynchronous: false
|
||||||
|
source: "PushConditions/" + filename + ".qml"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
NegativeButton {
|
||||||
|
icon.name: "pushrule-condition-remove"
|
||||||
|
iconItem.small: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import "../../../Base"
|
||||||
|
|
||||||
|
HFlow {
|
||||||
|
spacing: theme.spacing / 2
|
||||||
|
|
||||||
|
// transitions break CustomLabel opacity for some reason
|
||||||
|
populate: null
|
||||||
|
add: null
|
||||||
|
move: null
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import ".."
|
||||||
|
import "../../../Base"
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
readonly property var matrixObject: ({kind: "contains_display_name"})
|
||||||
|
|
||||||
|
text: qsTr("Message contains my display name")
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import ".."
|
||||||
|
import "../../../Base"
|
||||||
|
|
||||||
|
CustomFlow {
|
||||||
|
readonly property var matrixObject: ({
|
||||||
|
kind: "event_match",
|
||||||
|
key: keyCombo.editText,
|
||||||
|
pattern: patternField.text,
|
||||||
|
})
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
text: qsTr("Message")
|
||||||
|
verticalAlignment: CustomLabel.AlignVCenter
|
||||||
|
height: keyCombo.height
|
||||||
|
}
|
||||||
|
|
||||||
|
HComboBox {
|
||||||
|
id: keyCombo
|
||||||
|
width: Math.min(implicitWidth, parent.width)
|
||||||
|
editText: condition.key
|
||||||
|
editable: true
|
||||||
|
currentIndex: model.indexOf(condition.key)
|
||||||
|
model: [...new Set([
|
||||||
|
"content.body",
|
||||||
|
"content.msgtype",
|
||||||
|
"room_id",
|
||||||
|
"sender",
|
||||||
|
"state_key",
|
||||||
|
"type",
|
||||||
|
condition.key,
|
||||||
|
])].sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
text: keyCombo.editText === "content.body" ? qsTr("has") : qsTr("is")
|
||||||
|
verticalAlignment: CustomLabel.AlignVCenter
|
||||||
|
height: keyCombo.height
|
||||||
|
}
|
||||||
|
|
||||||
|
HTextField {
|
||||||
|
id: patternField
|
||||||
|
defaultText: condition.pattern
|
||||||
|
width: Math.min(implicitWidth, parent.width)
|
||||||
|
placeholderText: ({
|
||||||
|
"content.body": qsTr("text..."),
|
||||||
|
"content.msgtype": qsTr("e.g. m.image"),
|
||||||
|
"room_id": qsTr("!room:example.org"),
|
||||||
|
"sender": qsTr("@user:example.org"),
|
||||||
|
"state_key": qsTr("@user:example.org"),
|
||||||
|
"type": qsTr("e.g. m.room.message"),
|
||||||
|
}[keyCombo.editText] || qsTr("value"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import ".."
|
||||||
|
import "../../../Base"
|
||||||
|
|
||||||
|
CustomFlow {
|
||||||
|
readonly property var matrixObject: ({
|
||||||
|
kind: "room_member_count",
|
||||||
|
is: operatorCombo.operators[operatorCombo.currentIndex]
|
||||||
|
.replace("==", "") + countSpin.value,
|
||||||
|
})
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
text: qsTr("Room has")
|
||||||
|
verticalAlignment: CustomLabel.AlignVCenter
|
||||||
|
height: operatorCombo.height
|
||||||
|
}
|
||||||
|
|
||||||
|
HComboBox {
|
||||||
|
readonly property var operators: ["==", ">=", "<=", ">", "<"]
|
||||||
|
|
||||||
|
id: operatorCombo
|
||||||
|
width: Math.min(implicitWidth, parent.width)
|
||||||
|
currentIndex: operators.indexOf(/[=<>]+/.exec(condition.is + "==")[0])
|
||||||
|
model: [
|
||||||
|
qsTr("exactly"),
|
||||||
|
qsTr("at least"),
|
||||||
|
qsTr("at most"),
|
||||||
|
qsTr("more than"),
|
||||||
|
qsTr("less than"),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
HSpinBox {
|
||||||
|
id: countSpin
|
||||||
|
width: Math.min(implicitWidth, parent.width)
|
||||||
|
defaultValue: parseInt(condition.is.replace(/[=<>]/, ""), 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
text: qsTr("members")
|
||||||
|
verticalAlignment: CustomLabel.AlignVCenter
|
||||||
|
height: operatorCombo.height
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import ".."
|
||||||
|
import "../../../Base"
|
||||||
|
|
||||||
|
CustomFlow {
|
||||||
|
readonly property var matrixObject: ({
|
||||||
|
kind: "sender_notification_permission",
|
||||||
|
key: keyCombo.editText,
|
||||||
|
})
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
text: qsTr("Sender has permission to send")
|
||||||
|
verticalAlignment: CustomLabel.AlignVCenter
|
||||||
|
height: keyCombo.height
|
||||||
|
}
|
||||||
|
|
||||||
|
HComboBox {
|
||||||
|
id: keyCombo
|
||||||
|
width: Math.min(implicitWidth, parent.width)
|
||||||
|
editable: true
|
||||||
|
editText: condition.key
|
||||||
|
currentIndex: model.indexOf(condition.key)
|
||||||
|
model: [...new Set(["room", condition.key])].sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
text: qsTr("notifications")
|
||||||
|
verticalAlignment: CustomLabel.AlignVCenter
|
||||||
|
height: keyCombo.height
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import ".."
|
||||||
|
import "../../../Base"
|
||||||
|
|
||||||
|
AutoDirectionLayout {
|
||||||
|
readonly property var matrixObject: {
|
||||||
|
try {
|
||||||
|
JSON.parse(jsonField.text)
|
||||||
|
} catch (e) {
|
||||||
|
// TODO
|
||||||
|
return condition.condition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rowSpacing: theme.spacing / 2
|
||||||
|
columnSpacing: rowSpacing
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
text: qsTr("Custom JSON:")
|
||||||
|
verticalAlignment: CustomLabel.AlignVCenter
|
||||||
|
Layout.fillWidth: false
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
HTextField {
|
||||||
|
// TODO: validate the JSON
|
||||||
|
id: jsonField
|
||||||
|
font.family: theme.fontFamily.mono
|
||||||
|
defaultText: JSON.stringify(condition.condition)
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
243
src/gui/Popups/PushRuleSettingsPopup/PushRuleSettingsPopup.qml
Normal file
243
src/gui/Popups/PushRuleSettingsPopup/PushRuleSettingsPopup.qml
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import ".."
|
||||||
|
import "../.."
|
||||||
|
import "../../Base"
|
||||||
|
import "../../Base/Buttons"
|
||||||
|
|
||||||
|
HFlickableColumnPopup {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string userId
|
||||||
|
// A rule item from ModelStore.get(userId, "pushrules")
|
||||||
|
property var rule
|
||||||
|
|
||||||
|
readonly property bool generalChecked:
|
||||||
|
overrideRadio.checked || underrideRadio.checked
|
||||||
|
|
||||||
|
readonly property string checkedKind:
|
||||||
|
overrideRadio.checked ? "override" :
|
||||||
|
contentRadio.checked ? "content" :
|
||||||
|
roomRadio.checked ? "room" :
|
||||||
|
senderRadio.checked ? "sender" :
|
||||||
|
"underride"
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
const details = swipeView.currentItem
|
||||||
|
const isBefore = positionCombo.currentIndex === 0
|
||||||
|
const position =
|
||||||
|
positionCombo.visible && ! positionCombo.isCurrent ?
|
||||||
|
positionCombo.model[positionCombo.currentIndex].rule_id :
|
||||||
|
undefined
|
||||||
|
|
||||||
|
const args = [
|
||||||
|
checkedKind,
|
||||||
|
details.idField.text,
|
||||||
|
rule.kind,
|
||||||
|
rule.rule_id,
|
||||||
|
isBefore && position ? position : undefined,
|
||||||
|
! isBefore && position ? position : undefined,
|
||||||
|
enableCheck.checked,
|
||||||
|
generalChecked ? details.matrixConditions : undefined,
|
||||||
|
contentRadio.checked ? details.idField.text : undefined,
|
||||||
|
]
|
||||||
|
|
||||||
|
py.callClientCoro(userId, "edit_pushrule", args, root.close)
|
||||||
|
}
|
||||||
|
|
||||||
|
page.implicitWidth: Math.min(maximumPreferredWidth, 550 * theme.uiScale)
|
||||||
|
|
||||||
|
page.footer: AutoDirectionLayout {
|
||||||
|
ApplyButton {
|
||||||
|
text: qsTr("Save changes")
|
||||||
|
enabled: true // TODO
|
||||||
|
onClicked: root.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
CancelButton {
|
||||||
|
text: qsTr("Cancel changes")
|
||||||
|
onClicked: root.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
NegativeButton {
|
||||||
|
icon.name: "pushrule-remove"
|
||||||
|
text: qsTr("Remove rule")
|
||||||
|
enabled: ! root.rule.default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
visible: root.rule.default
|
||||||
|
text: qsTr("Some settings cannot be changed for default server rules")
|
||||||
|
color: theme.colors.warningText
|
||||||
|
}
|
||||||
|
|
||||||
|
HColumnLayout {
|
||||||
|
enabled: ! root.rule.default
|
||||||
|
spacing: theme.spacing / 2
|
||||||
|
|
||||||
|
CustomLabel {
|
||||||
|
text: qsTr("Rule type:")
|
||||||
|
}
|
||||||
|
|
||||||
|
HRadioButton {
|
||||||
|
id: overrideRadio
|
||||||
|
text: "High priority general rule"
|
||||||
|
subtitle.text: qsTr(
|
||||||
|
"Control notifications for messages matching certain " +
|
||||||
|
"conditions"
|
||||||
|
)
|
||||||
|
defaultChecked: root.rule.kind === "override"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
HRadioButton {
|
||||||
|
id: contentRadio
|
||||||
|
text: "Message content rule"
|
||||||
|
subtitle.text: qsTr(
|
||||||
|
"Control notifications for text messages containing a " +
|
||||||
|
"certain word"
|
||||||
|
)
|
||||||
|
defaultChecked: root.rule.kind === "content"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
HRadioButton {
|
||||||
|
id: roomRadio
|
||||||
|
text: "Room rule"
|
||||||
|
subtitle.text: qsTr(
|
||||||
|
"Control notifications for all messages received in a " +
|
||||||
|
"certain room"
|
||||||
|
)
|
||||||
|
defaultChecked: root.rule.kind === "room"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
HRadioButton {
|
||||||
|
id: senderRadio
|
||||||
|
text: "Sender rule"
|
||||||
|
subtitle.text: qsTr(
|
||||||
|
"Control notifications for all messages sent by a " +
|
||||||
|
"certain user"
|
||||||
|
)
|
||||||
|
defaultChecked: root.rule.kind === "sender"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
HRadioButton {
|
||||||
|
id: underrideRadio
|
||||||
|
text: "Low priority general rule"
|
||||||
|
subtitle.text: qsTr(
|
||||||
|
"A general rule tested only after every other rule types"
|
||||||
|
)
|
||||||
|
defaultChecked: root.rule.kind === "underride"
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SwipeView {
|
||||||
|
id: swipeView
|
||||||
|
enabled: ! root.rule.default
|
||||||
|
clip: true
|
||||||
|
interactive: false
|
||||||
|
currentIndex:
|
||||||
|
overrideRadio.checked ? 0 :
|
||||||
|
contentRadio.checked ? 1 :
|
||||||
|
roomRadio.checked ? 2 :
|
||||||
|
senderRadio.checked ? 3 :
|
||||||
|
4
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Behavior on implicitHeight { HNumberAnimation {} }
|
||||||
|
|
||||||
|
GeneralRule { enabled: SwipeView.isCurrentItem }
|
||||||
|
ContentRule { enabled: SwipeView.isCurrentItem }
|
||||||
|
RoomRule { enabled: SwipeView.isCurrentItem }
|
||||||
|
SenderRule { enabled: SwipeView.isCurrentItem }
|
||||||
|
GeneralRule { enabled: SwipeView.isCurrentItem }
|
||||||
|
}
|
||||||
|
|
||||||
|
HLabeledItem {
|
||||||
|
visible: ! rule.default && positionCombo.model.length > 1
|
||||||
|
label.text: qsTr("Position:")
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
HComboBox {
|
||||||
|
id: positionCombo
|
||||||
|
|
||||||
|
property int currentPosition: 0
|
||||||
|
|
||||||
|
readonly property string name:
|
||||||
|
! model.length ? "" : utils.stripHtmlTags(
|
||||||
|
utils.formatPushRuleName(root.userId, model[currentIndex])
|
||||||
|
)
|
||||||
|
|
||||||
|
readonly property bool isCurrent:
|
||||||
|
model.length &&
|
||||||
|
currentIndex === currentPosition &&
|
||||||
|
root.rule.kind === root.checkedKind
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
currentIndex: currentPosition
|
||||||
|
displayText:
|
||||||
|
! model.length ? "" :
|
||||||
|
isCurrent ? qsTr("Current") :
|
||||||
|
currentIndex === 0 ? qsTr('Before "%1"').arg(name) :
|
||||||
|
qsTr('After "%1"').arg(name)
|
||||||
|
|
||||||
|
model: {
|
||||||
|
currentPosition = 0
|
||||||
|
|
||||||
|
const choices = []
|
||||||
|
const rules = ModelStore.get(userId, "pushrules")
|
||||||
|
|
||||||
|
for (let i = 0; i < rules.count; i++) {
|
||||||
|
const item = rules.get(i)
|
||||||
|
const isCurrent =
|
||||||
|
item.kind === root.checkedKind &&
|
||||||
|
item.rule_id === root.rule.rule_id
|
||||||
|
|
||||||
|
if (isCurrent && choices.length)
|
||||||
|
currentPosition = choices.length - 1
|
||||||
|
|
||||||
|
if (item.kind === root.checkedKind && ! item.default) {
|
||||||
|
if (! choices.length) choices.push(item)
|
||||||
|
if (! isCurrent) choices.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return choices
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: HMenuItem {
|
||||||
|
readonly property string name:
|
||||||
|
utils.formatPushRuleName(root.userId, modelData)
|
||||||
|
|
||||||
|
label.textFormat: HLabel.StyledText
|
||||||
|
text:
|
||||||
|
model.index === positionCombo.currentPosition &&
|
||||||
|
root.rule.kind === root.checkedKind ?
|
||||||
|
qsTr("Current") :
|
||||||
|
|
||||||
|
model.index === 0 ?
|
||||||
|
qsTr('Before "%1"').arg(name) :
|
||||||
|
|
||||||
|
qsTr('After "%1"').arg(name)
|
||||||
|
|
||||||
|
onTriggered: positionCombo.currentIndex = model.index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HCheckBox {
|
||||||
|
id: enableCheck
|
||||||
|
text: qsTr("Enable this rule")
|
||||||
|
defaultChecked: root.rule.enabled
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
21
src/gui/Popups/PushRuleSettingsPopup/RoomRule.qml
Normal file
21
src/gui/Popups/PushRuleSettingsPopup/RoomRule.qml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import "../../Base"
|
||||||
|
import "../../Base/Buttons"
|
||||||
|
|
||||||
|
HColumnLayout {
|
||||||
|
readonly property alias idField: idField
|
||||||
|
|
||||||
|
HLabeledItem {
|
||||||
|
label.text: qsTr("Room ID:")
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
HTextField {
|
||||||
|
id: idField
|
||||||
|
width: parent.width
|
||||||
|
defaultText: rule.kind === "room" ? rule.rule_id : ""
|
||||||
|
placeholderText: qsTr("!room:example.org")
|
||||||
|
maximumLength: 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
src/gui/Popups/PushRuleSettingsPopup/SenderRule.qml
Normal file
21
src/gui/Popups/PushRuleSettingsPopup/SenderRule.qml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import "../../Base"
|
||||||
|
import "../../Base/Buttons"
|
||||||
|
|
||||||
|
HColumnLayout {
|
||||||
|
readonly property alias idField: idField
|
||||||
|
|
||||||
|
HLabeledItem {
|
||||||
|
label.text: qsTr("User ID:")
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
HTextField {
|
||||||
|
id: idField
|
||||||
|
width: parent.width
|
||||||
|
defaultText: rule.kind === "sender" ? rule.rule_id : ""
|
||||||
|
placeholderText: qsTr("@alice:example.org")
|
||||||
|
maximumLength: 255
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -535,6 +535,7 @@ QtObject {
|
||||||
return {word, start, end: seen}
|
return {word, start, end: seen}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getClassPathRegex(obj) {
|
function getClassPathRegex(obj) {
|
||||||
const regexParts = []
|
const regexParts = []
|
||||||
let parent = obj
|
let parent = obj
|
||||||
|
@ -557,4 +558,79 @@ QtObject {
|
||||||
|
|
||||||
return new RegExp("^" + regexParts.reverse().join("") + "$")
|
return new RegExp("^" + regexParts.reverse().join("") + "$")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function formatPushRuleName(userId, rule) {
|
||||||
|
// rule: item from ModelStore.get(<userId>, "pushrules")
|
||||||
|
|
||||||
|
const roomColor = theme.colors.accentText
|
||||||
|
const room = ModelStore.get(userId, "rooms").find(rule.rule_id)
|
||||||
|
|
||||||
|
return (
|
||||||
|
rule.rule_id === ".m.rule.master" ?
|
||||||
|
qsTr("Any message") :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.suppress_notices" ?
|
||||||
|
qsTr("Messages sent by bots") :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.invite_for_me" ?
|
||||||
|
qsTr("Received room invites") :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.member_event" ?
|
||||||
|
qsTr("Membership, name & avatar changes") :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.contains_display_name" ?
|
||||||
|
qsTr("Messages containing my display name") :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.tombstone" ?
|
||||||
|
qsTr("Room migration alerts") :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.reaction" ?
|
||||||
|
qsTr("Emoji reactions") :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.roomnotif" ?
|
||||||
|
qsTr("Messages containing %1").arg(
|
||||||
|
htmlColorize("@room", roomColor),
|
||||||
|
) :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.contains_user_name" ?
|
||||||
|
qsTr("Contains %1").arg(coloredNameHtml(
|
||||||
|
"", userId, userId.split(":")[0].substring(1),
|
||||||
|
)):
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.call" ?
|
||||||
|
qsTr("Incoming audio calls") :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.encrypted_room_one_to_one" ?
|
||||||
|
qsTr("Encrypted 1-to-1 messages") :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.room_one_to_one" ?
|
||||||
|
qsTr("Unencrypted 1-to-1 messages") :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.message" ?
|
||||||
|
qsTr("Unencrypted group messages") :
|
||||||
|
|
||||||
|
rule.rule_id === ".m.rule.encrypted" ?
|
||||||
|
qsTr("Encrypted group messages") :
|
||||||
|
|
||||||
|
rule.rule_id === ".im.vector.jitsi" ?
|
||||||
|
qsTr("Incoming Jitsi calls") :
|
||||||
|
|
||||||
|
rule.kind === "content" ?
|
||||||
|
qsTr('Contains "%1"').arg(rule.pattern) :
|
||||||
|
|
||||||
|
rule.kind === "sender" ?
|
||||||
|
coloredNameHtml("", rule.rule_id) :
|
||||||
|
|
||||||
|
room && room.display_name && rule.kind !== "room" ?
|
||||||
|
qsTr("Messages in room %1").arg(
|
||||||
|
htmlColorize(escapeHtml(room.display_name), roomColor)
|
||||||
|
) :
|
||||||
|
|
||||||
|
room && room.display_name ?
|
||||||
|
escapeHtml(room.display_name) :
|
||||||
|
|
||||||
|
escapeHtml(rule.rule_id)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
1
src/icons/thin/combo-box-menu.svg
Normal file
1
src/icons/thin/combo-box-menu.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 7.33l2.829-2.83 9.175 9.339 9.167-9.339 2.829 2.83-11.996 12.17z"/></svg>
|
After Width: | Height: | Size: 168 B |
3
src/icons/thin/pushrule-condition-add.svg
Normal file
3
src/icons/thin/pushrule-condition-add.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="m24 10h-10v-10h-4v10h-10v4h10v10h4v-10h10z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 150 B |
3
src/icons/thin/pushrule-condition-remove.svg
Normal file
3
src/icons/thin/pushrule-condition-remove.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="m23 20.168-8.185-8.187 8.185-8.174-2.832-2.807-8.182 8.179-8.176-8.179-2.81 2.81 8.186 8.196-8.186 8.184 2.81 2.81 8.203-8.192 8.18 8.192z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 246 B |
3
src/icons/thin/pushrule-remove.svg
Normal file
3
src/icons/thin/pushrule-remove.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="m9 19c0 .552-.448 1-1 1s-1-.448-1-1v-10c0-.552.448-1 1-1s1 .448 1 1zm4 0c0 .552-.448 1-1 1s-1-.448-1-1v-10c0-.552.448-1 1-1s1 .448 1 1zm4 0c0 .552-.448 1-1 1s-1-.448-1-1v-10c0-.552.448-1 1-1s1 .448 1 1zm5-17v2h-20v-2h5.711c.9 0 1.631-1.099 1.631-2h5.315c0 .901.73 2 1.631 2zm-3 4v16h-14v-16h-2v18h18v-18z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 412 B |
Loading…
Reference in New Issue
Block a user