Add Global Notification Settings

This add the final list of settings in the main setting window as a new page notifications as there are quite a few now. This completes previous work on push rules giving the ability to set the default global rules. Adding keyword rules is also now supported. 

This also uses the new mobileform layout. The settings are designed to give some visual feedback as options for whether notifications are on/off, play a sound or are highlighted are chosen. The left icon is designed to mimic the notification dot in the roomlist. The whole mobileform delegate can also be clicked to cycle through the available options.

The rationale for whether an option is available is as follows:
- Highlight is not available if would lead to every message in a room being highlighted
- Keyword notifications cannot be switched off instead the rule is just deleted
- Only keyword rules can be deleted, default rules cannot be touched

There is also rules plumbed in for features that don't exist in neochat yet, i.e. encrypted chats and rooms, calls. I figured I may as well plumb these in and test them my plan was to hide them before merge, they can then be unhidden when the features are complete.

![image](/uploads/12fa8378847887ea7234e22b1460f952/image.png)
This commit is contained in:
James Graham
2022-11-16 20:59:35 +00:00
committed by Carl Schwan
parent c3fcd280fb
commit 1946228d2b
12 changed files with 1030 additions and 35 deletions

View File

@@ -72,22 +72,8 @@ Kirigami.ScrollablePage {
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormCardHeader {
title: i18n("Notifications and events")
title: i18n("Timeline Events")
}
MobileForm.FormCheckDelegate {
id: showNotificationsDelegate
// TODO: When there are enough notification and timeline event
// settings, make 2 separate groups with FormData labels.
text: i18n("Show notifications")
checked: Config.showNotifications
enabled: !Config.isShowNotificationsImmutable
onToggled: {
Config.showNotifications = checked
Config.save()
}
}
MobileForm.FormDelegateSeparator { above: showNotificationsDelegate; below: showLeaveJoinEventDelegate }
MobileForm.FormCheckDelegate {
id: showLeaveJoinEventDelegate

View File

@@ -0,0 +1,328 @@
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm
import org.kde.neochat 1.0
Kirigami.ScrollablePage {
title: i18nc("@title:window", "Notifications")
ColumnLayout {
id: notificationLayout
anchors.fill: parent
MobileForm.FormCard {
Layout.fillWidth: true
contentItem: MobileForm.FormSwitchDelegate {
text: i18n("Enable notifications for this account")
checked: Config.showNotifications
enabled: !Config.isShowNotificationsImmutable
onToggled: {
Config.showNotifications = checked
Config.save()
NotificationsManager.globalNotificationsEnabled = checked
}
}
}
MobileForm.FormCard {
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormCardHeader {
title: i18n("Room Notifications")
}
NotificationRuleItem {
text: i18n("Messages in one-to-one chats")
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.oneToOneNotificationAction)
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.oneToOneNotificationAction)
enabled: NotificationsManager.oneToOneNotificationAction !== PushNotificationAction.Unknown
notificationAction: NotificationsManager.oneToOneNotificationAction
onNotificationActionChanged: {
if (notificationAction && NotificationsManager.oneToOneNotificationAction != notificationAction) {
NotificationsManager.oneToOneNotificationAction = notificationAction
}
}
}
NotificationRuleItem {
text: i18n("Encrypted messages in one-to-one chats")
visible: Controller.encryptionSupported
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.encryptedOneToOneNotificationAction)
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.encryptedOneToOneNotificationAction)
enabled: NotificationsManager.encryptedOneToOneNotificationAction !== PushNotificationAction.Unknown
notificationAction: NotificationsManager.encryptedOneToOneNotificationAction
onNotificationActionChanged: {
if (notificationAction && NotificationsManager.encryptedOneToOneNotificationAction != notificationAction) {
NotificationsManager.encryptedOneToOneNotificationAction = notificationAction
}
}
}
NotificationRuleItem {
text: i18n("Messages in group chats")
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.groupChatNotificationAction)
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.groupChatNotificationAction)
enabled: NotificationsManager.groupChatNotificationAction !== PushNotificationAction.Unknown
notificationAction: NotificationsManager.groupChatNotificationAction
onNotificationActionChanged: {
if (notificationAction && NotificationsManager.groupChatNotificationAction != notificationAction) {
NotificationsManager.groupChatNotificationAction = notificationAction
}
}
}
NotificationRuleItem {
text: i18n("Messages in encrypted group chats")
visible: Controller.encryptionSupported
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.encryptedGroupChatNotificationAction)
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.encryptedGroupChatNotificationAction)
enabled: NotificationsManager.encryptedGroupChatNotificationAction !== PushNotificationAction.Unknown
notificationAction: NotificationsManager.encryptedGroupChatNotificationAction
onNotificationActionChanged: {
if (notificationAction && NotificationsManager.encryptedGroupChatNotificationAction != notificationAction) {
NotificationsManager.encryptedGroupChatNotificationAction = notificationAction
}
}
}
NotificationRuleItem {
text: i18n("Room upgrade messages")
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.tombstoneNotificationAction)
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.tombstoneNotificationAction)
highlightable: true
highlightOn: notificationLayout.isNotificationRuleHighlight(NotificationsManager.tombstoneNotificationAction)
enabled: NotificationsManager.tombstoneNotificationAction !== PushNotificationAction.Unknown
notificationAction: NotificationsManager.tombstoneNotificationAction
onNotificationActionChanged: {
if (notificationAction && NotificationsManager.tombstoneNotificationAction != notificationAction) {
NotificationsManager.tombstoneNotificationAction = notificationAction
}
}
}
}
}
MobileForm.FormCard {
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormCardHeader {
title: i18n("@Mentions")
}
NotificationRuleItem {
text: i18n("Messages containing my display name")
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.displayNameNotificationAction)
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.displayNameNotificationAction)
highlightable: true
highlightOn: notificationLayout.isNotificationRuleHighlight(NotificationsManager.displayNameNotificationAction)
enabled: NotificationsManager.displayNameNotificationAction !== PushNotificationAction.Unknown
notificationAction: NotificationsManager.displayNameNotificationAction
onNotificationActionChanged: {
if (notificationAction && NotificationsManager.displayNameNotificationAction != notificationAction) {
NotificationsManager.displayNameNotificationAction = notificationAction
}
}
}
NotificationRuleItem {
text: i18n("Whole room (@room) notifications")
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.roomNotificationAction)
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.roomNotificationAction)
highlightable: true
highlightOn: notificationLayout.isNotificationRuleHighlight(NotificationsManager.roomNotificationAction)
enabled: NotificationsManager.roomNotificationAction !== PushNotificationAction.Unknown
notificationAction: NotificationsManager.roomNotificationAction
onNotificationActionChanged: {
if (notificationAction && NotificationsManager.roomNotificationAction != notificationAction) {
NotificationsManager.roomNotificationAction = notificationAction
}
}
}
}
}
MobileForm.FormCard {
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormCardHeader {
title: i18n("Keywords")
}
NotificationRuleItem {
id: keywordNotificationAction
text: i18n("Messages containing my keywords")
notificationsOn: true
notificationsOnModifiable: false
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.keywordNotificationAction)
highlightable: true
highlightOn: notificationLayout.isNotificationRuleHighlight(NotificationsManager.keywordNotificationAction)
enabled: NotificationsManager.keywordNotificationAction !== PushNotificationAction.Unknown &&
NotificationsManager.keywordNotificationAction !== PushNotificationAction.Off
notificationAction: NotificationsManager.keywordNotificationAction
onNotificationActionChanged: {
if (notificationAction && NotificationsManager.keywordNotificationAction != notificationAction) {
NotificationsManager.keywordNotificationAction = notificationAction
}
}
}
MobileForm.FormDelegateSeparator {}
Repeater {
model: KeywordNotificationRuleModel {
id: keywordNotificationRuleModel
}
delegate: NotificationRuleItem {
text: name
notificationAction: keywordNotificationAction.notificationAction
notificationsOn: keywordNotificationAction.notificationsOn
notificationsOnModifiable: false
noisyOn: keywordNotificationAction.noisyOn
noisyModifiable: false
highlightOn: keywordNotificationAction.highlightOn
deletable: true
onDeleteItemChanged: {
if (deleteItem && deletable) {
keywordNotificationRuleModel.removeKeywordAtIndex(index)
}
}
}
}
MobileForm.AbstractFormDelegate {
Layout.fillWidth: true
contentItem : RowLayout {
Kirigami.ActionTextField {
id: keywordAddField
Layout.fillWidth: true
placeholderText: i18n("Keyword…")
rightActions: Kirigami.Action {
icon.name: "edit-clear"
visible: keywordAddField.text.length > 0
onTriggered: {
keywordAddField.text = ""
}
}
onAccepted: {
keywordNotificationRuleModel.addKeyword(keywordAddField.text, PushNotificationAction.On)
keywordAddField.text = ""
}
}
QQC2.Button {
id: addButton
text: i18n("Add keyword")
Accessible.name: text
icon.name: "list-add"
display: QQC2.AbstractButton.IconOnly
onClicked: {
keywordNotificationRuleModel.addKeyword(keywordAddField.text, PushNotificationAction.On)
keywordAddField.text = ""
}
QQC2.ToolTip {
text: addButton.text
delay: Kirigami.Units.toolTipDelay
}
}
}
}
}
}
MobileForm.FormCard {
Layout.fillWidth: true
contentItem: ColumnLayout {
spacing: 0
MobileForm.FormCardHeader {
title: i18n("Invites")
}
NotificationRuleItem {
text: i18n("Invites to a room")
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.inviteNotificationAction)
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.inviteNotificationAction)
highlightable: true
highlightOn: notificationLayout.isNotificationRuleHighlight(NotificationsManager.inviteNotificationAction)
enabled: NotificationsManager.inviteNotificationAction !== PushNotificationAction.Unknown
notificationAction: NotificationsManager.inviteNotificationAction
onNotificationActionChanged: {
if (notificationAction && NotificationsManager.inviteNotificationAction != notificationAction) {
NotificationsManager.inviteNotificationAction = notificationAction
}
}
}
NotificationRuleItem {
text: i18n("Call invitation")
// TODO enable this option when calls are supported
visible: false
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.callInviteNotificationAction)
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.callInviteNotificationAction)
highlightable: true
highlightOn: notificationLayout.isNotificationRuleHighlight(NotificationsManager.callInviteNotificationAction)
enabled: NotificationsManager.callInviteNotificationAction !== PushNotificationAction.Unknown
notificationAction: NotificationsManager.callInviteNotificationAction
onNotificationActionChanged: {
if (notificationAction && NotificationsManager.callInviteNotificationAction != notificationAction) {
NotificationsManager.callInviteNotificationAction = notificationAction
}
}
}
}
}
function isNotificationRuleOn(action) {
return action == PushNotificationAction.On ||
action == PushNotificationAction.Noisy ||
action == PushNotificationAction.Highlight ||
action == PushNotificationAction.NoisyHighlight
}
function isNotificationRuleNoisy(action) {
return action == PushNotificationAction.Noisy ||
action == PushNotificationAction.NoisyHighlight
}
function isNotificationRuleHighlight(action) {
return action == PushNotificationAction.Highlight ||
action == PushNotificationAction.NoisyHighlight
}
}
}

