mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-21 10:44:01 -04:00
notifications: allow extra conditions per rule (#3683)
* notifications: allow extra conditions per rule Co-Authored-By: Claude Opus 5.5 <[email protected]> * notifications: add translator context to the rule condition strings Co-Authored-By: Claude Opus 5.5 <[email protected]> * don't use deprecated widgets --------- Co-authored-by: Claude Opus 5.5 <[email protected]> Co-authored-by: bbedward <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
bbedward
parent
566ccf2fde
commit
93bd3eecd1
@@ -3399,7 +3399,7 @@ Singleton {
|
||||
if (!app && !desktop)
|
||||
return -1;
|
||||
return rules.findIndex(rule => {
|
||||
if (!predicate(rule) || (rule.matchType || "contains").toString().toLowerCase() !== "exact")
|
||||
if (!predicate(rule) || (rule.matchType || "contains").toString().toLowerCase() !== "exact" || (rule.conditions || []).length > 0)
|
||||
return false;
|
||||
const pattern = (rule.pattern || "").toString().toLowerCase();
|
||||
const value = !rule.field || rule.field === "appName" ? app : rule.field === "desktopEntry" ? desktop : "";
|
||||
|
||||
@@ -20,6 +20,7 @@ DDialog {
|
||||
property string ruleAction: ""
|
||||
property string ruleUrgency: ""
|
||||
property bool ruleBypassDnd: false
|
||||
property var ruleConditions: []
|
||||
readonly property bool isEditMode: editingIndex >= 0
|
||||
|
||||
signal ruleSubmitted
|
||||
@@ -54,6 +55,11 @@ DDialog {
|
||||
ruleAction = rule.action || actionOptions[0].value;
|
||||
ruleUrgency = rule.urgency || urgencyOptions[0].value;
|
||||
ruleBypassDnd = rule.bypassDnd === true;
|
||||
ruleConditions = (rule.conditions || []).map(condition => ({
|
||||
field: condition.field || fieldOptions[0].value,
|
||||
matchType: condition.matchType || matchTypeOptions[0].value,
|
||||
pattern: condition.pattern || ""
|
||||
}));
|
||||
patternInput.text = rule.pattern || "";
|
||||
fieldDropdown.currentValue = labelOf(fieldOptions, ruleField);
|
||||
matchTypeDropdown.currentValue = labelOf(matchTypeOptions, ruleMatchType);
|
||||
@@ -66,6 +72,7 @@ DDialog {
|
||||
field: ruleField,
|
||||
pattern: patternInput.text,
|
||||
matchType: ruleMatchType,
|
||||
conditions: ruleConditions.filter(condition => condition.pattern.trim() !== "").map(condition => Object.assign({}, condition)),
|
||||
action: ruleAction,
|
||||
urgency: ruleUrgency,
|
||||
bypassDnd: ruleBypassDnd
|
||||
@@ -77,6 +84,20 @@ DDialog {
|
||||
ruleSubmitted();
|
||||
}
|
||||
|
||||
function addCondition() {
|
||||
ruleConditions = ruleConditions.concat([
|
||||
{
|
||||
field: fieldOptions[0].value,
|
||||
matchType: matchTypeOptions[0].value,
|
||||
pattern: ""
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
function removeCondition(index) {
|
||||
ruleConditions = ruleConditions.filter((_, i) => i !== index);
|
||||
}
|
||||
|
||||
actions: [
|
||||
DButton {
|
||||
text: I18n.tr("Cancel")
|
||||
@@ -129,6 +150,77 @@ DDialog {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Edits go to the array entry in place: modelData is a copy, and reassigning the array would rebuild the rows and drop focus.
|
||||
Repeater {
|
||||
model: root.ruleConditions
|
||||
|
||||
delegate: SettingsRow {
|
||||
id: conditionRow
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
body: Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DDropdown {
|
||||
id: conditionFieldDropdown
|
||||
width: Math.round(parent.width / 3)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
compactMode: true
|
||||
Accessible.name: I18n.tr("Field", "notification rule dropdown label, which notification field to match")
|
||||
options: root.fieldOptions.map(option => option.label)
|
||||
currentValue: root.labelOf(root.fieldOptions, conditionRow.modelData.field)
|
||||
onValueChanged: value => root.ruleConditions[conditionRow.index].field = root.valueOf(root.fieldOptions, value)
|
||||
}
|
||||
|
||||
DDropdown {
|
||||
id: conditionMatchTypeDropdown
|
||||
width: Math.round(parent.width / 4)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
compactMode: true
|
||||
Accessible.name: I18n.tr("Match type", "notification rule dropdown label, how the pattern is compared")
|
||||
options: root.matchTypeOptions.map(option => option.label)
|
||||
currentValue: root.labelOf(root.matchTypeOptions, conditionRow.modelData.matchType)
|
||||
onValueChanged: value => root.ruleConditions[conditionRow.index].matchType = root.valueOf(root.matchTypeOptions, value)
|
||||
}
|
||||
|
||||
DTextField {
|
||||
id: conditionPatternInput
|
||||
width: parent.width - conditionFieldDropdown.width - conditionMatchTypeDropdown.width - removeConditionButton.width - parent.spacing * 3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
outlined: true
|
||||
placeholderText: I18n.tr("Pattern", "noun, text field label for a match or filter pattern")
|
||||
Component.onCompleted: text = conditionRow.modelData.pattern
|
||||
onTextEdited: root.ruleConditions[conditionRow.index].pattern = text
|
||||
onAccepted: root.submit()
|
||||
}
|
||||
|
||||
DActionButton {
|
||||
id: removeConditionButton
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: "delete"
|
||||
iconColor: Theme.error
|
||||
Accessible.name: I18n.tr("Remove condition", "button in the notification rule editor, removes this match condition")
|
||||
onClicked: root.removeCondition(conditionRow.index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsRow {
|
||||
subtitle: root.ruleConditions.length > 0 ? I18n.tr("A notification must match every condition", "hint in the notification rule editor, all match conditions must apply") : ""
|
||||
|
||||
DButton {
|
||||
text: I18n.tr("Add condition", "button in the notification rule editor, adds another match condition")
|
||||
iconName: "add"
|
||||
backgroundColor: "transparent"
|
||||
textColor: Theme.primary
|
||||
onClicked: root.addCondition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
|
||||
@@ -109,7 +109,11 @@ Item {
|
||||
}
|
||||
|
||||
function matchSummary(rule) {
|
||||
return [getRuleOptionLabel(notificationRuleFieldOptions, rule.field, notificationRuleFieldOptions[0].label), getRuleOptionLabel(notificationRuleMatchTypeOptions, rule.matchType, notificationRuleMatchTypeOptions[0].label)].join(" · ");
|
||||
const parts = [getRuleOptionLabel(notificationRuleFieldOptions, rule.field, notificationRuleFieldOptions[0].label), getRuleOptionLabel(notificationRuleMatchTypeOptions, rule.matchType, notificationRuleMatchTypeOptions[0].label)];
|
||||
const extra = (rule.conditions || []).length;
|
||||
if (extra > 0)
|
||||
parts.push(I18n.tr("+%1 conditions", "notification rule summary, %1 is the number of extra conditions").arg(extra));
|
||||
return parts.join(" · ");
|
||||
}
|
||||
|
||||
function remainingLabel(rule) {
|
||||
|
||||
@@ -458,25 +458,27 @@ Singleton {
|
||||
if (rule.enabled === false)
|
||||
return false;
|
||||
|
||||
const pattern = (rule.pattern || "").toString();
|
||||
if (!pattern.trim())
|
||||
return false;
|
||||
|
||||
const value = (_ruleFieldValue(rule.field, info) || "").toString();
|
||||
const matchType = (rule.matchType || "contains").toString().toLowerCase();
|
||||
|
||||
if (matchType === "exact")
|
||||
return value.toLowerCase() === pattern.toLowerCase();
|
||||
if (matchType === "regex") {
|
||||
try {
|
||||
return new RegExp(pattern, "i").test(value);
|
||||
} catch (e) {
|
||||
log.warn("invalid notification rule regex:", pattern);
|
||||
return [rule, ...(rule.conditions || [])].every(condition => {
|
||||
const pattern = (condition.pattern || "").toString();
|
||||
if (!pattern.trim())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return value.toLowerCase().includes(pattern.toLowerCase());
|
||||
const value = (_ruleFieldValue(condition.field, info) || "").toString();
|
||||
const matchType = (condition.matchType || "contains").toString().toLowerCase();
|
||||
|
||||
if (matchType === "exact")
|
||||
return value.toLowerCase() === pattern.toLowerCase();
|
||||
if (matchType === "regex") {
|
||||
try {
|
||||
return new RegExp(pattern, "i").test(value);
|
||||
} catch (e) {
|
||||
log.warn("invalid notification rule regex:", pattern);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return value.toLowerCase().includes(pattern.toLowerCase());
|
||||
});
|
||||
}
|
||||
|
||||
function _allowedInDnd(urgency, bypassDnd) {
|
||||
|
||||
Reference in New Issue
Block a user