NotificationManager rework

Rework notifications manager to no longer be a singleton, but a component of controller.

The dependency on it for neochat room and connection is also removed.
This commit is contained in:
James Graham
2024-10-06 17:14:18 +00:00
parent 71468e453c
commit 1237e9d4bd
9 changed files with 107 additions and 91 deletions

View File

@@ -18,6 +18,7 @@
#include <Quotient/settings.h>
#include "neochatconfig.h"
#include "neochatconnection.h"
#include "neochatroom.h"
#include "notificationsmanager.h"
#include "proxycontroller.h"
@@ -168,6 +169,10 @@ void Controller::addConnection(NeoChatConnection *c)
dropConnection(c);
});
connect(c, &NeoChatConnection::badgeNotificationCountChanged, this, &Controller::updateBadgeNotificationCount);
connect(c, &NeoChatConnection::syncDone, this, [this, c]() {
m_notificationsManager.handleNotifications(c);
});
connect(c, &NeoChatConnection::showInviteNotification, &m_notificationsManager, &NotificationsManager::postInviteNotification);
c->sync();
@@ -178,6 +183,8 @@ void Controller::dropConnection(NeoChatConnection *c)
{
Q_ASSERT_X(c, __FUNCTION__, "Attempt to drop a null connection");
c->disconnect(this);
c->disconnect(&m_notificationsManager);
m_accountRegistry.drop(c);
Q_EMIT connectionDropped(c);
}
@@ -327,6 +334,7 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
if (m_connection != nullptr) {
m_connection->disconnect(this);
m_connection->disconnect(&m_notificationsManager);
}
m_connection = connection;
@@ -350,7 +358,7 @@ void Controller::listenForNotifications()
connect(timer, &QTimer::timeout, qGuiApp, &QGuiApplication::quit);
connect(connector, &KUnifiedPush::Connector::messageReceived, [timer](const QByteArray &data) {
NotificationsManager::instance().postPushNotification(data);
m_notificationsManager.postPushNotification(data);
timer->stop();
});
@@ -362,6 +370,11 @@ void Controller::listenForNotifications()
#endif
}
void Controller::clearInvitationNotification(const QString &roomId)
{
m_notificationsManager.clearInvitationNotification(roomId);
}
void Controller::updateBadgeNotificationCount(NeoChatConnection *connection, int count)
{
if (connection == m_connection) {

View File

@@ -7,6 +7,7 @@
#include <QQmlEngine>
#include "neochatconnection.h"
#include "notificationsmanager.h"
#include <Quotient/accountregistry.h>
class TrayIcon;
@@ -88,6 +89,13 @@ public:
*/
static void listenForNotifications();
/**
* @brief Clear an existing invite notification for the given room.
*
* Nothing happens if the given room doesn't have an invite notification.
*/
Q_INVOKABLE void clearInvitationNotification(const QString &roomId);
Q_INVOKABLE QString loadFileContent(const QString &path) const;
Quotient::AccountRegistry &accounts();
@@ -123,6 +131,8 @@ private:
QString m_endpoint;
QStringList m_shownImages;
NotificationsManager m_notificationsManager;
private Q_SLOTS:
void invokeLogin();
void setQuitOnLastWindowClosed();

View File

@@ -10,7 +10,6 @@
#include "jobs/neochatdeactivateaccountjob.h"
#include "neochatconfig.h"
#include "neochatroom.h"
#include "notificationsmanager.h"
#include "spacehierarchycache.h"
#include <Quotient/jobs/basejob.h>
@@ -66,10 +65,6 @@ void NeoChatConnection::connectSignals()
});
connect(this, &NeoChatConnection::syncDone, this, [this] {
setIsOnline(true);
connect(this, &NeoChatConnection::syncDone, this, [this]() {
NotificationsManager::instance().handleNotifications(this);
});
});
connect(this, &NeoChatConnection::networkError, this, [this]() {
setIsOnline(false);
@@ -114,6 +109,10 @@ void NeoChatConnection::connectSignals()
Q_EMIT homeHaveHighlightNotificationsChanged();
});
});
connect(this, &NeoChatConnection::invitedRoom, this, [this](Quotient::Room *room) {
auto r = dynamic_cast<NeoChatRoom *>(room);
connect(r, &NeoChatRoom::showInviteNotification, this, &NeoChatConnection::showInviteNotification);
});
connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) {
Q_UNUSED(room)
if (prev && prev->isDirectChat()) {

View File

@@ -209,6 +209,11 @@ Q_SIGNALS:
*/
void errorOccured(const QString &error);
/**
* @brief Request a notification be shown for an invite to this room.
*/
void showInviteNotification(NeoChatRoom *room);
private:
bool m_isOnline = true;
void setIsOnline(bool isOnline);

View File

@@ -40,9 +40,7 @@
#include "events/joinrulesevent.h"
#include "events/pollevent.h"
#include "filetransferpseudojob.h"
#include "jobs/neochatgetcommonroomsjob.h"
#include "neochatconfig.h"
#include "notificationsmanager.h"
#include "roomlastmessageprovider.h"
#include "spacehierarchycache.h"
#include "texthandler.h"
@@ -125,45 +123,8 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
updatePushNotificationState(QStringLiteral("m.push_rules"));
Q_EMIT canEncryptRoomChanged();
if (this->joinState() != JoinState::Invite) {
return;
}
auto roomMemberEvent = currentState().get<RoomMemberEvent>(localMember().id());
auto showNotification = [this, roomMemberEvent] {
QImage avatar_image;
if (roomMemberEvent && !member(roomMemberEvent->senderId()).avatarUrl().isEmpty()) {
#if Quotient_VERSION_MINOR > 8
avatar_image = member(roomMemberEvent->senderId()).avatar(128, 128, {});
#else
avatar_image = memberAvatar(roomMemberEvent->senderId()).get(this->connection(), 128, [] {});
#endif
} else {
qWarning() << "using this room's avatar";
avatar_image = avatar(128);
}
NotificationsManager::instance().postInviteNotification(this,
displayName(),
member(roomMemberEvent->senderId()).htmlSafeDisplayName(),
avatar_image);
};
if (NeoChatConfig::rejectUnknownInvites()) {
auto job = this->connection()->callApi<NeochatGetCommonRoomsJob>(roomMemberEvent->senderId());
connect(job, &BaseJob::result, this, [this, job, showNotification] {
QJsonObject replyData = job->jsonData();
if (replyData.contains(QStringLiteral("joined"))) {
const bool inAnyOfOurRooms = !replyData[QStringLiteral("joined")].toArray().isEmpty();
if (inAnyOfOurRooms) {
showNotification();
} else {
leaveRoom();
}
}
});
} else {
showNotification();
if (this->joinState() == JoinState::Invite) {
Q_EMIT showInviteNotification(this);
}
},
Qt::SingleShotConnection);
@@ -946,11 +907,6 @@ QCoro::Task<void> NeoChatRoom::doDeleteMessagesByUser(const QString &user, QStri
}
}
void NeoChatRoom::clearInvitationNotification()
{
NotificationsManager::instance().clearInvitationNotification(id());
}
bool NeoChatRoom::hasParent() const
{
return currentState().eventsOfType("m.space.parent"_ls).size() > 0;

View File

@@ -418,8 +418,6 @@ public:
bool readOnly() const;
Q_INVOKABLE void clearInvitationNotification();
[[nodiscard]] QString joinRule() const;
/**
@@ -664,6 +662,14 @@ Q_SIGNALS:
*/
void showMessage(MessageType::Type messageType, const QString &message);
/**
* @brief Request a notification be shown for an invite to this room.
*
* @note This may later be blocked if there are any rules on where invites can
* come from, but this is not NeoChatRoom's responsibility.
*/
void showInviteNotification(NeoChatRoom *room);
public Q_SLOTS:
/**
* @brief Upload a file to the matrix server and post the file to the room.

View File

@@ -15,6 +15,7 @@
#include <QPainter>
#include <Quotient/accountregistry.h>
#include <Quotient/csapi/pushrules.h>
#include <Quotient/events/roommemberevent.h>
#include <Quotient/user.h>
#ifdef HAVE_KIO
@@ -22,6 +23,8 @@
#endif
#include "controller.h"
#include "jobs/neochatgetcommonroomsjob.h"
#include "neochatconfig.h"
#include "neochatconnection.h"
#include "neochatroom.h"
#include "roommanager.h"
@@ -30,12 +33,6 @@
using namespace Quotient;
NotificationsManager &NotificationsManager::instance()
{
static NotificationsManager _instance;
return _instance;
}
NotificationsManager::NotificationsManager(QObject *parent)
: QObject(parent)
{
@@ -249,15 +246,57 @@ void NotificationsManager::postNotification(NeoChatRoom *room,
notification->sendEvent();
}
void NotificationsManager::postInviteNotification(NeoChatRoom *rawRoom, const QString &title, const QString &sender, const QImage &icon)
void NotificationsManager::postInviteNotification(NeoChatRoom *rawRoom)
{
QPointer room(rawRoom);
QPixmap img;
img.convertFromImage(icon);
const auto roomMemberEvent = room->currentState().get<RoomMemberEvent>(room->localMember().id());
if (roomMemberEvent == nullptr) {
return;
}
if (NeoChatConfig::rejectUnknownInvites()) {
auto job = room->connection()->callApi<NeochatGetCommonRoomsJob>(roomMemberEvent->senderId());
connect(job, &BaseJob::result, this, [this, job, room] {
QJsonObject replyData = job->jsonData();
if (replyData.contains(QStringLiteral("joined"))) {
const bool inAnyOfOurRooms = !replyData[QStringLiteral("joined")].toArray().isEmpty();
if (inAnyOfOurRooms) {
doPostInviteNotification(room);
} else {
room->leaveRoom();
}
}
});
} else {
doPostInviteNotification(room);
}
}
void NotificationsManager::doPostInviteNotification(QPointer<NeoChatRoom> room)
{
const auto roomMemberEvent = room->currentState().get<RoomMemberEvent>(room->localMember().id());
if (roomMemberEvent == nullptr) {
return;
}
const auto sender = room->member(roomMemberEvent->senderId());
QImage avatar_image;
if (roomMemberEvent && !room->member(roomMemberEvent->senderId()).avatarUrl().isEmpty()) {
#if Quotient_VERSION_MINOR > 8
avatar_image = room->member(roomMemberEvent->senderId()).avatar(128, 128, {});
#else
avatar_image = room->memberAvatar(roomMemberEvent->senderId()).get(room->connection(), 128, [] {});
#endif
} else {
qWarning() << "using this room's avatar";
avatar_image = room->avatar(128);
}
KNotification *notification = new KNotification(QStringLiteral("invite"));
notification->setText(i18n("%1 invited you to a room", sender));
notification->setTitle(title);
notification->setPixmap(createNotificationImage(icon, nullptr));
notification->setText(i18n("%1 invited you to a room", sender.htmlSafeDisplayName()));
notification->setTitle(room->displayName());
notification->setPixmap(createNotificationImage(avatar_image, nullptr));
auto defaultAction = notification->addDefaultAction(i18n("Open this invitation in NeoChat"));
connect(defaultAction, &KNotificationAction::activated, this, [notification, room]() {
if (!room) {

View File

@@ -34,31 +34,14 @@ class NotificationsManager : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
static NotificationsManager &instance();
static NotificationsManager *create(QQmlEngine *engine, QJSEngine *)
{
engine->setObjectOwnership(&instance(), QQmlEngine::CppOwnership);
return &instance();
}
/**
* @brief Display a native notification for an message.
*/
Q_INVOKABLE void postNotification(NeoChatRoom *room,
const QString &sender,
const QString &text,
const QImage &icon,
const QString &replyEventId,
bool canReply,
qint64 timestamp);
explicit NotificationsManager(QObject *parent = nullptr);
/**
* @brief Display a native notification for an invite.
*/
void postInviteNotification(NeoChatRoom *room, const QString &title, const QString &sender, const QImage &icon);
void postInviteNotification(NeoChatRoom *room);
/**
* @brief Clear an existing invite notification for the given room.
@@ -78,21 +61,26 @@ public:
void handleNotifications(QPointer<NeoChatConnection> connection);
private:
explicit NotificationsManager(QObject *parent = nullptr);
QHash<QString, qint64> m_initialTimestamp;
QHash<QString, QStringList> m_oldNotifications;
QStringList m_connActiveJob;
QPixmap createNotificationImage(const QImage &icon, NeoChatRoom *room);
bool shouldPostNotification(QPointer<NeoChatConnection> connection, const QJsonValue &notification);
void postNotification(NeoChatRoom *room,
const QString &sender,
const QString &text,
const QImage &icon,
const QString &replyEventId,
bool canReply,
qint64 timestamp);
void doPostInviteNotification(QPointer<NeoChatRoom> room);
QHash<QString, std::pair<qint64, KNotification *>> m_notifications;
QHash<QString, QPointer<KNotification>> m_invitations;
private Q_SLOTS:
void processNotificationJob(QPointer<NeoChatConnection> connection, Quotient::GetNotificationsJob *job, bool initialization);
private:
QPixmap createNotificationImage(const QImage &icon, NeoChatRoom *room);
};

View File

@@ -186,7 +186,7 @@ Kirigami.Page {
target: RoomManager
function onCurrentRoomChanged() {
if (root.currentRoom && root.currentRoom.isInvite) {
root.currentRoom.clearInvitationNotification();
Controller.clearInvitationNotification(root.currentRoom.id);
}
}