View File

@@ -0,0 +1,182 @@
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami
import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm
import org.kde.neochat 1.0
MobileForm.AbstractFormDelegate {
id: notificationRuleItem
property var notificationAction: PushNotificationAction.Unkown
property bool notificationsOn: false
property bool notificationsOnModifiable: true
property bool noisyOn: false
property bool noisyModifiable: true
property bool highlightOn: false
property bool highlightable: false
property bool deleteItem: false
property bool deletable: false
Layout.fillWidth: true
onClicked: {
notificationAction = nextNotificationRuleAction(notificationAction)
}
contentItem : RowLayout {
spacing: Kirigami.Units.largeSpacing
QQC2.Label {
Layout.minimumWidth: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing
Layout.minimumHeight: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing
text: notificationsOn ? "" : "●"
color: Kirigami.Theme.textColor
horizontalAlignment: Text.AlignHCenter
background: Rectangle {
visible: notificationsOn
Kirigami.Theme.colorSet: Kirigami.Theme.Button
color: highlightOn ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.disabledTextColor
opacity: highlightOn ? 1 : 0.3
radius: height / 2
}
}
QQC2.Label {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
text: notificationRuleItem.text
elide: Text.ElideRight
wrapMode: Text.Wrap
maximumLineCount: 2
}
RowLayout {
Layout.alignment: Qt.AlignRight
QQC2.Button {
id: onButton
text: onButton.checked ? i18n("Disable notifications") : i18n("Enable notifications")
Accessible.name: text
icon.name: checked ? "notifications" : "notifications-disabled"
display: QQC2.AbstractButton.IconOnly
visible: notificationRuleItem.notificationsOnModifiable
checkable: true
checked: notificationRuleItem.notificationsOn
enabled: notificationRuleItem.enabled
down: checked
onToggled: {
notificationRuleItem.notificationAction = notificationRuleItem.notifcationRuleAction()
}
QQC2.ToolTip {
text: onButton.text
delay: Kirigami.Units.toolTipDelay
}
}
QQC2.Button {
id: noisyButton
text: noisyButton.checked ? i18n("Mute notifications") : i18n("Unmute notifications")
Accessible.name: text
icon.name: checked ? "audio-volume-high" : "audio-volume-muted"
display: QQC2.AbstractButton.IconOnly
visible: notificationRuleItem.noisyModifiable
checkable: true
checked: notificationRuleItem.noisyOn
enabled: (onButton.checked || !notificationRuleItem.notificationsOnModifiable) && notificationRuleItem.enabled
down: checked
onToggled: {
notificationRuleItem.notificationAction = notificationRuleItem.notifcationRuleAction()
}
QQC2.ToolTip {
text: noisyButton.text
delay: Kirigami.Units.toolTipDelay
}
}
QQC2.Button {
id: highlightButton
text: highlightButton.checked ? i18nc("As in clicking this button will switch off highlights for messages that match this rule", "Disable message highlights") : i18nc("As in clicking this button will switch on highlights for messages that match this rule", "Enable message highlights")
Accessible.name: text
icon.name: "draw-highlight"
display: QQC2.AbstractButton.IconOnly
visible: notificationRuleItem.highlightable
checkable: true
checked: notificationRuleItem.highlightOn
enabled: (onButton.checked || !notificationRuleItem.notificationsOnModifiable) && notificationRuleItem.enabled
down: checked
onToggled: {
notificationRuleItem.notificationAction = notificationRuleItem.notifcationRuleAction()
}
QQC2.ToolTip {
text: highlightButton.text
delay: Kirigami.Units.toolTipDelay
}
}
QQC2.Button {
id: deleteButton
Accessible.name: i18n("Delete keyword")
icon.name: "edit-delete-remove"
visible: notificationRuleItem.deletable
onClicked: {
notificationRuleItem.deleteItem = !notificationRuleItem.deleteItem
}
}
}
}
function notifcationRuleAction() {
if (onButton.checked) {
if (noisyButton.checked && highlightButton.checked) {
return PushNotificationAction.NoisyHighlight
} else if (noisyButton.checked) {
return PushNotificationAction.Noisy
} else if (highlightButton.checked) {
return PushNotificationAction.Highlight
} else {
return PushNotificationAction.On
}
} else {
return PushNotificationAction.Off
}
}
function nextNotificationRuleAction(action) {
let finished = false
if (action == PushNotificationAction.NoisyHighlight) {
action = PushNotificationAction.Off
} else {
action += 1
}
while (!finished) {
if (action == PushNotificationAction.Off && !notificationRuleItem.notificationsOnModifiable) {
action = PushNotificationAction.On
} else if (action == PushNotificationAction.Noisy && !notificationRuleItem.noisyModifiable) {
action = PushNotificationAction.Highlight
} else if (action == PushNotificationAction.Highlight && !notificationRuleItem.highlightable) {
action = PushNotificationAction.Off
} else {
finished = true
}
}
return action
}
}

View File

@@ -18,6 +18,11 @@ Kirigami.CategorizedSettings {
icon.name: "preferences-desktop-theme-global"
page: Qt.resolvedUrl("AppearanceSettingsPage.qml")
},
Kirigami.SettingAction {
text: i18n("Notifications")
icon.name: "preferences-desktop-notification"
page: Qt.resolvedUrl("GlobalNotificationsPage.qml")
},
Kirigami.SettingAction {
text: i18n("Accounts")
icon.name: "preferences-system-users"