Push Rule Model Rework
This is a significant rework of the handling of push rules. Rather than using a lot of boilerplate code for the default models `KeywordNotificationModel` has been converted to `PushRuleModel` and now handles all push rules. The new model has the following features: - Handles all push rules - Has special handling for the names of default keywords (i.e. it still gives the same text as previously for showing in the settings menus) - Push rules for blocking individuals or room overrides are still there but hidden so will be available for developer tools (to follow) - Room specific keywords are now supported. The notification settings pages have also been refactored to take advantage of the new models. Each section is now just a repeater with a filter for the rules that it should contain. The push rule delegate has now been cleaned up and uses required properties. Implements network/neochat#574
This commit is contained in:
@@ -24,7 +24,7 @@ add_library(neochat STATIC
|
|||||||
models/userfiltermodel.cpp
|
models/userfiltermodel.cpp
|
||||||
models/publicroomlistmodel.cpp
|
models/publicroomlistmodel.cpp
|
||||||
models/userdirectorylistmodel.cpp
|
models/userdirectorylistmodel.cpp
|
||||||
models/keywordnotificationrulemodel.cpp
|
models/pushrulemodel.cpp
|
||||||
models/emoticonfiltermodel.cpp
|
models/emoticonfiltermodel.cpp
|
||||||
notificationsmanager.cpp
|
notificationsmanager.cpp
|
||||||
models/sortfilterroomlistmodel.cpp
|
models/sortfilterroomlistmodel.cpp
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
#include "models/pushrulemodel.h"
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||||
#include <qt5keychain/keychain.h>
|
#include <qt5keychain/keychain.h>
|
||||||
@@ -126,6 +127,10 @@ Controller::Controller(QObject *parent)
|
|||||||
oldAccountCount = Accounts.size();
|
oldAccountCount = Accounts.size();
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QTimer::singleShot(0, this, [this] {
|
||||||
|
m_pushRuleModel = new PushRuleModel;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller &Controller::instance()
|
Controller &Controller::instance()
|
||||||
@@ -507,6 +512,11 @@ void Controller::setActiveConnection(Connection *connection)
|
|||||||
Q_EMIT activeAccountLabelChanged();
|
Q_EMIT activeAccountLabelChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PushRuleModel *Controller::pushRuleModel() const
|
||||||
|
{
|
||||||
|
return m_pushRuleModel;
|
||||||
|
}
|
||||||
|
|
||||||
void Controller::saveWindowGeometry()
|
void Controller::saveWindowGeometry()
|
||||||
{
|
{
|
||||||
WindowController::instance().saveGeometry();
|
WindowController::instance().saveGeometry();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "models/pushrulemodel.h"
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QQuickItem>
|
#include <QQuickItem>
|
||||||
|
|
||||||
@@ -50,6 +51,11 @@ class Controller : public QObject
|
|||||||
*/
|
*/
|
||||||
Q_PROPERTY(Quotient::Connection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged)
|
Q_PROPERTY(Quotient::Connection *activeConnection READ activeConnection WRITE setActiveConnection NOTIFY activeConnectionChanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The PushRuleModel that has the active connection's push rules.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(PushRuleModel *pushRuleModel READ pushRuleModel CONSTANT)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief The row number in the accounts directory of the active connection.
|
* @brief The row number in the accounts directory of the active connection.
|
||||||
*/
|
*/
|
||||||
@@ -119,6 +125,8 @@ public:
|
|||||||
void setActiveConnection(Quotient::Connection *connection);
|
void setActiveConnection(Quotient::Connection *connection);
|
||||||
[[nodiscard]] Quotient::Connection *activeConnection() const;
|
[[nodiscard]] Quotient::Connection *activeConnection() const;
|
||||||
|
|
||||||
|
[[nodiscard]] PushRuleModel *pushRuleModel() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add a new connection to the account registry.
|
* @brief Add a new connection to the account registry.
|
||||||
*/
|
*/
|
||||||
@@ -236,6 +244,8 @@ private:
|
|||||||
|
|
||||||
bool hasWindowSystem() const;
|
bool hasWindowSystem() const;
|
||||||
|
|
||||||
|
QPointer<PushRuleModel> m_pushRuleModel;
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void invokeLogin();
|
void invokeLogin();
|
||||||
void showWindow();
|
void showWindow();
|
||||||
|
|||||||
@@ -57,13 +57,13 @@
|
|||||||
#include "models/emojimodel.h"
|
#include "models/emojimodel.h"
|
||||||
#include "models/emoticonfiltermodel.h"
|
#include "models/emoticonfiltermodel.h"
|
||||||
#include "models/imagepacksmodel.h"
|
#include "models/imagepacksmodel.h"
|
||||||
#include "models/keywordnotificationrulemodel.h"
|
|
||||||
#include "models/livelocationsmodel.h"
|
#include "models/livelocationsmodel.h"
|
||||||
#include "models/locationsmodel.h"
|
#include "models/locationsmodel.h"
|
||||||
#include "models/mediamessagefiltermodel.h"
|
#include "models/mediamessagefiltermodel.h"
|
||||||
#include "models/messageeventmodel.h"
|
#include "models/messageeventmodel.h"
|
||||||
#include "models/messagefiltermodel.h"
|
#include "models/messagefiltermodel.h"
|
||||||
#include "models/publicroomlistmodel.h"
|
#include "models/publicroomlistmodel.h"
|
||||||
|
#include "models/pushrulemodel.h"
|
||||||
#include "models/reactionmodel.h"
|
#include "models/reactionmodel.h"
|
||||||
#include "models/roomlistmodel.h"
|
#include "models/roomlistmodel.h"
|
||||||
#include "models/searchmodel.h"
|
#include "models/searchmodel.h"
|
||||||
@@ -257,13 +257,15 @@ int main(int argc, char *argv[])
|
|||||||
#ifdef QUOTIENT_07
|
#ifdef QUOTIENT_07
|
||||||
qmlRegisterType<PollHandler>("org.kde.neochat", 1, 0, "PollHandler");
|
qmlRegisterType<PollHandler>("org.kde.neochat", 1, 0, "PollHandler");
|
||||||
#endif
|
#endif
|
||||||
qmlRegisterType<KeywordNotificationRuleModel>("org.kde.neochat", 1, 0, "KeywordNotificationRuleModel");
|
qmlRegisterType<PushRuleModel>("org.kde.neochat", 1, 0, "PushRuleModel");
|
||||||
qmlRegisterType<StickerModel>("org.kde.neochat", 1, 0, "StickerModel");
|
qmlRegisterType<StickerModel>("org.kde.neochat", 1, 0, "StickerModel");
|
||||||
qmlRegisterType<ImagePacksModel>("org.kde.neochat", 1, 0, "ImagePacksModel");
|
qmlRegisterType<ImagePacksModel>("org.kde.neochat", 1, 0, "ImagePacksModel");
|
||||||
qmlRegisterType<AccountEmoticonModel>("org.kde.neochat", 1, 0, "AccountEmoticonModel");
|
qmlRegisterType<AccountEmoticonModel>("org.kde.neochat", 1, 0, "AccountEmoticonModel");
|
||||||
qmlRegisterType<EmoticonFilterModel>("org.kde.neochat", 1, 0, "EmoticonFilterModel");
|
qmlRegisterType<EmoticonFilterModel>("org.kde.neochat", 1, 0, "EmoticonFilterModel");
|
||||||
qmlRegisterType<DelegateSizeHelper>("org.kde.neochat", 1, 0, "DelegateSizeHelper");
|
qmlRegisterType<DelegateSizeHelper>("org.kde.neochat", 1, 0, "DelegateSizeHelper");
|
||||||
qmlRegisterUncreatableType<RoomMessageEvent>("org.kde.neochat", 1, 0, "RoomMessageEvent", "ENUM");
|
qmlRegisterUncreatableType<RoomMessageEvent>("org.kde.neochat", 1, 0, "RoomMessageEvent", "ENUM");
|
||||||
|
qmlRegisterUncreatableType<PushNotificationKind>("org.kde.neochat", 1, 0, "PushNotificationKind", "ENUM");
|
||||||
|
qmlRegisterUncreatableType<PushNotificationSection>("org.kde.neochat", 1, 0, "PushNotificationSection", "ENUM");
|
||||||
qmlRegisterUncreatableType<PushNotificationState>("org.kde.neochat", 1, 0, "PushNotificationState", "ENUM");
|
qmlRegisterUncreatableType<PushNotificationState>("org.kde.neochat", 1, 0, "PushNotificationState", "ENUM");
|
||||||
qmlRegisterUncreatableType<PushNotificationAction>("org.kde.neochat", 1, 0, "PushNotificationAction", "ENUM");
|
qmlRegisterUncreatableType<PushNotificationAction>("org.kde.neochat", 1, 0, "PushNotificationAction", "ENUM");
|
||||||
qmlRegisterUncreatableType<NeoChatRoomType>("org.kde.neochat", 1, 0, "NeoChatRoomType", "ENUM");
|
qmlRegisterUncreatableType<NeoChatRoomType>("org.kde.neochat", 1, 0, "NeoChatRoomType", "ENUM");
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
// 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
|
|
||||||
|
|
||||||
#include "keywordnotificationrulemodel.h"
|
|
||||||
#include "controller.h"
|
|
||||||
#include "notificationsmanager.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <connection.h>
|
|
||||||
#include <converters.h>
|
|
||||||
#include <csapi/definitions/push_ruleset.h>
|
|
||||||
#include <csapi/pushrules.h>
|
|
||||||
#include <jobs/basejob.h>
|
|
||||||
|
|
||||||
KeywordNotificationRuleModel::KeywordNotificationRuleModel(QObject *parent)
|
|
||||||
: QAbstractListModel(parent)
|
|
||||||
{
|
|
||||||
if (Controller::instance().activeConnection()) {
|
|
||||||
controllerConnectionChanged();
|
|
||||||
}
|
|
||||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, &KeywordNotificationRuleModel::controllerConnectionChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeywordNotificationRuleModel::controllerConnectionChanged()
|
|
||||||
{
|
|
||||||
connect(Controller::instance().activeConnection(), &Quotient::Connection::accountDataChanged, this, &KeywordNotificationRuleModel::updateNotificationRules);
|
|
||||||
updateNotificationRules("m.push_rules");
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeywordNotificationRuleModel::updateNotificationRules(const QString &type)
|
|
||||||
{
|
|
||||||
if (type != "m.push_rules") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QJsonObject ruleDataJson = Controller::instance().activeConnection()->accountDataJson("m.push_rules");
|
|
||||||
const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson["global"].toObject());
|
|
||||||
const QVector<Quotient::PushRule> contentRules = ruleData.content;
|
|
||||||
|
|
||||||
beginResetModel();
|
|
||||||
m_notificationRules.clear();
|
|
||||||
for (const auto &i : contentRules) {
|
|
||||||
if (!m_notificationRules.contains(i.ruleId) && i.ruleId[0] != '.') {
|
|
||||||
m_notificationRules.append(i.ruleId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
endResetModel();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant KeywordNotificationRuleModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (!index.isValid()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index.row() >= m_notificationRules.count()) {
|
|
||||||
qDebug() << "KeywordNotificationRuleModel, something's wrong: index.row() >= m_notificationRules.count()";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (role == NameRole) {
|
|
||||||
return m_notificationRules.at(index.row());
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int KeywordNotificationRuleModel::rowCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(parent)
|
|
||||||
|
|
||||||
return m_notificationRules.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeywordNotificationRuleModel::addKeyword(const QString &keyword)
|
|
||||||
{
|
|
||||||
if (m_notificationRules.count() == 0) {
|
|
||||||
NotificationsManager::instance().initializeKeywordNotificationAction();
|
|
||||||
}
|
|
||||||
|
|
||||||
const QVector<QVariant> actions = NotificationsManager::instance().getKeywordNotificationActions();
|
|
||||||
|
|
||||||
auto job = Controller::instance()
|
|
||||||
.activeConnection()
|
|
||||||
->callApi<Quotient::SetPushRuleJob>("global", "content", keyword, actions, "", "", QVector<Quotient::PushCondition>(), keyword);
|
|
||||||
connect(job, &Quotient::BaseJob::success, this, [this, keyword]() {
|
|
||||||
beginInsertRows(QModelIndex(), m_notificationRules.count(), m_notificationRules.count());
|
|
||||||
m_notificationRules.append(keyword);
|
|
||||||
endInsertRows();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void KeywordNotificationRuleModel::removeKeywordAtIndex(int index)
|
|
||||||
{
|
|
||||||
auto job = Controller::instance().activeConnection()->callApi<Quotient::DeletePushRuleJob>("global", "content", m_notificationRules[index]);
|
|
||||||
connect(job, &Quotient::BaseJob::success, this, [this, index]() {
|
|
||||||
beginRemoveRows(QModelIndex(), index, index);
|
|
||||||
m_notificationRules.removeAt(index);
|
|
||||||
endRemoveRows();
|
|
||||||
|
|
||||||
if (m_notificationRules.count() == 0) {
|
|
||||||
NotificationsManager::instance().deactivateKeywordNotificationAction();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> KeywordNotificationRuleModel::roleNames() const
|
|
||||||
{
|
|
||||||
return {{NameRole, QByteArrayLiteral("name")}};
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
// 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
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <csapi/definitions/push_rule.h>
|
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class KeywordNotificationRuleModel
|
|
||||||
*
|
|
||||||
* This class defines the model for managing notification push rule keywords.
|
|
||||||
*/
|
|
||||||
class KeywordNotificationRuleModel : public QAbstractListModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Defines the model roles.
|
|
||||||
*/
|
|
||||||
enum EventRoles {
|
|
||||||
NameRole = Qt::DisplayRole, /**< The push rule keyword. */
|
|
||||||
};
|
|
||||||
|
|
||||||
KeywordNotificationRuleModel(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the given role value at the given index.
|
|
||||||
*
|
|
||||||
* @sa QAbstractItemModel::data
|
|
||||||
*/
|
|
||||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Number of rows in the model.
|
|
||||||
*
|
|
||||||
* @sa QAbstractItemModel::rowCount
|
|
||||||
*/
|
|
||||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Returns a mapping from Role enum values to role names.
|
|
||||||
*
|
|
||||||
* @sa EventRoles, QAbstractItemModel::roleNames()
|
|
||||||
*/
|
|
||||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Add a new keyword to the model.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void addKeyword(const QString &keyword);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Remove a keyword from the model.
|
|
||||||
*/
|
|
||||||
Q_INVOKABLE void removeKeywordAtIndex(int index);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void controllerConnectionChanged();
|
|
||||||
void updateNotificationRules(const QString &type);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QList<QString> m_notificationRules;
|
|
||||||
};
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include "livelocationsmodel.h"
|
#include "livelocationsmodel.h"
|
||||||
|
|
||||||
#include <Quotient/events/roommessageevent.h>
|
#include <events/roommessageevent.h>
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
|
|||||||
445
src/models/pushrulemodel.cpp
Normal file
445
src/models/pushrulemodel.cpp
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
#include "pushrulemodel.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <connection.h>
|
||||||
|
#include <converters.h>
|
||||||
|
#include <csapi/definitions/push_ruleset.h>
|
||||||
|
#include <csapi/pushrules.h>
|
||||||
|
#include <jobs/basejob.h>
|
||||||
|
#include <qobjectdefs.h>
|
||||||
|
|
||||||
|
#include "controller.h"
|
||||||
|
#include "neochatconfig.h"
|
||||||
|
#include "notificationsmanager.h"
|
||||||
|
|
||||||
|
// Alternate name text for default rules.
|
||||||
|
static const QHash<QString, QString> defaultRuleNames = {
|
||||||
|
{QStringLiteral(".m.rule.master"), QStringLiteral("Enable notifications for this account")},
|
||||||
|
{QStringLiteral(".m.rule.room_one_to_one"), QStringLiteral("Messages in one-to-one chats")},
|
||||||
|
{QStringLiteral(".m.rule.encrypted_room_one_to_one"), QStringLiteral("Encrypted messages in one-to-one chats")},
|
||||||
|
{QStringLiteral(".m.rule.message"), QStringLiteral("Messages in group chats")},
|
||||||
|
{QStringLiteral(".m.rule.encrypted"), QStringLiteral("Messages in encrypted group chats")},
|
||||||
|
{QStringLiteral(".m.rule.tombstone"), QStringLiteral("Room upgrade messages")},
|
||||||
|
{QStringLiteral(".m.rule.contains_display_name"), QStringLiteral("Messages containing my display name")},
|
||||||
|
{QStringLiteral(".m.rule.roomnotif"), QStringLiteral("Whole room (@room) notifications")},
|
||||||
|
{QStringLiteral(".m.rule.invite_for_me"), QStringLiteral("Invites to a room")},
|
||||||
|
{QStringLiteral(".m.rule.call"), QStringLiteral("Call invitation")},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sections for default rules.
|
||||||
|
static const QHash<QString, PushNotificationSection::Section> defaultSections = {
|
||||||
|
{QStringLiteral(".m.rule.master"), PushNotificationSection::Master},
|
||||||
|
{QStringLiteral(".m.rule.room_one_to_one"), PushNotificationSection::Room},
|
||||||
|
{QStringLiteral(".m.rule.encrypted_room_one_to_one"), PushNotificationSection::Room},
|
||||||
|
{QStringLiteral(".m.rule.message"), PushNotificationSection::Room},
|
||||||
|
{QStringLiteral(".m.rule.encrypted"), PushNotificationSection::Room},
|
||||||
|
{QStringLiteral(".m.rule.tombstone"), PushNotificationSection::Room},
|
||||||
|
{QStringLiteral(".m.rule.contains_display_name"), PushNotificationSection::Mentions},
|
||||||
|
{QStringLiteral(".m.rule.roomnotif"), PushNotificationSection::Mentions},
|
||||||
|
{QStringLiteral(".m.rule.invite_for_me"), PushNotificationSection::Invites},
|
||||||
|
{QStringLiteral(".m.rule.call"), PushNotificationSection::Undefined}, // TODO: make invites when VOIP added.
|
||||||
|
{QStringLiteral(".m.rule.suppress_notices"), PushNotificationSection::Undefined},
|
||||||
|
{QStringLiteral(".m.rule.member_event"), PushNotificationSection::Undefined},
|
||||||
|
{QStringLiteral(".m.rule.reaction"), PushNotificationSection::Undefined},
|
||||||
|
{QStringLiteral(".m.rule.room.server_acl"), PushNotificationSection::Undefined},
|
||||||
|
{QStringLiteral(".im.vector.jitsi"), PushNotificationSection::Undefined},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default rules that don't have a highlight option as it would lead to all messages
|
||||||
|
// in a room being highlighted.
|
||||||
|
static const QStringList noHighlight = {
|
||||||
|
QStringLiteral(".m.rule.room_one_to_one"),
|
||||||
|
QStringLiteral(".m.rule.encrypted_room_one_to_one"),
|
||||||
|
QStringLiteral(".m.rule.message"),
|
||||||
|
QStringLiteral(".m.rule.encrypted"),
|
||||||
|
};
|
||||||
|
|
||||||
|
PushRuleModel::PushRuleModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
m_defaultKeywordAction = static_cast<PushNotificationAction::Action>(NeoChatConfig::self()->keywordPushRuleDefault());
|
||||||
|
|
||||||
|
if (Controller::instance().activeConnection()) {
|
||||||
|
controllerConnectionChanged();
|
||||||
|
}
|
||||||
|
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, &PushRuleModel::controllerConnectionChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushRuleModel::controllerConnectionChanged()
|
||||||
|
{
|
||||||
|
connect(Controller::instance().activeConnection(), &Quotient::Connection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
|
||||||
|
updateNotificationRules("m.push_rules");
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushRuleModel::updateNotificationRules(const QString &type)
|
||||||
|
{
|
||||||
|
if (type != "m.push_rules") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QJsonObject ruleDataJson = Controller::instance().activeConnection()->accountDataJson("m.push_rules");
|
||||||
|
const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson["global"].toObject());
|
||||||
|
|
||||||
|
beginResetModel();
|
||||||
|
m_rules.clear();
|
||||||
|
|
||||||
|
// Doing this 5 times because PushRuleset is a struct.
|
||||||
|
setRules(ruleData.override, PushNotificationKind::Override);
|
||||||
|
setRules(ruleData.content, PushNotificationKind::Content);
|
||||||
|
setRules(ruleData.room, PushNotificationKind::Room);
|
||||||
|
setRules(ruleData.sender, PushNotificationKind::Sender);
|
||||||
|
setRules(ruleData.underride, PushNotificationKind::Underride);
|
||||||
|
|
||||||
|
Q_EMIT globalNotificationsEnabledChanged();
|
||||||
|
Q_EMIT globalNotificationsSetChanged();
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushRuleModel::setRules(QVector<Quotient::PushRule> rules, PushNotificationKind::Kind kind)
|
||||||
|
{
|
||||||
|
for (const auto &rule : rules) {
|
||||||
|
QString roomId;
|
||||||
|
if (rule.conditions.size() > 0) {
|
||||||
|
for (const auto &condition : rule.conditions) {
|
||||||
|
if (condition.key == QStringLiteral("room_id")) {
|
||||||
|
roomId = condition.pattern;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rules.append(Rule{
|
||||||
|
rule.ruleId,
|
||||||
|
kind,
|
||||||
|
variantToAction(rule.actions, rule.enabled),
|
||||||
|
getSection(rule),
|
||||||
|
rule.enabled,
|
||||||
|
roomId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PushRuleModel::getRuleIndex(const QString &ruleId) const
|
||||||
|
{
|
||||||
|
for (auto i = 0; i < m_rules.count(); i++) {
|
||||||
|
if (m_rules[i].id == ruleId) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PushNotificationSection::Section PushRuleModel::getSection(Quotient::PushRule rule)
|
||||||
|
{
|
||||||
|
auto ruleId = rule.ruleId;
|
||||||
|
|
||||||
|
if (defaultSections.contains(ruleId)) {
|
||||||
|
return defaultSections.value(ruleId);
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* If the rule name resolves to a matrix id for a room that the user is part
|
||||||
|
* of it shouldn't appear in the global list as it's overriding the global
|
||||||
|
* state for that room.
|
||||||
|
*
|
||||||
|
* Rooms that the user hasn't joined shouldn't have a rule.
|
||||||
|
*/
|
||||||
|
auto connection = Controller::instance().activeConnection();
|
||||||
|
if (connection->room(ruleId) != nullptr) {
|
||||||
|
return PushNotificationSection::Undefined;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* If the rule name resolves to a matrix id for a user it shouldn't appear
|
||||||
|
* in the global list as it's a rule to block notifications from a user and
|
||||||
|
* is handled elsewhere.
|
||||||
|
*/
|
||||||
|
auto testUserId = ruleId;
|
||||||
|
// Rules for user matrix IDs often don't have the @ on the beginning so add
|
||||||
|
// if not there to avoid malformed ID.
|
||||||
|
if (!testUserId.startsWith(u'@')) {
|
||||||
|
testUserId.prepend(u'@');
|
||||||
|
}
|
||||||
|
if (connection->user(testUserId) != nullptr) {
|
||||||
|
return PushNotificationSection::Undefined;
|
||||||
|
}
|
||||||
|
// If the rule has push conditions and one is a room ID it is a room only keyword.
|
||||||
|
if (!rule.conditions.isEmpty()) {
|
||||||
|
for (auto condition : rule.conditions) {
|
||||||
|
if (condition.key == QStringLiteral("room_id")) {
|
||||||
|
return PushNotificationSection::RoomKeywords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PushNotificationSection::Keywords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PushNotificationAction::Action PushRuleModel::defaultState() const
|
||||||
|
{
|
||||||
|
return m_defaultKeywordAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushRuleModel::setDefaultState(PushNotificationAction::Action defaultState)
|
||||||
|
{
|
||||||
|
if (defaultState == m_defaultKeywordAction) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_defaultKeywordAction = defaultState;
|
||||||
|
NeoChatConfig::setKeywordPushRuleDefault(m_defaultKeywordAction);
|
||||||
|
Q_EMIT defaultStateChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PushRuleModel::globalNotificationsEnabled() const
|
||||||
|
{
|
||||||
|
auto masterIndex = getRuleIndex(QStringLiteral(".m.rule.master"));
|
||||||
|
if (masterIndex > -1) {
|
||||||
|
return !m_rules[masterIndex].enabled;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushRuleModel::setGlobalNotificationsEnabled(bool enabled)
|
||||||
|
{
|
||||||
|
setNotificationRuleEnabled("override", ".m.rule.master", !enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PushRuleModel::globalNotificationsSet() const
|
||||||
|
{
|
||||||
|
return getRuleIndex(QStringLiteral(".m.rule.master")) > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant PushRuleModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.row() >= rowCount()) {
|
||||||
|
qDebug() << "PushRuleModel, something's wrong: index.row() >= m_rules.count()";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == NameRole) {
|
||||||
|
auto ruleId = m_rules.at(index.row()).id;
|
||||||
|
if (defaultRuleNames.contains(ruleId)) {
|
||||||
|
return defaultRuleNames.value(ruleId);
|
||||||
|
} else {
|
||||||
|
return ruleId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (role == IdRole) {
|
||||||
|
return m_rules.at(index.row()).id;
|
||||||
|
}
|
||||||
|
if (role == KindRole) {
|
||||||
|
return m_rules.at(index.row()).kind;
|
||||||
|
}
|
||||||
|
if (role == ActionRole) {
|
||||||
|
return m_rules.at(index.row()).action;
|
||||||
|
}
|
||||||
|
if (role == HighlightableRole) {
|
||||||
|
return !noHighlight.contains(m_rules.at(index.row()).id);
|
||||||
|
}
|
||||||
|
if (role == DeletableRole) {
|
||||||
|
return !m_rules.at(index.row()).id.startsWith(QStringLiteral("."));
|
||||||
|
}
|
||||||
|
if (role == SectionRole) {
|
||||||
|
return m_rules.at(index.row()).section;
|
||||||
|
}
|
||||||
|
if (role == RoomIdRole) {
|
||||||
|
return m_rules.at(index.row()).roomId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int PushRuleModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent)
|
||||||
|
|
||||||
|
return m_rules.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> PushRuleModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
|
||||||
|
roles[NameRole] = "name";
|
||||||
|
roles[IdRole] = "id";
|
||||||
|
roles[KindRole] = "kind";
|
||||||
|
roles[ActionRole] = "ruleAction";
|
||||||
|
roles[HighlightableRole] = "highlightable";
|
||||||
|
roles[DeletableRole] = "deletable";
|
||||||
|
roles[SectionRole] = "section";
|
||||||
|
roles[RoomIdRole] = "roomId";
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushRuleModel::setPushRuleAction(const QString &id, PushNotificationAction::Action action)
|
||||||
|
{
|
||||||
|
int index = getRuleIndex(id);
|
||||||
|
if (index == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rule = m_rules[index];
|
||||||
|
|
||||||
|
// Override rules need to be disabled when off so that other rules can match the message if they apply.
|
||||||
|
if (action == PushNotificationAction::Off && rule.kind == PushNotificationKind::Override) {
|
||||||
|
setNotificationRuleEnabled(PushNotificationKind::kindString(rule.kind), rule.id, false);
|
||||||
|
} else if (rule.kind == PushNotificationKind::Override) {
|
||||||
|
setNotificationRuleEnabled(PushNotificationKind::kindString(rule.kind), rule.id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
setNotificationRuleActions(PushNotificationKind::kindString(rule.kind), rule.id, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushRuleModel::addKeyword(const QString &keyword, const QString &roomId)
|
||||||
|
{
|
||||||
|
PushNotificationKind::Kind kind = PushNotificationKind::Content;
|
||||||
|
const QVector<QVariant> actions = actionToVariant(m_defaultKeywordAction);
|
||||||
|
QVector<Quotient::PushCondition> pushConditions;
|
||||||
|
if (!roomId.isEmpty()) {
|
||||||
|
kind = PushNotificationKind::Override;
|
||||||
|
|
||||||
|
Quotient::PushCondition roomCondition;
|
||||||
|
roomCondition.kind = "event_match";
|
||||||
|
roomCondition.key = "room_id";
|
||||||
|
roomCondition.pattern = roomId;
|
||||||
|
pushConditions.append(roomCondition);
|
||||||
|
|
||||||
|
Quotient::PushCondition keywordCondition;
|
||||||
|
keywordCondition.kind = "event_match";
|
||||||
|
keywordCondition.key = "content.body";
|
||||||
|
keywordCondition.pattern = keyword;
|
||||||
|
pushConditions.append(keywordCondition);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto job = Controller::instance().activeConnection()->callApi<Quotient::SetPushRuleJob>("global",
|
||||||
|
PushNotificationKind::kindString(kind),
|
||||||
|
keyword,
|
||||||
|
actions,
|
||||||
|
QLatin1String(""),
|
||||||
|
QLatin1String(""),
|
||||||
|
pushConditions,
|
||||||
|
roomId.isEmpty() ? keyword : QLatin1String(""));
|
||||||
|
connect(job, &Quotient::BaseJob::failure, this, [job, keyword]() {
|
||||||
|
qWarning() << QLatin1String("Unable to set push rule for keyword %1: ").arg(keyword) << job->errorString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The rule never being removed from the list by this function is intentional. When
|
||||||
|
* the server is updated the new push rule account data will be synced and it will
|
||||||
|
* be removed when the model is updated then.
|
||||||
|
*/
|
||||||
|
void PushRuleModel::removeKeyword(const QString &keyword)
|
||||||
|
{
|
||||||
|
int index = getRuleIndex(keyword);
|
||||||
|
if (index == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto kind = PushNotificationKind::kindString(m_rules[index].kind);
|
||||||
|
auto job = Controller::instance().activeConnection()->callApi<Quotient::DeletePushRuleJob>("global", kind, m_rules[index].id);
|
||||||
|
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
|
||||||
|
qWarning() << QLatin1String("Unable to remove push rule for keyword %1: ").arg(m_rules[index].id) << job->errorString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
|
||||||
|
{
|
||||||
|
auto job = Controller::instance().activeConnection()->callApi<Quotient::IsPushRuleEnabledJob>("global", kind, ruleId);
|
||||||
|
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled]() {
|
||||||
|
if (job->enabled() != enabled) {
|
||||||
|
Controller::instance().activeConnection()->callApi<Quotient::SetPushRuleEnabledJob>("global", kind, ruleId, enabled);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushRuleModel::setNotificationRuleActions(const QString &kind, const QString &ruleId, PushNotificationAction::Action action)
|
||||||
|
{
|
||||||
|
QVector<QVariant> actions;
|
||||||
|
if (ruleId == ".m.rule.call") {
|
||||||
|
actions = actionToVariant(action, "ring");
|
||||||
|
} else {
|
||||||
|
actions = actionToVariant(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
Controller::instance().activeConnection()->callApi<Quotient::SetPushRuleActionsJob>("global", kind, ruleId, actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
PushNotificationAction::Action PushRuleModel::variantToAction(const QVector<QVariant> &actions, bool enabled)
|
||||||
|
{
|
||||||
|
bool notify = false;
|
||||||
|
bool isNoisy = false;
|
||||||
|
bool highlightEnabled = false;
|
||||||
|
for (const auto &i : actions) {
|
||||||
|
auto actionString = i.toString();
|
||||||
|
if (!actionString.isEmpty()) {
|
||||||
|
if (actionString == QLatin1String("notify")) {
|
||||||
|
notify = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject action = i.toJsonObject();
|
||||||
|
if (action["set_tweak"].toString() == "sound") {
|
||||||
|
isNoisy = true;
|
||||||
|
} else if (action["set_tweak"].toString() == "highlight") {
|
||||||
|
if (action["value"].toString() != "false") {
|
||||||
|
highlightEnabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
return PushNotificationAction::Off;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notify) {
|
||||||
|
if (isNoisy && highlightEnabled) {
|
||||||
|
return PushNotificationAction::NoisyHighlight;
|
||||||
|
} else if (isNoisy) {
|
||||||
|
return PushNotificationAction::Noisy;
|
||||||
|
} else if (highlightEnabled) {
|
||||||
|
return PushNotificationAction::Highlight;
|
||||||
|
} else {
|
||||||
|
return PushNotificationAction::On;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return PushNotificationAction::Off;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<QVariant> PushRuleModel::actionToVariant(PushNotificationAction::Action action, const QString &sound)
|
||||||
|
{
|
||||||
|
// The caller should never try to set the state to unknown.
|
||||||
|
// It exists only as a default state to diable the settings options until the actual state is retrieved from the server.
|
||||||
|
if (action == PushNotificationAction::Unknown) {
|
||||||
|
Q_ASSERT(false);
|
||||||
|
return QVector<QVariant>();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<QVariant> actions;
|
||||||
|
|
||||||
|
if (action != PushNotificationAction::Off) {
|
||||||
|
actions.append("notify");
|
||||||
|
} else {
|
||||||
|
actions.append("dont_notify");
|
||||||
|
}
|
||||||
|
if (action == PushNotificationAction::Noisy || action == PushNotificationAction::NoisyHighlight) {
|
||||||
|
QJsonObject soundTweak;
|
||||||
|
soundTweak.insert("set_tweak", "sound");
|
||||||
|
soundTweak.insert("value", sound);
|
||||||
|
actions.append(soundTweak);
|
||||||
|
}
|
||||||
|
if (action == PushNotificationAction::Highlight || action == PushNotificationAction::NoisyHighlight) {
|
||||||
|
QJsonObject highlightTweak;
|
||||||
|
highlightTweak.insert("set_tweak", "highlight");
|
||||||
|
actions.append(highlightTweak);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
241
src/models/pushrulemodel.h
Normal file
241
src/models/pushrulemodel.h
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
|
#include <csapi/definitions/push_rule.h>
|
||||||
|
|
||||||
|
#include "notificationsmanager.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PushNotificationKind
|
||||||
|
*
|
||||||
|
* A class with the Kind enum for push notifications and helper functions.
|
||||||
|
*
|
||||||
|
* The kind relates to the kinds of push rule definied in the matrix spec, see
|
||||||
|
* https://spec.matrix.org/v1.7/client-server-api/#push-rules for full details.
|
||||||
|
*/
|
||||||
|
class PushNotificationKind : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Defines the different kinds of push rule.
|
||||||
|
*/
|
||||||
|
enum Kind {
|
||||||
|
Override = 0, /**< The highest priority rules. */
|
||||||
|
Content, /**< These configure behaviour for messages that match certain patterns. */
|
||||||
|
Room, /**< These rules change the behaviour of all messages for a given room. */
|
||||||
|
Sender, /**< These rules configure notification behaviour for messages from a specific Matrix user ID. */
|
||||||
|
Underride, /**< These are identical to override rules, but have a lower priority than content, room and sender rules. */
|
||||||
|
};
|
||||||
|
Q_ENUM(Kind);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Translate the Kind enum value to a human readable string.
|
||||||
|
*
|
||||||
|
* @sa Kind
|
||||||
|
*/
|
||||||
|
static QString kindString(Kind kind)
|
||||||
|
{
|
||||||
|
switch (kind) {
|
||||||
|
case Kind::Override:
|
||||||
|
return QLatin1String("override");
|
||||||
|
case Kind::Content:
|
||||||
|
return QLatin1String("content");
|
||||||
|
case Kind::Room:
|
||||||
|
return QLatin1String("room");
|
||||||
|
case Kind::Sender:
|
||||||
|
return QLatin1String("sender");
|
||||||
|
case Kind::Underride:
|
||||||
|
return QLatin1String("underride");
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PushNotificationSection
|
||||||
|
*
|
||||||
|
* A class with the Section enum for push notifications and helper functions.
|
||||||
|
*
|
||||||
|
* @note This is different from the PushNotificationKind and instead is used for sorting
|
||||||
|
* in the settings page which is not necessarily by Kind.
|
||||||
|
*
|
||||||
|
* @sa PushNotificationKind
|
||||||
|
*/
|
||||||
|
class PushNotificationSection : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Defines the sections to sort push rules into.
|
||||||
|
*/
|
||||||
|
enum Section {
|
||||||
|
Master = 0, /**< The master push rule */
|
||||||
|
Room, /**< Push rules relating to all rooms. */
|
||||||
|
Mentions, /**< Push rules relating to user mentions. */
|
||||||
|
Keywords, /**< Global Keyword push rules. */
|
||||||
|
RoomKeywords, /**< Keyword push rules that only apply to a specific room. */
|
||||||
|
Invites, /**< Push rules relating to invites. */
|
||||||
|
/**
|
||||||
|
* @brief Push rules that should never be shown.
|
||||||
|
*
|
||||||
|
* There are numerous rules that get set that shouldn't be shown in the general
|
||||||
|
* list e.g. The array of rules used to override global settings in individual
|
||||||
|
* rooms.
|
||||||
|
*/
|
||||||
|
Undefined,
|
||||||
|
};
|
||||||
|
Q_ENUM(Section);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Translate the Section enum value to a human readable string.
|
||||||
|
*
|
||||||
|
* @sa Section
|
||||||
|
*/
|
||||||
|
static QString sectionString(Section section)
|
||||||
|
{
|
||||||
|
switch (section) {
|
||||||
|
case Section::Master:
|
||||||
|
return QLatin1String("Master");
|
||||||
|
case Section::Room:
|
||||||
|
return QLatin1String("Room Notifications");
|
||||||
|
case Section::Mentions:
|
||||||
|
return QLatin1String("@Mentions");
|
||||||
|
case Section::Keywords:
|
||||||
|
return QLatin1String("Keywords");
|
||||||
|
case Section::Invites:
|
||||||
|
return QLatin1String("Invites");
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class PushRuleModel
|
||||||
|
*
|
||||||
|
* This class defines the model for managing notification push rule keywords.
|
||||||
|
*/
|
||||||
|
class PushRuleModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The default state for any newly created keyword rule.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(PushNotificationAction::Action defaultState READ defaultState WRITE setDefaultState NOTIFY defaultStateChanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The global notification state.
|
||||||
|
*
|
||||||
|
* If this rule is set to off all push notifications are disabled regardless
|
||||||
|
* of other settings.
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool globalNotificationsEnabled READ globalNotificationsEnabled WRITE setGlobalNotificationsEnabled NOTIFY globalNotificationsEnabledChanged)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether the global notification state has been retrieved from the server.
|
||||||
|
*
|
||||||
|
* @sa globalNotificationsEnabled, PushNotificationAction::Action
|
||||||
|
*/
|
||||||
|
Q_PROPERTY(bool globalNotificationsSet READ globalNotificationsSet NOTIFY globalNotificationsSetChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct Rule {
|
||||||
|
QString id;
|
||||||
|
PushNotificationKind::Kind kind;
|
||||||
|
PushNotificationAction::Action action;
|
||||||
|
PushNotificationSection::Section section;
|
||||||
|
bool enabled;
|
||||||
|
QString roomId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Defines the model roles.
|
||||||
|
*/
|
||||||
|
enum EventRoles {
|
||||||
|
NameRole = Qt::DisplayRole, /**< The push rule name. */
|
||||||
|
IdRole, /**< The push rule ID. */
|
||||||
|
KindRole, /**< The kind of notification rule; override, content, etc. */
|
||||||
|
ActionRole, /**< The PushNotificationAction for the rule. */
|
||||||
|
HighlightableRole, /**< Whether the rule can have a highlight action. */
|
||||||
|
DeletableRole, /**< Whether the rule can be deleted the rule. */
|
||||||
|
SectionRole, /**< The section to sort into in the settings page. */
|
||||||
|
RoomIdRole, /**< The room the rule applies to (blank if global). */
|
||||||
|
};
|
||||||
|
Q_ENUM(EventRoles)
|
||||||
|
|
||||||
|
PushRuleModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
[[nodiscard]] PushNotificationAction::Action defaultState() const;
|
||||||
|
void setDefaultState(PushNotificationAction::Action defaultState);
|
||||||
|
|
||||||
|
[[nodiscard]] bool globalNotificationsEnabled() const;
|
||||||
|
void setGlobalNotificationsEnabled(bool enabled);
|
||||||
|
|
||||||
|
[[nodiscard]] bool globalNotificationsSet() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the given role value at the given index.
|
||||||
|
*
|
||||||
|
* @sa QAbstractItemModel::data
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Number of rows in the model.
|
||||||
|
*
|
||||||
|
* @sa QAbstractItemModel::rowCount
|
||||||
|
*/
|
||||||
|
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a mapping from Role enum values to role names.
|
||||||
|
*
|
||||||
|
* @sa EventRoles, QAbstractItemModel::roleNames()
|
||||||
|
*/
|
||||||
|
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
Q_INVOKABLE void setPushRuleAction(const QString &id, PushNotificationAction::Action action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add a new keyword to the model.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void addKeyword(const QString &keyword, const QString &roomId = {});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a keyword from the model.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void removeKeyword(const QString &keyword);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void defaultStateChanged();
|
||||||
|
void globalNotificationsEnabledChanged();
|
||||||
|
void globalNotificationsSetChanged();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void controllerConnectionChanged();
|
||||||
|
void updateNotificationRules(const QString &type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PushNotificationAction::Action m_defaultKeywordAction;
|
||||||
|
QList<Rule> m_rules;
|
||||||
|
|
||||||
|
void setRules(QVector<Quotient::PushRule> rules, PushNotificationKind::Kind kind);
|
||||||
|
|
||||||
|
int getRuleIndex(const QString &ruleId) const;
|
||||||
|
PushNotificationSection::Section getSection(Quotient::PushRule rule);
|
||||||
|
|
||||||
|
void setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled);
|
||||||
|
void setNotificationRuleActions(const QString &kind, const QString &ruleId, PushNotificationAction::Action action);
|
||||||
|
PushNotificationAction::Action variantToAction(const QVector<QVariant> &actions, bool enabled);
|
||||||
|
QVector<QVariant> actionToVariant(PushNotificationAction::Action action, const QString &sound = "default");
|
||||||
|
};
|
||||||
|
Q_DECLARE_METATYPE(PushRuleModel *)
|
||||||
@@ -54,6 +54,10 @@
|
|||||||
<entry name="LastSaveDirectory" type="String">
|
<entry name="LastSaveDirectory" type="String">
|
||||||
<label>Directory last used for saving a file</label>
|
<label>Directory last used for saving a file</label>
|
||||||
</entry>
|
</entry>
|
||||||
|
<entry name="KeywordPushRuleDefault" type="int">
|
||||||
|
<label>The default setting for a new keyword push rule.</label>
|
||||||
|
<default>4</default>
|
||||||
|
</entry>
|
||||||
</group>
|
</group>
|
||||||
<group name="Timeline">
|
<group name="Timeline">
|
||||||
<entry name="ShowAvatarInTimeline" type="bool">
|
<entry name="ShowAvatarInTimeline" type="bool">
|
||||||
|
|||||||
@@ -41,12 +41,6 @@ NotificationsManager &NotificationsManager::instance()
|
|||||||
NotificationsManager::NotificationsManager(QObject *parent)
|
NotificationsManager::NotificationsManager(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
// Can't connect the signal up until the active connection has been established by the controller
|
|
||||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, [this]() {
|
|
||||||
connect(Controller::instance().activeConnection(), &Connection::accountDataChanged, this, &NotificationsManager::updateNotificationRules);
|
|
||||||
// Ensure that the push rule states are retrieved after the connection is changed
|
|
||||||
updateNotificationRules("m.push_rules");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef QUOTIENT_07
|
#ifdef QUOTIENT_07
|
||||||
@@ -300,309 +294,3 @@ void NotificationsManager::clearInvitationNotification(const QString &roomId)
|
|||||||
m_invitations[roomId]->close();
|
m_invitations[roomId]->close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The master push rule sets all notifications to off when enabled
|
|
||||||
* see https://spec.matrix.org/v1.3/client-server-api/#default-override-rules
|
|
||||||
* therefore to enable push rules the master rule needs to be disabled and vice versa
|
|
||||||
*/
|
|
||||||
void NotificationsManager::setGlobalNotificationsEnabled(bool enabled)
|
|
||||||
{
|
|
||||||
setNotificationRuleEnabled("override", ".m.rule.master", !enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationsManager::setOneToOneNotificationAction(PushNotificationAction::Action action)
|
|
||||||
{
|
|
||||||
setNotificationRuleActions("underride", ".m.rule.room_one_to_one", action);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationsManager::setEncryptedOneToOneNotificationAction(PushNotificationAction::Action action)
|
|
||||||
{
|
|
||||||
setNotificationRuleActions("underride", ".m.rule.encrypted_room_one_to_one", action);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationsManager::setGroupChatNotificationAction(PushNotificationAction::Action action)
|
|
||||||
{
|
|
||||||
setNotificationRuleActions("underride", ".m.rule.message", action);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationsManager::setEncryptedGroupChatNotificationAction(PushNotificationAction::Action action)
|
|
||||||
{
|
|
||||||
setNotificationRuleActions("underride", ".m.rule.encrypted", action);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* .m.rule.contains_display_name is an override rule so it needs to be disabled when off
|
|
||||||
* so that other rules can match the message if they apply.
|
|
||||||
*/
|
|
||||||
void NotificationsManager::setDisplayNameNotificationAction(PushNotificationAction::Action action)
|
|
||||||
{
|
|
||||||
if (action == PushNotificationAction::Off) {
|
|
||||||
setNotificationRuleEnabled("override", ".m.rule.contains_display_name", false);
|
|
||||||
} else {
|
|
||||||
setNotificationRuleActions("override", ".m.rule.contains_display_name", action);
|
|
||||||
setNotificationRuleEnabled("override", ".m.rule.contains_display_name", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* .m.rule.roomnotif is an override rule so it needs to be disabled when off
|
|
||||||
* so that other rules can match the message if they apply.
|
|
||||||
*/
|
|
||||||
void NotificationsManager::setRoomNotificationAction(PushNotificationAction::Action action)
|
|
||||||
{
|
|
||||||
if (action == PushNotificationAction::Off) {
|
|
||||||
setNotificationRuleEnabled("override", ".m.rule.roomnotif", false);
|
|
||||||
} else {
|
|
||||||
setNotificationRuleActions("override", ".m.rule.roomnotif", action);
|
|
||||||
setNotificationRuleEnabled("override", ".m.rule.roomnotif", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationsManager::initializeKeywordNotificationAction()
|
|
||||||
{
|
|
||||||
m_keywordNotificationAction = PushNotificationAction::Highlight;
|
|
||||||
Q_EMIT keywordNotificationActionChanged(m_keywordNotificationAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationsManager::deactivateKeywordNotificationAction()
|
|
||||||
{
|
|
||||||
m_keywordNotificationAction = PushNotificationAction::Off;
|
|
||||||
Q_EMIT keywordNotificationActionChanged(m_keywordNotificationAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<QVariant> NotificationsManager::getKeywordNotificationActions()
|
|
||||||
{
|
|
||||||
return toActions(m_keywordNotificationAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationsManager::setKeywordNotificationAction(PushNotificationAction::Action action)
|
|
||||||
{
|
|
||||||
// Unlike the other rules this needs to be set here for the case where there are no keyords.
|
|
||||||
m_keywordNotificationAction = action;
|
|
||||||
Q_EMIT keywordNotificationActionChanged(m_keywordNotificationAction);
|
|
||||||
|
|
||||||
const QJsonObject accountData = Controller::instance().activeConnection()->accountDataJson("m.push_rules");
|
|
||||||
const QJsonArray contentRuleArray = accountData["global"].toObject()["content"].toArray();
|
|
||||||
for (const auto &i : contentRuleArray) {
|
|
||||||
const QJsonObject contentRule = i.toObject();
|
|
||||||
if (contentRule["rule_id"].toString()[0] != '.') {
|
|
||||||
setNotificationRuleActions("content", contentRule["rule_id"].toString(), action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* .m.rule.invite_for_me is an override rule so it needs to be disabled when off
|
|
||||||
* so that other rules can match the message if they apply.
|
|
||||||
*/
|
|
||||||
void NotificationsManager::setInviteNotificationAction(PushNotificationAction::Action action)
|
|
||||||
{
|
|
||||||
if (action == PushNotificationAction::Off) {
|
|
||||||
setNotificationRuleEnabled("override", ".m.rule.invite_for_me", false);
|
|
||||||
} else {
|
|
||||||
setNotificationRuleActions("override", ".m.rule.invite_for_me", action);
|
|
||||||
setNotificationRuleEnabled("override", ".m.rule.invite_for_me", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationsManager::setCallInviteNotificationAction(PushNotificationAction::Action action)
|
|
||||||
{
|
|
||||||
setNotificationRuleActions("underride", ".m.rule.call", action);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* .m.rule.tombstone is an override rule so it needs to be disabled when off
|
|
||||||
* so that other rules can match the message if they apply.
|
|
||||||
*/
|
|
||||||
void NotificationsManager::setTombstoneNotificationAction(PushNotificationAction::Action action)
|
|
||||||
{
|
|
||||||
if (action == PushNotificationAction::Off) {
|
|
||||||
setNotificationRuleEnabled("override", ".m.rule.tombstone", false);
|
|
||||||
} else {
|
|
||||||
setNotificationRuleActions("override", ".m.rule.tombstone", action);
|
|
||||||
setNotificationRuleEnabled("override", ".m.rule.tombstone", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationsManager::updateNotificationRules(const QString &type)
|
|
||||||
{
|
|
||||||
if (type != "m.push_rules") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Controller::instance().activeConnection()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QJsonObject accountData = Controller::instance().activeConnection()->accountDataJson("m.push_rules");
|
|
||||||
|
|
||||||
// Update override rules
|
|
||||||
const QJsonArray overrideRuleArray = accountData["global"].toObject()["override"].toArray();
|
|
||||||
for (const auto &i : overrideRuleArray) {
|
|
||||||
const QJsonObject overrideRule = i.toObject();
|
|
||||||
if (overrideRule["rule_id"] == ".m.rule.master") {
|
|
||||||
bool ruleEnabled = overrideRule["enabled"].toBool();
|
|
||||||
m_globalNotificationsEnabled = !ruleEnabled;
|
|
||||||
if (!m_globalNotificationsSet) {
|
|
||||||
m_globalNotificationsSet = true;
|
|
||||||
}
|
|
||||||
Q_EMIT globalNotificationsEnabledChanged(m_globalNotificationsEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
const PushNotificationAction::Action action = toAction(overrideRule);
|
|
||||||
|
|
||||||
if (overrideRule["rule_id"] == ".m.rule.contains_display_name") {
|
|
||||||
m_displayNameNotificationAction = action;
|
|
||||||
Q_EMIT displayNameNotificationActionChanged(m_displayNameNotificationAction);
|
|
||||||
} else if (overrideRule["rule_id"] == ".m.rule.roomnotif") {
|
|
||||||
m_roomNotificationAction = action;
|
|
||||||
Q_EMIT roomNotificationActionChanged(m_roomNotificationAction);
|
|
||||||
} else if (overrideRule["rule_id"] == ".m.rule.invite_for_me") {
|
|
||||||
m_inviteNotificationAction = action;
|
|
||||||
Q_EMIT inviteNotificationActionChanged(m_inviteNotificationAction);
|
|
||||||
} else if (overrideRule["rule_id"] == ".m.rule.tombstone") {
|
|
||||||
m_tombstoneNotificationAction = action;
|
|
||||||
Q_EMIT tombstoneNotificationActionChanged(m_tombstoneNotificationAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update content rules
|
|
||||||
const QJsonArray contentRuleArray = accountData["global"].toObject()["content"].toArray();
|
|
||||||
PushNotificationAction::Action keywordAction = PushNotificationAction::Unknown;
|
|
||||||
for (const auto &i : contentRuleArray) {
|
|
||||||
const QJsonObject contentRule = i.toObject();
|
|
||||||
const PushNotificationAction::Action action = toAction(contentRule);
|
|
||||||
bool actionMismatch = false;
|
|
||||||
|
|
||||||
if (contentRule["rule_id"].toString()[0] != '.' && !actionMismatch) {
|
|
||||||
if (keywordAction == PushNotificationAction::Unknown) {
|
|
||||||
keywordAction = action;
|
|
||||||
m_keywordNotificationAction = action;
|
|
||||||
Q_EMIT keywordNotificationActionChanged(m_keywordNotificationAction);
|
|
||||||
} else if (action != keywordAction) {
|
|
||||||
actionMismatch = true;
|
|
||||||
m_keywordNotificationAction = PushNotificationAction::On;
|
|
||||||
Q_EMIT keywordNotificationActionChanged(m_keywordNotificationAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If there are no keywords set the state to off, this is the only time it'll be in the off state
|
|
||||||
if (keywordAction == PushNotificationAction::Unknown) {
|
|
||||||
m_keywordNotificationAction = PushNotificationAction::Off;
|
|
||||||
Q_EMIT keywordNotificationActionChanged(m_keywordNotificationAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update underride rules
|
|
||||||
const QJsonArray underrideRuleArray = accountData["global"].toObject()["underride"].toArray();
|
|
||||||
for (const auto &i : underrideRuleArray) {
|
|
||||||
const QJsonObject underrideRule = i.toObject();
|
|
||||||
const PushNotificationAction::Action action = toAction(underrideRule);
|
|
||||||
|
|
||||||
if (underrideRule["rule_id"] == ".m.rule.room_one_to_one") {
|
|
||||||
m_oneToOneNotificationAction = action;
|
|
||||||
Q_EMIT oneToOneNotificationActionChanged(m_oneToOneNotificationAction);
|
|
||||||
} else if (underrideRule["rule_id"] == ".m.rule.encrypted_room_one_to_one") {
|
|
||||||
m_encryptedOneToOneNotificationAction = action;
|
|
||||||
Q_EMIT encryptedOneToOneNotificationActionChanged(m_encryptedOneToOneNotificationAction);
|
|
||||||
} else if (underrideRule["rule_id"] == ".m.rule.message") {
|
|
||||||
m_groupChatNotificationAction = action;
|
|
||||||
Q_EMIT groupChatNotificationActionChanged(m_groupChatNotificationAction);
|
|
||||||
} else if (underrideRule["rule_id"] == ".m.rule.encrypted") {
|
|
||||||
m_encryptedGroupChatNotificationAction = action;
|
|
||||||
Q_EMIT encryptedGroupChatNotificationActionChanged(m_encryptedGroupChatNotificationAction);
|
|
||||||
} else if (underrideRule["rule_id"] == ".m.rule.call") {
|
|
||||||
m_callInviteNotificationAction = action;
|
|
||||||
Q_EMIT callInviteNotificationActionChanged(m_callInviteNotificationAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationsManager::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
|
|
||||||
{
|
|
||||||
auto job = Controller::instance().activeConnection()->callApi<IsPushRuleEnabledJob>("global", kind, ruleId);
|
|
||||||
connect(job, &BaseJob::success, this, [job, kind, ruleId, enabled]() {
|
|
||||||
if (job->enabled() != enabled) {
|
|
||||||
Controller::instance().activeConnection()->callApi<SetPushRuleEnabledJob>("global", kind, ruleId, enabled);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationsManager::setNotificationRuleActions(const QString &kind, const QString &ruleId, PushNotificationAction::Action action)
|
|
||||||
{
|
|
||||||
QVector<QVariant> actions;
|
|
||||||
if (ruleId == ".m.rule.call") {
|
|
||||||
actions = toActions(action, "ring");
|
|
||||||
} else {
|
|
||||||
actions = toActions(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
Controller::instance().activeConnection()->callApi<SetPushRuleActionsJob>("global", kind, ruleId, actions);
|
|
||||||
}
|
|
||||||
|
|
||||||
PushNotificationAction::Action NotificationsManager::toAction(const QJsonObject &rule)
|
|
||||||
{
|
|
||||||
const QJsonArray actions = rule["actions"].toArray();
|
|
||||||
bool isNoisy = false;
|
|
||||||
bool highlightEnabled = false;
|
|
||||||
const bool enabled = rule["enabled"].toBool();
|
|
||||||
for (const auto &i : actions) {
|
|
||||||
QJsonObject action = i.toObject();
|
|
||||||
if (action["set_tweak"].toString() == "sound") {
|
|
||||||
isNoisy = true;
|
|
||||||
} else if (action["set_tweak"].toString() == "highlight") {
|
|
||||||
if (action["value"].toString() != "false") {
|
|
||||||
highlightEnabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!enabled) {
|
|
||||||
return PushNotificationAction::Off;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actions[0] == "notify") {
|
|
||||||
if (isNoisy && highlightEnabled) {
|
|
||||||
return PushNotificationAction::NoisyHighlight;
|
|
||||||
} else if (isNoisy) {
|
|
||||||
return PushNotificationAction::Noisy;
|
|
||||||
} else if (highlightEnabled) {
|
|
||||||
return PushNotificationAction::Highlight;
|
|
||||||
} else {
|
|
||||||
return PushNotificationAction::On;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return PushNotificationAction::Off;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<QVariant> NotificationsManager::toActions(PushNotificationAction::Action action, const QString &sound)
|
|
||||||
{
|
|
||||||
// The caller should never try to set the state to unknown.
|
|
||||||
// It exists only as a default state to diable the settings options until the actual state is retrieved from the server.
|
|
||||||
if (action == PushNotificationAction::Unknown) {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return QVector<QVariant>();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<QVariant> actions;
|
|
||||||
|
|
||||||
if (action != PushNotificationAction::Off) {
|
|
||||||
actions.append("notify");
|
|
||||||
} else {
|
|
||||||
actions.append("dont_notify");
|
|
||||||
}
|
|
||||||
if (action == PushNotificationAction::Noisy || action == PushNotificationAction::NoisyHighlight) {
|
|
||||||
QJsonObject soundTweak;
|
|
||||||
soundTweak.insert("set_tweak", "sound");
|
|
||||||
soundTweak.insert("value", sound);
|
|
||||||
actions.append(soundTweak);
|
|
||||||
}
|
|
||||||
if (action == PushNotificationAction::Highlight || action == PushNotificationAction::NoisyHighlight) {
|
|
||||||
QJsonObject highlightTweak;
|
|
||||||
highlightTweak.insert("set_tweak", "highlight");
|
|
||||||
actions.append(highlightTweak);
|
|
||||||
}
|
|
||||||
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -56,93 +56,6 @@ class NotificationsManager : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The global notification state.
|
|
||||||
*
|
|
||||||
* If this rule is set to off all push notifications are disabled regardless
|
|
||||||
* of other settings.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(bool globalNotificationsEnabled MEMBER m_globalNotificationsEnabled WRITE setGlobalNotificationsEnabled NOTIFY globalNotificationsEnabledChanged)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Whether the global notification state has been retrieved from the server.
|
|
||||||
*
|
|
||||||
* This is different to the others below as globalNotificationsEnabled is only
|
|
||||||
* a bool rather than a PushNotificationAction::Action so a separate property
|
|
||||||
* is required to track if the state has been retrieved from the server.
|
|
||||||
*
|
|
||||||
* @sa globalNotificationsEnabled, PushNotificationAction::Action
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(bool globalNotificationsSet MEMBER m_globalNotificationsSet NOTIFY globalNotificationsSetChanged)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The notification action for direct chats.
|
|
||||||
*
|
|
||||||
* @note Encrypted direct chats have a separate setting.
|
|
||||||
*
|
|
||||||
* @sa encryptedOneToOneNotificationAction
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(PushNotificationAction::Action oneToOneNotificationAction MEMBER m_oneToOneNotificationAction WRITE setOneToOneNotificationAction NOTIFY
|
|
||||||
oneToOneNotificationActionChanged)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The notification action for encrypted direct chats.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(PushNotificationAction::Action encryptedOneToOneNotificationAction MEMBER m_encryptedOneToOneNotificationAction WRITE
|
|
||||||
setEncryptedOneToOneNotificationAction NOTIFY encryptedOneToOneNotificationActionChanged)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The notification action for group chats.
|
|
||||||
*
|
|
||||||
* @note Encrypted group chats have a separate setting.
|
|
||||||
*
|
|
||||||
* @sa encryptedGroupChatNotificationAction
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(PushNotificationAction::Action groupChatNotificationAction MEMBER m_groupChatNotificationAction WRITE setGroupChatNotificationAction NOTIFY
|
|
||||||
groupChatNotificationActionChanged)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The notification action for encrypted group chats.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(PushNotificationAction::Action encryptedGroupChatNotificationAction MEMBER m_encryptedGroupChatNotificationAction WRITE
|
|
||||||
setEncryptedGroupChatNotificationAction NOTIFY encryptedGroupChatNotificationActionChanged)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The notification action for events containing the local user's display name.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(PushNotificationAction::Action displayNameNotificationAction MEMBER m_displayNameNotificationAction WRITE setDisplayNameNotificationAction NOTIFY
|
|
||||||
displayNameNotificationActionChanged)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The notification action for room events.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(PushNotificationAction::Action roomNotificationAction MEMBER m_roomNotificationAction WRITE setRoomNotificationAction NOTIFY
|
|
||||||
roomNotificationActionChanged)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The notification action for keyword push rules.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(PushNotificationAction::Action keywordNotificationAction MEMBER m_keywordNotificationAction WRITE setKeywordNotificationAction NOTIFY
|
|
||||||
keywordNotificationActionChanged)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The notification action for invites to chats.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(PushNotificationAction::Action inviteNotificationAction MEMBER m_inviteNotificationAction WRITE setInviteNotificationAction NOTIFY
|
|
||||||
inviteNotificationActionChanged)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The notification action for voice calls.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(PushNotificationAction::Action callInviteNotificationAction MEMBER m_callInviteNotificationAction WRITE setCallInviteNotificationAction NOTIFY
|
|
||||||
callInviteNotificationActionChanged)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief The notification action for room upgrade events.
|
|
||||||
*/
|
|
||||||
Q_PROPERTY(PushNotificationAction::Action tombstoneNotificationAction MEMBER m_tombstoneNotificationAction WRITE setTombstoneNotificationAction NOTIFY
|
|
||||||
tombstoneNotificationActionChanged)
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static NotificationsManager &instance();
|
static NotificationsManager &instance();
|
||||||
|
|
||||||
@@ -164,30 +77,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
void clearInvitationNotification(const QString &roomId);
|
void clearInvitationNotification(const QString &roomId);
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the initial keyword notification action.
|
|
||||||
*
|
|
||||||
* This is only required if no keyword rules exist and one is created. The default
|
|
||||||
* action is PushNotificationAction::Action::Highlight.
|
|
||||||
*
|
|
||||||
* @sa PushNotificationAction::Action
|
|
||||||
*/
|
|
||||||
void initializeKeywordNotificationAction();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Set the keyword notification action to PushNotificationAction::Action::Off.
|
|
||||||
*
|
|
||||||
* This is only required the last keyword rule is deleted.
|
|
||||||
*
|
|
||||||
* @sa PushNotificationAction::Action
|
|
||||||
*/
|
|
||||||
void deactivateKeywordNotificationAction();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Return the keyword notification action in a form required for the server push rule.
|
|
||||||
*/
|
|
||||||
QVector<QVariant> getKeywordNotificationActions();
|
|
||||||
|
|
||||||
#ifdef QUOTIENT_07
|
#ifdef QUOTIENT_07
|
||||||
/**
|
/**
|
||||||
* @brief Handle the notifications for the given connection.
|
* @brief Handle the notifications for the given connection.
|
||||||
@@ -208,52 +97,6 @@ private:
|
|||||||
QHash<QString, KNotification *> m_notifications;
|
QHash<QString, KNotification *> m_notifications;
|
||||||
QHash<QString, QPointer<KNotification>> m_invitations;
|
QHash<QString, QPointer<KNotification>> m_invitations;
|
||||||
|
|
||||||
bool m_globalNotificationsEnabled = false;
|
|
||||||
bool m_globalNotificationsSet = false;
|
|
||||||
PushNotificationAction::Action m_oneToOneNotificationAction = PushNotificationAction::Unknown;
|
|
||||||
PushNotificationAction::Action m_encryptedOneToOneNotificationAction = PushNotificationAction::Unknown;
|
|
||||||
PushNotificationAction::Action m_groupChatNotificationAction = PushNotificationAction::Unknown;
|
|
||||||
PushNotificationAction::Action m_encryptedGroupChatNotificationAction = PushNotificationAction::Unknown;
|
|
||||||
PushNotificationAction::Action m_displayNameNotificationAction = PushNotificationAction::Unknown;
|
|
||||||
PushNotificationAction::Action m_roomNotificationAction = PushNotificationAction::Unknown;
|
|
||||||
PushNotificationAction::Action m_keywordNotificationAction = PushNotificationAction::Unknown;
|
|
||||||
PushNotificationAction::Action m_inviteNotificationAction = PushNotificationAction::Unknown;
|
|
||||||
PushNotificationAction::Action m_callInviteNotificationAction = PushNotificationAction::Unknown;
|
|
||||||
PushNotificationAction::Action m_tombstoneNotificationAction = PushNotificationAction::Unknown;
|
|
||||||
|
|
||||||
void setGlobalNotificationsEnabled(bool enabled);
|
|
||||||
void setOneToOneNotificationAction(PushNotificationAction::Action action);
|
|
||||||
void setEncryptedOneToOneNotificationAction(PushNotificationAction::Action action);
|
|
||||||
void setGroupChatNotificationAction(PushNotificationAction::Action action);
|
|
||||||
void setEncryptedGroupChatNotificationAction(PushNotificationAction::Action action);
|
|
||||||
void setDisplayNameNotificationAction(PushNotificationAction::Action action);
|
|
||||||
void setRoomNotificationAction(PushNotificationAction::Action action);
|
|
||||||
void setKeywordNotificationAction(PushNotificationAction::Action action);
|
|
||||||
void setInviteNotificationAction(PushNotificationAction::Action action);
|
|
||||||
void setCallInviteNotificationAction(PushNotificationAction::Action action);
|
|
||||||
void setTombstoneNotificationAction(PushNotificationAction::Action action);
|
|
||||||
|
|
||||||
void setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled);
|
|
||||||
void setNotificationRuleActions(const QString &kind, const QString &ruleId, PushNotificationAction::Action action);
|
|
||||||
PushNotificationAction::Action toAction(const QJsonObject &rule);
|
|
||||||
QVector<QVariant> toActions(PushNotificationAction::Action action, const QString &sound = "default");
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void processNotificationJob(QPointer<Quotient::Connection> connection, Quotient::GetNotificationsJob *job, bool initialization);
|
void processNotificationJob(QPointer<Quotient::Connection> connection, Quotient::GetNotificationsJob *job, bool initialization);
|
||||||
|
|
||||||
void updateNotificationRules(const QString &type);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void globalNotificationsEnabledChanged(bool newState);
|
|
||||||
void globalNotificationsSetChanged(bool newState);
|
|
||||||
void oneToOneNotificationActionChanged(PushNotificationAction::Action action);
|
|
||||||
void encryptedOneToOneNotificationActionChanged(PushNotificationAction::Action action);
|
|
||||||
void groupChatNotificationActionChanged(PushNotificationAction::Action action);
|
|
||||||
void encryptedGroupChatNotificationActionChanged(PushNotificationAction::Action action);
|
|
||||||
void displayNameNotificationActionChanged(PushNotificationAction::Action action);
|
|
||||||
void roomNotificationActionChanged(PushNotificationAction::Action action);
|
|
||||||
void keywordNotificationActionChanged(PushNotificationAction::Action action);
|
|
||||||
void inviteNotificationActionChanged(PushNotificationAction::Action action);
|
|
||||||
void callInviteNotificationActionChanged(PushNotificationAction::Action action);
|
|
||||||
void tombstoneNotificationActionChanged(PushNotificationAction::Action action);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ import QtQuick.Layouts 1.15
|
|||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm
|
import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm
|
||||||
|
import org.kde.kitemmodels 1.0
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
Kirigami.ScrollablePage {
|
Kirigami.ScrollablePage {
|
||||||
|
id: root
|
||||||
|
|
||||||
property NeoChatRoom room
|
property NeoChatRoom room
|
||||||
|
|
||||||
@@ -62,5 +64,86 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MobileForm.FormCard {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
contentItem: ColumnLayout {
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
MobileForm.FormCardHeader {
|
||||||
|
title: i18n("Keywords")
|
||||||
|
}
|
||||||
|
Repeater {
|
||||||
|
model: KSortFilterProxyModel {
|
||||||
|
sourceModel: Controller.pushRuleModel
|
||||||
|
|
||||||
|
filterRowCallback: function(source_row, source_parent) {
|
||||||
|
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
||||||
|
let roomIdRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.RoomIdRole)
|
||||||
|
return sectionRole == PushNotificationSection.RoomKeywords && roomIdRole == root.room.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ruleDelegate
|
||||||
|
}
|
||||||
|
MobileForm.AbstractFormDelegate {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
contentItem : RowLayout {
|
||||||
|
Kirigami.ActionTextField {
|
||||||
|
id: keywordAddField
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
placeholderText: i18n("Keyword…")
|
||||||
|
enabled: NotificationsManager.keywordNotificationAction !== PushNotificationAction.Unknown
|
||||||
|
|
||||||
|
rightActions: Kirigami.Action {
|
||||||
|
icon.name: "edit-clear"
|
||||||
|
visible: keywordAddField.text.length > 0
|
||||||
|
onTriggered: {
|
||||||
|
keywordAddField.text = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
Controller.pushRuleModel.addKeyword(keywordAddField.text, root.room.id)
|
||||||
|
keywordAddField.text = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QQC2.Button {
|
||||||
|
id: addButton
|
||||||
|
|
||||||
|
text: i18n("Add keyword")
|
||||||
|
Accessible.name: text
|
||||||
|
icon.name: "list-add"
|
||||||
|
display: QQC2.AbstractButton.IconOnly
|
||||||
|
enabled: NotificationsManager.keywordNotificationAction !== PushNotificationAction.Unknown
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
Controller.pushRuleModel.addKeyword(keywordAddField.text, root.room.id)
|
||||||
|
keywordAddField.text = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
QQC2.ToolTip {
|
||||||
|
text: addButton.text
|
||||||
|
delay: Kirigami.Units.toolTipDelay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: ruleDelegate
|
||||||
|
NotificationRuleItem {
|
||||||
|
onDeleteRule: {
|
||||||
|
Controller.pushRuleModel.removeKeyword(id)
|
||||||
|
}
|
||||||
|
onActionChanged: (action) => Controller.pushRuleModel.setPushRuleAction(id, action)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,17 @@ import QtQuick.Layouts 1.15
|
|||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm
|
import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm
|
||||||
|
import org.kde.kitemmodels 1.0
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
Kirigami.ScrollablePage {
|
Kirigami.ScrollablePage {
|
||||||
|
id: root
|
||||||
|
|
||||||
title: i18nc("@title:window", "Notifications")
|
title: i18nc("@title:window", "Notifications")
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: notificationLayout
|
id: notificationLayout
|
||||||
|
|
||||||
@@ -22,10 +26,10 @@ Kirigami.ScrollablePage {
|
|||||||
contentItem: MobileForm.FormCheckDelegate {
|
contentItem: MobileForm.FormCheckDelegate {
|
||||||
text: i18n("Enable notifications for this account")
|
text: i18n("Enable notifications for this account")
|
||||||
description: i18n("Whether push notifications are generated by your Matrix server")
|
description: i18n("Whether push notifications are generated by your Matrix server")
|
||||||
checked: NotificationsManager.globalNotificationsEnabled
|
checked: Controller.pushRuleModel.globalNotificationsEnabled
|
||||||
enabled: NotificationsManager.globalNotificationsSet
|
enabled: Controller.pushRuleModel.globalNotificationsSet
|
||||||
onToggled: {
|
onToggled: {
|
||||||
NotificationsManager.globalNotificationsEnabled = checked
|
Controller.pushRuleModel.globalNotificationsEnabled = checked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,81 +43,17 @@ Kirigami.ScrollablePage {
|
|||||||
MobileForm.FormCardHeader {
|
MobileForm.FormCardHeader {
|
||||||
title: i18n("Room Notifications")
|
title: i18n("Room Notifications")
|
||||||
}
|
}
|
||||||
NotificationRuleItem {
|
Repeater {
|
||||||
text: i18n("Messages in one-to-one chats")
|
model: KSortFilterProxyModel {
|
||||||
|
sourceModel: Controller.pushRuleModel
|
||||||
|
|
||||||
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.oneToOneNotificationAction)
|
filterRowCallback: function(source_row, source_parent) {
|
||||||
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.oneToOneNotificationAction)
|
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
||||||
enabled: NotificationsManager.oneToOneNotificationAction !== PushNotificationAction.Unknown
|
return sectionRole == PushNotificationSection.Room;
|
||||||
|
|
||||||
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
|
delegate: ruleDelegate
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,37 +67,17 @@ Kirigami.ScrollablePage {
|
|||||||
MobileForm.FormCardHeader {
|
MobileForm.FormCardHeader {
|
||||||
title: i18n("@Mentions")
|
title: i18n("@Mentions")
|
||||||
}
|
}
|
||||||
NotificationRuleItem {
|
Repeater {
|
||||||
text: i18n("Messages containing my display name")
|
model: KSortFilterProxyModel {
|
||||||
|
sourceModel: Controller.pushRuleModel
|
||||||
|
|
||||||
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.displayNameNotificationAction)
|
filterRowCallback: function(source_row, source_parent) {
|
||||||
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.displayNameNotificationAction)
|
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
||||||
highlightable: true
|
return sectionRole == PushNotificationSection.Mentions;
|
||||||
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)
|
delegate: ruleDelegate
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,47 +91,17 @@ Kirigami.ScrollablePage {
|
|||||||
MobileForm.FormCardHeader {
|
MobileForm.FormCardHeader {
|
||||||
title: i18n("Keywords")
|
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 {
|
Repeater {
|
||||||
model: KeywordNotificationRuleModel {
|
model: KSortFilterProxyModel {
|
||||||
id: keywordNotificationRuleModel
|
sourceModel: Controller.pushRuleModel
|
||||||
}
|
|
||||||
|
|
||||||
delegate: NotificationRuleItem {
|
filterRowCallback: function(source_row, source_parent) {
|
||||||
text: name
|
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
||||||
notificationAction: keywordNotificationAction.notificationAction
|
return sectionRole == PushNotificationSection.Keywords;
|
||||||
notificationsOn: keywordNotificationAction.notificationsOn
|
|
||||||
notificationsOnModifiable: false
|
|
||||||
noisyOn: keywordNotificationAction.noisyOn
|
|
||||||
noisyModifiable: false
|
|
||||||
highlightOn: keywordNotificationAction.highlightOn
|
|
||||||
deletable: true
|
|
||||||
|
|
||||||
onDeleteItemChanged: {
|
|
||||||
if (deleteItem && deletable) {
|
|
||||||
keywordNotificationRuleModel.removeKeywordAtIndex(index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delegate: ruleDelegate
|
||||||
}
|
}
|
||||||
MobileForm.AbstractFormDelegate {
|
MobileForm.AbstractFormDelegate {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@@ -234,7 +124,7 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
keywordNotificationRuleModel.addKeyword(keywordAddField.text, PushNotificationAction.On)
|
Controller.pushRuleModel.addKeyword(keywordAddField.text)
|
||||||
keywordAddField.text = ""
|
keywordAddField.text = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,7 +138,7 @@ Kirigami.ScrollablePage {
|
|||||||
enabled: NotificationsManager.keywordNotificationAction !== PushNotificationAction.Unknown
|
enabled: NotificationsManager.keywordNotificationAction !== PushNotificationAction.Unknown
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
keywordNotificationRuleModel.addKeyword(keywordAddField.text, PushNotificationAction.On)
|
Controller.pushRuleModel.addKeyword(keywordAddField.text)
|
||||||
keywordAddField.text = ""
|
keywordAddField.text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,59 +161,29 @@ Kirigami.ScrollablePage {
|
|||||||
MobileForm.FormCardHeader {
|
MobileForm.FormCardHeader {
|
||||||
title: i18n("Invites")
|
title: i18n("Invites")
|
||||||
}
|
}
|
||||||
NotificationRuleItem {
|
Repeater {
|
||||||
text: i18n("Invites to a room")
|
model: KSortFilterProxyModel {
|
||||||
|
sourceModel: Controller.pushRuleModel
|
||||||
|
|
||||||
notificationsOn: notificationLayout.isNotificationRuleOn(NotificationsManager.inviteNotificationAction)
|
filterRowCallback: function(source_row, source_parent) {
|
||||||
noisyOn: notificationLayout.isNotificationRuleNoisy(NotificationsManager.inviteNotificationAction)
|
let sectionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PushRuleModel.SectionRole)
|
||||||
highlightable: true
|
return sectionRole == PushNotificationSection.Invites;
|
||||||
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
|
delegate: ruleDelegate
|
||||||
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) {
|
Component {
|
||||||
return action == PushNotificationAction.On ||
|
id: ruleDelegate
|
||||||
action == PushNotificationAction.Noisy ||
|
NotificationRuleItem {
|
||||||
action == PushNotificationAction.Highlight ||
|
onDeleteRule: {
|
||||||
action == PushNotificationAction.NoisyHighlight
|
Controller.pushRuleModel.removeKeyword(id)
|
||||||
}
|
}
|
||||||
|
onActionChanged: (action) => Controller.pushRuleModel.setPushRuleAction(id, action)
|
||||||
function isNotificationRuleNoisy(action) {
|
|
||||||
return action == PushNotificationAction.Noisy ||
|
|
||||||
action == PushNotificationAction.NoisyHighlight
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNotificationRuleHighlight(action) {
|
|
||||||
return action == PushNotificationAction.Highlight ||
|
|
||||||
action == PushNotificationAction.NoisyHighlight
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,19 +11,25 @@ import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm
|
|||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
MobileForm.AbstractFormDelegate {
|
MobileForm.AbstractFormDelegate {
|
||||||
id: notificationRuleItem
|
id: root
|
||||||
|
|
||||||
property var notificationAction: PushNotificationAction.Unkown
|
required property string id
|
||||||
property bool notificationsOn: false
|
required property string name
|
||||||
property bool notificationsOnModifiable: true
|
required property int ruleAction
|
||||||
property bool noisyOn: false
|
required property bool highlightable
|
||||||
property bool noisyModifiable: true
|
required property bool deletable
|
||||||
property bool highlightOn: false
|
|
||||||
property bool highlightable: false
|
readonly property bool notificationsOn: isNotificationRuleOn(ruleAction)
|
||||||
property bool deleteItem: false
|
readonly property bool notificationsOnModifiable: !deletable
|
||||||
property bool deletable: false
|
readonly property bool highlightOn: isNotificationRuleHighlight(ruleAction)
|
||||||
|
|
||||||
|
signal actionChanged(int action)
|
||||||
|
signal deleteRule()
|
||||||
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
enabled: ruleAction !== PushNotificationAction.Unknown
|
||||||
|
|
||||||
|
text: name
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
notificationAction = nextNotificationRuleAction(notificationAction)
|
notificationAction = nextNotificationRuleAction(notificationAction)
|
||||||
@@ -42,8 +48,8 @@ MobileForm.AbstractFormDelegate {
|
|||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
visible: notificationsOn
|
visible: notificationsOn
|
||||||
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
Kirigami.Theme.colorSet: Kirigami.Theme.Button
|
||||||
color: highlightOn ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.disabledTextColor
|
color: highlightOn && highlightable ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.disabledTextColor
|
||||||
opacity: highlightOn ? 1 : 0.3
|
opacity: highlightOn && highlightable ? 1 : 0.3
|
||||||
radius: height / 2
|
radius: height / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,7 +57,7 @@ MobileForm.AbstractFormDelegate {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
|
||||||
text: notificationRuleItem.text
|
text: root.text
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
maximumLineCount: 2
|
maximumLineCount: 2
|
||||||
@@ -67,13 +73,13 @@ MobileForm.AbstractFormDelegate {
|
|||||||
icon.name: checked ? "notifications" : "notifications-disabled"
|
icon.name: checked ? "notifications" : "notifications-disabled"
|
||||||
display: QQC2.AbstractButton.IconOnly
|
display: QQC2.AbstractButton.IconOnly
|
||||||
|
|
||||||
visible: notificationRuleItem.notificationsOnModifiable
|
visible: root.notificationsOnModifiable
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: notificationRuleItem.notificationsOn
|
checked: root.notificationsOn
|
||||||
enabled: notificationRuleItem.enabled
|
enabled: root.enabled
|
||||||
down: checked
|
down: checked
|
||||||
onToggled: {
|
onToggled: {
|
||||||
notificationRuleItem.notificationAction = notificationRuleItem.notifcationRuleAction()
|
root.actionChanged(root.notifcationRuleAction())
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.ToolTip {
|
QQC2.ToolTip {
|
||||||
@@ -89,13 +95,12 @@ MobileForm.AbstractFormDelegate {
|
|||||||
icon.name: checked ? "audio-volume-high" : "audio-volume-muted"
|
icon.name: checked ? "audio-volume-high" : "audio-volume-muted"
|
||||||
display: QQC2.AbstractButton.IconOnly
|
display: QQC2.AbstractButton.IconOnly
|
||||||
|
|
||||||
visible: notificationRuleItem.noisyModifiable
|
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: notificationRuleItem.noisyOn
|
checked: isNotificationRuleNoisy(root.ruleAction)
|
||||||
enabled: (onButton.checked || !notificationRuleItem.notificationsOnModifiable) && notificationRuleItem.enabled
|
enabled: (onButton.checked || !root.notificationsOnModifiable) && root.enabled
|
||||||
down: checked
|
down: checked
|
||||||
onToggled: {
|
onToggled: {
|
||||||
notificationRuleItem.notificationAction = notificationRuleItem.notifcationRuleAction()
|
root.actionChanged(root.notifcationRuleAction())
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.ToolTip {
|
QQC2.ToolTip {
|
||||||
@@ -111,13 +116,13 @@ MobileForm.AbstractFormDelegate {
|
|||||||
icon.name: "draw-highlight"
|
icon.name: "draw-highlight"
|
||||||
display: QQC2.AbstractButton.IconOnly
|
display: QQC2.AbstractButton.IconOnly
|
||||||
|
|
||||||
visible: notificationRuleItem.highlightable
|
visible: root.highlightable
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: notificationRuleItem.highlightOn
|
checked: root.highlightOn
|
||||||
enabled: (onButton.checked || !notificationRuleItem.notificationsOnModifiable) && notificationRuleItem.enabled
|
enabled: (onButton.checked || !root.notificationsOnModifiable) && root.enabled
|
||||||
down: checked
|
down: checked
|
||||||
onToggled: {
|
onToggled: {
|
||||||
notificationRuleItem.notificationAction = notificationRuleItem.notifcationRuleAction()
|
root.actionChanged(root.notifcationRuleAction())
|
||||||
}
|
}
|
||||||
|
|
||||||
QQC2.ToolTip {
|
QQC2.ToolTip {
|
||||||
@@ -131,10 +136,10 @@ MobileForm.AbstractFormDelegate {
|
|||||||
Accessible.name: i18n("Delete keyword")
|
Accessible.name: i18n("Delete keyword")
|
||||||
icon.name: "edit-delete-remove"
|
icon.name: "edit-delete-remove"
|
||||||
|
|
||||||
visible: notificationRuleItem.deletable
|
visible: root.deletable
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
notificationRuleItem.deleteItem = !notificationRuleItem.deleteItem
|
root.deleteRule()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,11 +147,11 @@ MobileForm.AbstractFormDelegate {
|
|||||||
|
|
||||||
function notifcationRuleAction() {
|
function notifcationRuleAction() {
|
||||||
if (onButton.checked) {
|
if (onButton.checked) {
|
||||||
if (noisyButton.checked && highlightButton.checked) {
|
if (noisyButton.checked && highlightButton.checked && root.highlightable) {
|
||||||
return PushNotificationAction.NoisyHighlight
|
return PushNotificationAction.NoisyHighlight
|
||||||
} else if (noisyButton.checked) {
|
} else if (noisyButton.checked) {
|
||||||
return PushNotificationAction.Noisy
|
return PushNotificationAction.Noisy
|
||||||
} else if (highlightButton.checked) {
|
} else if (highlightButton.checked && root.highlightable) {
|
||||||
return PushNotificationAction.Highlight
|
return PushNotificationAction.Highlight
|
||||||
} else {
|
} else {
|
||||||
return PushNotificationAction.On
|
return PushNotificationAction.On
|
||||||
@@ -166,17 +171,34 @@ MobileForm.AbstractFormDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (!finished) {
|
while (!finished) {
|
||||||
if (action == PushNotificationAction.Off && !notificationRuleItem.notificationsOnModifiable) {
|
if (action == PushNotificationAction.Off && !root.notificationsOnModifiable) {
|
||||||
action = PushNotificationAction.On
|
action = PushNotificationAction.On
|
||||||
} else if (action == PushNotificationAction.Noisy && !notificationRuleItem.noisyModifiable) {
|
} else if (action == PushNotificationAction.Noisy) {
|
||||||
action = PushNotificationAction.Highlight
|
action = PushNotificationAction.Highlight
|
||||||
} else if (action == PushNotificationAction.Highlight && !notificationRuleItem.highlightable) {
|
} else if (action == PushNotificationAction.Highlight && !root.highlightable) {
|
||||||
action = PushNotificationAction.Off
|
action = PushNotificationAction.Off
|
||||||
} else {
|
} else {
|
||||||
finished = true
|
finished = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return action
|
actionChanged(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user