From 4b4c321397e0681f306eade889e0fab6dbbe94f5 Mon Sep 17 00:00:00 2001
From: Nicolas Werner <nicolas.werner@hotmail.de>
Date: Thu, 7 Jan 2021 10:44:59 +0100
Subject: [PATCH] Allow inline replies from notifications on linux

---
 src/ChatPage.cpp                     |  8 ++++++
 src/notifications/Manager.h          |  2 ++
 src/notifications/ManagerLinux.cpp   | 42 +++++++++++++++++++++-------
 src/timeline/InputBar.h              |  2 +-
 src/timeline/TimelineViewManager.cpp | 12 ++++++++
 src/timeline/TimelineViewManager.h   |  3 ++
 6 files changed, 58 insertions(+), 11 deletions(-)

diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 37248022d..4e87349a6 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -282,6 +282,14 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
                         room_list_->highlightSelectedRoom(roomid);
                         activateWindow();
                 });
+        connect(&notificationsManager,
+                &NotificationsManager::sendNotificationReply,
+                this,
+                [this](const QString &roomid, const QString &eventid, const QString &body) {
+                        view_manager_->queueReply(roomid, eventid, body);
+                        room_list_->highlightSelectedRoom(roomid);
+                        activateWindow();
+                });
 
         setGroupViewState(userSettings_->groupView());
 
diff --git a/src/notifications/Manager.h b/src/notifications/Manager.h
index e6be5953b..b5347bd61 100644
--- a/src/notifications/Manager.h
+++ b/src/notifications/Manager.h
@@ -36,6 +36,7 @@ public:
 
 signals:
         void notificationClicked(const QString roomId, const QString eventId);
+        void sendNotificationReply(const QString roomId, const QString eventId, const QString body);
 
 public slots:
         void removeNotification(const QString &roomId, const QString &eventId);
@@ -58,6 +59,7 @@ private:
 private slots:
         void actionInvoked(uint id, QString action);
         void notificationClosed(uint id, uint reason);
+        void notificationReplied(uint id, QString reply);
 };
 
 #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)
diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp
index b9eca1a85..b5e9a6a4d 100644
--- a/src/notifications/ManagerLinux.cpp
+++ b/src/notifications/ManagerLinux.cpp
@@ -28,6 +28,12 @@ NotificationsManager::NotificationsManager(QObject *parent)
                                               "NotificationClosed",
                                               this,
                                               SLOT(notificationClosed(uint, uint)));
+        QDBusConnection::sessionBus().connect("org.freedesktop.Notifications",
+                                              "/org/freedesktop/Notifications",
+                                              "org.freedesktop.Notifications",
+                                              "NotificationReplied",
+                                              this,
+                                              SLOT(notificationReplied(uint, QString)));
 }
 
 void
@@ -56,14 +62,19 @@ NotificationsManager::showNotification(const QString summary,
         hints["image-data"] = image;
         hints["sound-name"] = "message-new-instant";
         QList<QVariant> argumentList;
-        argumentList << "nheko";                             // app_name
-        argumentList << (uint)0;                             // replace_id
-        argumentList << "";                                  // app_icon
-        argumentList << summary;                             // summary
-        argumentList << text;                                // body
-        argumentList << (QStringList("default") << "reply"); // actions
-        argumentList << hints;                               // hints
-        argumentList << (int)-1;                             // timeout in ms
+        argumentList << "nheko"; // app_name
+        argumentList << (uint)0; // replace_id
+        argumentList << "";      // app_icon
+        argumentList << summary; // summary
+        argumentList << text;    // body
+        // The list of actions has always the action name and then a localized version of that
+        // action. Currently we just use an empty string for that.
+        // TODO(Nico): Look into what to actually put there.
+        argumentList << (QStringList("default") << ""
+                                                << "inline-reply"
+                                                << ""); // actions
+        argumentList << hints;                          // hints
+        argumentList << (int)-1;                        // timeout in ms
 
         static QDBusInterface notifyApp("org.freedesktop.Notifications",
                                         "/org/freedesktop/Notifications",
@@ -121,9 +132,20 @@ NotificationsManager::removeNotification(const QString &roomId, const QString &e
 void
 NotificationsManager::actionInvoked(uint id, QString action)
 {
-        if (action == "default" && notificationIds.contains(id)) {
+        if (notificationIds.contains(id)) {
+                roomEventId idEntry = notificationIds[id];
+                if (action == "default") {
+                        emit notificationClicked(idEntry.roomId, idEntry.eventId);
+                }
+        }
+}
+
+void
+NotificationsManager::notificationReplied(uint id, QString reply)
+{
+        if (notificationIds.contains(id)) {
                 roomEventId idEntry = notificationIds[id];
-                emit notificationClicked(idEntry.roomId, idEntry.eventId);
+                emit sendNotificationReply(idEntry.roomId, idEntry.eventId, reply);
         }
 }
 
diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h
index 27aa4bc3f..89ca34fe3 100644
--- a/src/timeline/InputBar.h
+++ b/src/timeline/InputBar.h
@@ -42,6 +42,7 @@ public slots:
         void openFileSelection();
         bool uploading() const { return uploading_; }
         void callButton();
+        void message(QString body);
 
         QObject *completerFor(QString completerName);
 
@@ -54,7 +55,6 @@ signals:
         void uploadingChanged(bool value);
 
 private:
-        void message(QString body);
         void emote(QString body);
         void command(QString name, QString args);
         void image(const QString &filename,
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index f31b5ea5e..f10c2c0de 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -508,6 +508,18 @@ TimelineViewManager::initWithMessages(const std::vector<QString> &roomIds)
                 addRoom(roomId);
 }
 
+void
+TimelineViewManager::queueReply(const QString &roomid,
+                                const QString &repliedToEvent,
+                                const QString &replyBody)
+{
+        auto room = models.find(roomid);
+        if (room != models.end()) {
+                room.value()->setReply(repliedToEvent);
+                room.value()->input()->message(replyBody);
+        }
+}
+
 void
 TimelineViewManager::queueReactionMessage(const QString &reactedEvent, const QString &reactionKey)
 {
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index f346acf83..1cec09392 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -120,6 +120,9 @@ public slots:
         }
 
         void updateColorPalette();
+        void queueReply(const QString &roomid,
+                        const QString &repliedToEvent,
+                        const QString &replyBody);
         void queueReactionMessage(const QString &reactedEvent, const QString &reactionKey);
         void queueCallMessage(const QString &roomid, const mtx::events::msg::CallInvite &);
         void queueCallMessage(const QString &roomid, const mtx::events::msg::CallCandidates &);
-- 
GitLab