mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-15 15:54:00 -04:00
* 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]>
251 lines
10 KiB
QML
251 lines
10 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import qs.Common
|
|
import qs.DCommon.Widgets
|
|
import qs.Widgets
|
|
import qs.Modules.Settings.Widgets
|
|
|
|
DDialog {
|
|
id: root
|
|
|
|
required property var fieldOptions
|
|
required property var matchTypeOptions
|
|
required property var actionOptions
|
|
required property var urgencyOptions
|
|
|
|
property int editingIndex: -1
|
|
property string ruleField: ""
|
|
property string ruleMatchType: ""
|
|
property string ruleAction: ""
|
|
property string ruleUrgency: ""
|
|
property bool ruleBypassDnd: false
|
|
property var ruleConditions: []
|
|
readonly property bool isEditMode: editingIndex >= 0
|
|
|
|
signal ruleSubmitted
|
|
|
|
embedded: nativeWindow
|
|
opened: nativeWindow
|
|
maximumWidth: SettingsMetrics.formDialogWidth
|
|
surfaceColor: Theme.hostSurface
|
|
title: isEditMode ? I18n.tr("Edit rule") : I18n.tr("Add rule", "notification rule dialog title and button")
|
|
acceptEnabled: patternInput.text.trim() !== ""
|
|
onAccepted: submit()
|
|
|
|
function labelOf(options, value) {
|
|
return (options.find(option => option.value === value) ?? options[0]).label;
|
|
}
|
|
|
|
function valueOf(options, label) {
|
|
return (options.find(option => option.label === label) ?? options[0]).value;
|
|
}
|
|
|
|
function show(index, rule) {
|
|
editingIndex = index;
|
|
populate(rule || {});
|
|
opened = true;
|
|
forceActiveFocus();
|
|
patternInput.forceActiveFocus();
|
|
}
|
|
|
|
function populate(rule) {
|
|
ruleField = rule.field || fieldOptions[0].value;
|
|
ruleMatchType = rule.matchType || matchTypeOptions[0].value;
|
|
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);
|
|
}
|
|
|
|
function submit() {
|
|
if (!acceptEnabled)
|
|
return;
|
|
const rule = {
|
|
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
|
|
};
|
|
if (isEditMode)
|
|
SettingsData.updateNotificationRule(editingIndex, rule);
|
|
else
|
|
SettingsData.addNotificationRule(rule);
|
|
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")
|
|
backgroundColor: "transparent"
|
|
textColor: Theme.primary
|
|
onClicked: root.rejected()
|
|
},
|
|
DButton {
|
|
text: root.isEditMode ? I18n.tr("Save") : I18n.tr("Add")
|
|
enabled: root.acceptEnabled
|
|
onClicked: root.submit()
|
|
}
|
|
]
|
|
|
|
SettingsCard {
|
|
title: I18n.tr("Match Criteria")
|
|
|
|
SettingsRow {
|
|
body: Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingS
|
|
|
|
DDropdown {
|
|
id: fieldDropdown
|
|
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)
|
|
onValueChanged: value => root.ruleField = root.valueOf(root.fieldOptions, value)
|
|
}
|
|
|
|
DDropdown {
|
|
id: matchTypeDropdown
|
|
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)
|
|
onValueChanged: value => root.ruleMatchType = root.valueOf(root.matchTypeOptions, value)
|
|
}
|
|
|
|
DTextField {
|
|
id: patternInput
|
|
width: parent.width - fieldDropdown.width - matchTypeDropdown.width - parent.spacing * 2
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
outlined: true
|
|
placeholderText: I18n.tr("Pattern", "noun, text field label for a match or filter pattern")
|
|
onAccepted: root.submit()
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
title: I18n.tr("Actions")
|
|
|
|
SettingsDropdownRow {
|
|
text: I18n.tr("Action", "noun, dropdown label for what a notification rule or keybind does")
|
|
description: I18n.tr("The Default action only overrides priority")
|
|
options: root.actionOptions.map(option => option.label)
|
|
currentValue: root.labelOf(root.actionOptions, root.ruleAction)
|
|
onValueChanged: value => root.ruleAction = root.valueOf(root.actionOptions, value)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
text: I18n.tr("Priority", "notification rule dropdown label, urgency assigned to matches")
|
|
options: root.urgencyOptions.map(option => option.label)
|
|
currentValue: root.labelOf(root.urgencyOptions, root.ruleUrgency)
|
|
onValueChanged: value => root.ruleUrgency = root.valueOf(root.urgencyOptions, value)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
text: I18n.tr("Allow in Do Not Disturb")
|
|
checked: root.ruleBypassDnd
|
|
onToggled: checked => root.ruleBypassDnd = checked
|
|
}
|
|
}
|
|
}
|