From 5cc815b274e0022ab05b3c6a303e8852518039d2 Mon Sep 17 00:00:00 2001
From: Joseph Donofry <joedonofry@gmail.com>
Date: Fri, 5 Aug 2022 21:54:56 -0400
Subject: [PATCH 1/5] Add ability to respond to notifications on macOS

* Currently doesn't actually send a message, just logs it
---
 .clang-format                                |   3 +
 CMakeLists.txt                               |   4 +-
 src/main.cpp                                 |   5 +
 src/notifications/MacNotificationDelegate.h  |  10 +
 src/notifications/MacNotificationDelegate.mm |  26 +++
 src/notifications/Manager.h                  |   8 +-
 src/notifications/ManagerMac.cpp             |  37 +++-
 src/notifications/ManagerMac.mm              | 217 ++++++++++++-------
 8 files changed, 224 insertions(+), 86 deletions(-)
 create mode 100644 src/notifications/MacNotificationDelegate.h
 create mode 100644 src/notifications/MacNotificationDelegate.mm

diff --git a/.clang-format b/.clang-format
index e9ebcb6df..adb431771 100644
--- a/.clang-format
+++ b/.clang-format
@@ -13,3 +13,6 @@ KeepEmptyLinesAtTheStartOfBlocks: false
 PointerAlignment: Right
 Cpp11BracedListStyle: true
 PenaltyReturnTypeOnItsOwnLine: 0
+---
+BasedOnStyle: WebKit
+Language: ObjC
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2b456b1a9..8818fcaea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -629,9 +629,9 @@ set(TRANSLATION_DEPS ${LANG_QRC} ${QRC} ${QM_SRC})
 
 if (APPLE)
 	set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa -framework UserNotifications")
-	set(SRC_FILES ${SRC_FILES} src/notifications/ManagerMac.mm src/notifications/ManagerMac.cpp src/emoji/MacHelper.mm src/emoji/MacHelper.h)
+	set(SRC_FILES ${SRC_FILES} src/notifications/MacNotificationDelegate.h src/notifications/MacNotificationDelegate.mm src/notifications/ManagerMac.mm src/notifications/ManagerMac.cpp src/emoji/MacHelper.mm src/emoji/MacHelper.h)
 	if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
-		set_source_files_properties( src/notifications/ManagerMac.mm src/emoji/MacHelper.mm src/emoji/MacHelper.h PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
+		set_source_files_properties( src/notifications/MacNotificationDelegate.h src/notifications/MacNotificationDelegate.mm src/notifications/ManagerMac.mm src/emoji/MacHelper.mm src/emoji/MacHelper.h PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
 	endif()
 elseif (WIN32)
 	file(DOWNLOAD
diff --git a/src/main.cpp b/src/main.cpp
index 3937c6b77..d1b4b7699 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -33,6 +33,7 @@
 
 #if defined(Q_OS_MAC)
 #include "emoji/MacHelper.h"
+#include "notifications/Manager.h"
 #endif
 
 #if defined(GSTREAMER_AVAILABLE) && (defined(Q_OS_MAC) || defined(Q_OS_WINDOWS))
@@ -389,6 +390,10 @@ main(int argc, char *argv[])
     // Temporary solution for the emoji picker until
     // nheko has a proper menu bar with more functionality.
     MacHelper::initializeMenus();
+
+    // Need to set up notification delegate so users can respond to messages from within the
+    // notification itself.
+    NotificationsManager::attachToMacNotifCenter();
 #endif
 
     nhlog::ui()->info("starting nheko {}", nheko::version);
diff --git a/src/notifications/MacNotificationDelegate.h b/src/notifications/MacNotificationDelegate.h
new file mode 100644
index 000000000..8441d1981
--- /dev/null
+++ b/src/notifications/MacNotificationDelegate.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "notifications/Manager.h"
+#include <mtx/responses/notifications.hpp>
+
+#import <Foundation/Foundation.h>
+#import <UserNotifications/UserNotifications.h>
+
+@interface MacNotificationDelegate : NSObject <UNUserNotificationCenterDelegate>
+@end
diff --git a/src/notifications/MacNotificationDelegate.mm b/src/notifications/MacNotificationDelegate.mm
new file mode 100644
index 000000000..f81a29d15
--- /dev/null
+++ b/src/notifications/MacNotificationDelegate.mm
@@ -0,0 +1,26 @@
+#import "notifications/MacNotificationDelegate.h"
+
+@implementation MacNotificationDelegate
+
+- (void)userNotificationCenter:(UNUserNotificationCenter*)center
+    didReceiveNotificationResponse:(UNNotificationResponse*)response
+             withCompletionHandler:(void (^)())completionHandler
+{
+    if ([response.actionIdentifier isEqualToString:@"ReplyAction"]) {
+        if ([response respondsToSelector:@selector(userText)]) {
+            NSString* textValue = [(UNTextInputNotificationResponse*)response userText];
+            NSLog(@"%@", textValue);
+        }
+    }
+    completionHandler();
+}
+
+- (void)userNotificationCenter:(UNUserNotificationCenter*)center
+       willPresentNotification:(UNNotification*)notification
+         withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
+{
+
+    completionHandler(UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound);
+}
+
+@end
\ No newline at end of file
diff --git a/src/notifications/Manager.h b/src/notifications/Manager.h
index 8a5f17254..de678738c 100644
--- a/src/notifications/Manager.h
+++ b/src/notifications/Manager.h
@@ -78,7 +78,13 @@ private:
                                 const QString &event_id,
                                 const QString &subtitle,
                                 const QString &informativeText,
-                                const QString &bodyImagePath);
+                                const QString &bodyImagePath,
+                                const QString &respondStr,
+                                const QString &sendStr,
+                                const QString &placeholder);
+
+public:
+    static void attachToMacNotifCenter();
 #endif
 
 #if defined(Q_OS_WINDOWS)
diff --git a/src/notifications/ManagerMac.cpp b/src/notifications/ManagerMac.cpp
index d5faaf59d..75ea838cb 100644
--- a/src/notifications/ManagerMac.cpp
+++ b/src/notifications/ManagerMac.cpp
@@ -40,12 +40,20 @@ NotificationsManager::postNotification(const mtx::responses::Notification &notif
     const auto isEncrypted = std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
                                &notification.event) != nullptr;
     const auto isReply = utils::isReply(notification.event);
+
+    // Putting these here to pass along since I'm not sure how
+    // our translate step interacts with .mm files
+    const auto respondStr  = QObject::tr("Respond");
+    const auto sendStr     = QObject::tr("Send");
+    const auto placeholder = QObject::tr("Write a message...");
+
     if (isEncrypted) {
         // TODO: decrypt this message if the decryption setting is on in the UserSettings
         const QString messageInfo = (isReply ? tr("%1 replied with an encrypted message")
                                              : tr("%1 sent an encrypted message"))
                                       .arg(sender);
-        objCxxPostNotification(room_name, room_id, event_id, messageInfo, "", "");
+        objCxxPostNotification(
+          room_name, room_id, event_id, messageInfo, "", "", respondStr, sendStr, placeholder);
     } else {
         const QString messageInfo =
           (isReply ? tr("%1 replied to a message") : tr("%1 sent a message")).arg(sender);
@@ -53,17 +61,34 @@ NotificationsManager::postNotification(const mtx::responses::Notification &notif
             MxcImageProvider::download(
               QString::fromStdString(mtx::accessors::url(notification.event)).remove("mxc://"),
               QSize(200, 80),
-              [this, notification, room_name, room_id, event_id, messageInfo](
-                QString, QSize, QImage, QString imgPath) {
+              [this,
+               notification,
+               room_name,
+               room_id,
+               event_id,
+               messageInfo,
+               respondStr,
+               sendStr,
+               placeholder](QString, QSize, QImage, QString imgPath) {
                   objCxxPostNotification(room_name,
                                          room_id,
                                          event_id,
                                          messageInfo,
                                          formatNotification(notification),
-                                         imgPath);
+                                         imgPath,
+                                         respondStr,
+                                         sendStr,
+                                         placeholder);
               });
         else
-            objCxxPostNotification(
-              room_name, room_id, event_id, messageInfo, formatNotification(notification), "");
+            objCxxPostNotification(room_name,
+                                   room_id,
+                                   event_id,
+                                   messageInfo,
+                                   formatNotification(notification),
+                                   "",
+                                   respondStr,
+                                   sendStr,
+                                   placeholder);
     }
 }
diff --git a/src/notifications/ManagerMac.mm b/src/notifications/ManagerMac.mm
index d5d900a7e..7d7618179 100644
--- a/src/notifications/ManagerMac.mm
+++ b/src/notifications/ManagerMac.mm
@@ -1,112 +1,175 @@
+#include "notifications/MacNotificationDelegate.h"
 #include "notifications/Manager.h"
 
-#import <Foundation/Foundation.h>
 #import <AppKit/NSImage.h>
+#import <Foundation/Foundation.h>
 #import <UserNotifications/UserNotifications.h>
 
-#include <QtMac>
 #include <QImage>
+#include <QtMac>
 
 @interface UNNotificationAttachment (UNNotificationAttachmentAdditions)
-    + (UNNotificationAttachment *) createFromImageData:(NSData*)imgData identifier:(NSString *)imageFileIdentifier options:(NSDictionary*)attachmentOptions;
++ (UNNotificationAttachment*)createFromImageData:(NSData*)imgData
+                                      identifier:(NSString*)imageFileIdentifier
+                                         options:
+                                             (NSDictionary*)attachmentOptions;
 @end
 
 @implementation UNNotificationAttachment (UNNotificationAttachmentAdditions)
-    + (UNNotificationAttachment *) createFromImageData:(NSData*)imgData identifier:(NSString *)imageFileIdentifier options:(NSDictionary*)attachmentOptions {
-        NSFileManager *fileManager = [NSFileManager defaultManager];
-        NSString *tmpSubFolderName = [[NSProcessInfo processInfo] globallyUniqueString];
-        NSURL *tmpSubFolderURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:tmpSubFolderName] isDirectory:true];
-        NSError *error = nil;
-        [fileManager createDirectoryAtURL:tmpSubFolderURL withIntermediateDirectories:true attributes:nil error:&error];
-        if(error) {
-            NSLog(@"%@",[error localizedDescription]);
-            return nil;
-        }
-        NSURL *fileURL = [tmpSubFolderURL URLByAppendingPathComponent:imageFileIdentifier];
-        [imgData writeToURL:fileURL atomically:true];
-        UNNotificationAttachment *imageAttachment = [UNNotificationAttachment attachmentWithIdentifier:@"" URL:fileURL options:attachmentOptions error:&error];
-        if(error) {
-            NSLog(@"%@",[error localizedDescription]);
-            return nil;
-        }
-        return imageAttachment;
-
++ (UNNotificationAttachment*)createFromImageData:(NSData*)imgData
+                                      identifier:(NSString*)imageFileIdentifier
+                                         options:
+                                             (NSDictionary*)attachmentOptions
+{
+    NSFileManager* fileManager = [NSFileManager defaultManager];
+    NSString* tmpSubFolderName =
+        [[NSProcessInfo processInfo] globallyUniqueString];
+    NSURL* tmpSubFolderURL = [NSURL
+        fileURLWithPath:[NSTemporaryDirectory()
+                            stringByAppendingPathComponent:tmpSubFolderName]
+            isDirectory:true];
+    NSError* error = nil;
+    [fileManager createDirectoryAtURL:tmpSubFolderURL
+          withIntermediateDirectories:true
+                           attributes:nil
+                                error:&error];
+    if (error) {
+        NSLog(@"%@", [error localizedDescription]);
+        return nil;
+    }
+    NSURL* fileURL =
+        [tmpSubFolderURL URLByAppendingPathComponent:imageFileIdentifier];
+    [imgData writeToURL:fileURL atomically:true];
+    UNNotificationAttachment* imageAttachment =
+        [UNNotificationAttachment attachmentWithIdentifier:@""
+                                                       URL:fileURL
+                                                   options:attachmentOptions
+                                                     error:&error];
+    if (error) {
+        NSLog(@"%@", [error localizedDescription]);
+        return nil;
     }
+    return imageAttachment;
+}
 @end
 
-NotificationsManager::NotificationsManager(QObject *parent): QObject(parent)
+NotificationsManager::NotificationsManager(QObject* parent)
+    : QObject(parent)
 {
-
 }
 
-void
-NotificationsManager::objCxxPostNotification(const QString &room_name,
-                                             const QString &room_id,
-                                             const QString &event_id,
-                                             const QString &subtitle,
-                                             const QString &informativeText,
-                                             const QString &bodyImagePath)
+void NotificationsManager::objCxxPostNotification(
+    const QString& room_name,
+    const QString& room_id,
+    const QString& event_id,
+    const QString& subtitle,
+    const QString& informativeText,
+    const QString& bodyImagePath,
+    const QString& respondStr,
+    const QString& sendStr,
+    const QString& placeholder)
 {
+    // Request permissions for alerts (the generic type of notification), sound playback,
+    // and badges (which allows the Nheko app icon to show the little red bubble with unread count).
+    // NOTE: Possible macOS bug... the 'Play sound for notification checkbox' doesn't appear in
+    // the Notifications and Focus settings unless UNAuthorizationOptionBadges is also
+    // specified
     UNAuthorizationOptions options = UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge;
-    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
+    UNUserNotificationCenter* center =
+        [UNUserNotificationCenter currentNotificationCenter];
 
+    // TODO: Move this somewhere that isn't dependent on receiving a notification
+    // to actually request notification access.
     [center requestAuthorizationWithOptions:options
-    completionHandler:^(BOOL granted, NSError * _Nullable error) {
-        if (!granted) {
-            NSLog(@"No notification access");
-            if (error) {
-                NSLog(@"%@",[error localizedDescription]);
+                          completionHandler:^(BOOL granted,
+                              NSError* _Nullable error) {
+                              if (!granted) {
+                                  NSLog(@"No notification access");
+                                  if (error) {
+                                      NSLog(@"%@", [error localizedDescription]);
+                                  }
+                              }
+                          }];
+
+    UNTextInputNotificationAction* replyAction = [UNTextInputNotificationAction actionWithIdentifier:@"ReplyAction"
+                                                                                               title:respondStr.toNSString()
+                                                                                             options:UNNotificationActionOptionNone
+                                                                                textInputButtonTitle:sendStr.toNSString()
+                                                                                textInputPlaceholder:placeholder.toNSString()];
+
+    UNNotificationCategory* category = [UNNotificationCategory categoryWithIdentifier:@"ReplyCategory"
+                                                                              actions:@[ replyAction ]
+                                                                    intentIdentifiers:@[]
+                                                                              options:UNNotificationCategoryOptionNone];
+
+    NSString* title = room_name.toNSString();
+    NSString* sub = subtitle.toNSString();
+    NSString* body = informativeText.toNSString();
+    NSString* threadIdentifier = room_id.toNSString();
+    NSString* identifier = event_id.toNSString();
+    NSString* imgUrl = bodyImagePath.toNSString();
+
+    NSSet* categories = [NSSet setWithObject:category];
+    [center setNotificationCategories:categories];
+    [center getNotificationSettingsWithCompletionHandler:^(
+        UNNotificationSettings* _Nonnull settings) {
+        if (settings.authorizationStatus == UNAuthorizationStatusAuthorized) {
+            UNMutableNotificationContent* content =
+                [[UNMutableNotificationContent alloc] init];
+
+            content.title = title;
+            content.subtitle = sub;
+            content.body = body;
+            content.sound = [UNNotificationSound defaultSound];
+            content.threadIdentifier = threadIdentifier;
+            content.categoryIdentifier = @"ReplyCategory";
+
+            if ([imgUrl length] != 0) {
+                NSURL* imageURL = [NSURL fileURLWithPath:imgUrl];
+                NSData* img = [NSData dataWithContentsOfURL:imageURL];
+                NSArray* attachments = [NSMutableArray array];
+                UNNotificationAttachment* attachment = [UNNotificationAttachment
+                    createFromImageData:img
+                             identifier:@"attachment_image.jpeg"
+                                options:nil];
+                if (attachment) {
+                    attachments = [NSMutableArray arrayWithObjects:attachment, nil];
+                    content.attachments = attachments;
+                }
             }
-        }
-    }];
 
-    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
-
-    content.title             = room_name.toNSString();
-    content.subtitle          = subtitle.toNSString();
-    content.body              = informativeText.toNSString();
-    content.sound             = [UNNotificationSound defaultSound];
-    content.threadIdentifier  = room_id.toNSString();
-
-    if (!bodyImagePath.isEmpty()) {
-        NSURL *imageURL = [NSURL fileURLWithPath:bodyImagePath.toNSString()];
-        NSData *img = [NSData dataWithContentsOfURL:imageURL];
-        NSArray *attachments = [NSMutableArray array];
-        UNNotificationAttachment *attachment = [UNNotificationAttachment createFromImageData:img identifier:@"attachment_image.jpeg" options:nil];
-        if (attachment) {
-            attachments = [NSMutableArray arrayWithObjects: attachment, nil];
-            content.attachments = attachments;
-        }
-    }
+            UNNotificationRequest* notificationRequest =
+                [UNNotificationRequest requestWithIdentifier:identifier
+                                                     content:content
+                                                     trigger:nil];
 
-    UNNotificationRequest *notificationRequest = [UNNotificationRequest requestWithIdentifier:event_id.toNSString() content:content trigger:nil];
+            [center addNotificationRequest:notificationRequest
+                     withCompletionHandler:^(NSError* _Nullable error) {
+                         if (error != nil) {
+                             NSLog(@"Unable to Add Notification Request: %@", [error localizedDescription]);
+                         }
+                     }];
 
-    [center addNotificationRequest:notificationRequest withCompletionHandler:^(NSError * _Nullable error) {
-        if (error != nil) {
-            NSLog(@"Unable to Add Notification Request");
+            [content autorelease];
         }
     }];
-
-    [content autorelease];
 }
 
-//unused
-void
-NotificationsManager::actionInvoked(uint, QString)
+void NotificationsManager::attachToMacNotifCenter()
 {
-}
+    UNUserNotificationCenter* center =
+        [UNUserNotificationCenter currentNotificationCenter];
 
-void
-NotificationsManager::notificationReplied(uint, QString)
-{
-}
+    MacNotificationDelegate* notifDelegate = [MacNotificationDelegate new];
 
-void
-NotificationsManager::notificationClosed(uint, uint)
-{
+    center.delegate = notifDelegate;
 }
 
-void
-NotificationsManager::removeNotification(const QString &, const QString &)
-{}
+// unused
+void NotificationsManager::actionInvoked(uint, QString) { }
+
+void NotificationsManager::notificationReplied(uint, QString) { }
+
+void NotificationsManager::notificationClosed(uint, uint) { }
 
+void NotificationsManager::removeNotification(const QString&, const QString&) { }
-- 
GitLab


From 04b40a3c5a3efc63cd75ce94bd8a0edaedf9f11a Mon Sep 17 00:00:00 2001
From: Joseph Donofry <joedonofry@gmail.com>
Date: Fri, 5 Aug 2022 22:03:06 -0400
Subject: [PATCH 2/5] Add license headers to files

---
 src/notifications/MacNotificationDelegate.h  | 5 +++++
 src/notifications/MacNotificationDelegate.mm | 5 +++++
 src/notifications/ManagerMac.mm              | 5 +++++
 3 files changed, 15 insertions(+)

diff --git a/src/notifications/MacNotificationDelegate.h b/src/notifications/MacNotificationDelegate.h
index 8441d1981..448ca2cb5 100644
--- a/src/notifications/MacNotificationDelegate.h
+++ b/src/notifications/MacNotificationDelegate.h
@@ -1,3 +1,8 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+// SPDX-FileCopyrightText: 2022 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
 #pragma once
 
 #include "notifications/Manager.h"
diff --git a/src/notifications/MacNotificationDelegate.mm b/src/notifications/MacNotificationDelegate.mm
index f81a29d15..b1969386b 100644
--- a/src/notifications/MacNotificationDelegate.mm
+++ b/src/notifications/MacNotificationDelegate.mm
@@ -1,3 +1,8 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+// SPDX-FileCopyrightText: 2022 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
 #import "notifications/MacNotificationDelegate.h"
 
 @implementation MacNotificationDelegate
diff --git a/src/notifications/ManagerMac.mm b/src/notifications/ManagerMac.mm
index 7d7618179..ea910dd02 100644
--- a/src/notifications/ManagerMac.mm
+++ b/src/notifications/ManagerMac.mm
@@ -1,3 +1,8 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+// SPDX-FileCopyrightText: 2022 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
 #include "notifications/MacNotificationDelegate.h"
 #include "notifications/Manager.h"
 
-- 
GitLab


From 32d6742647d5b5df04604bdcc13e07a451bca966 Mon Sep 17 00:00:00 2001
From: Joseph Donofry <joedonofry@gmail.com>
Date: Fri, 28 Oct 2022 14:16:47 -0400
Subject: [PATCH 3/5] Add implementation of replies via notification on macOS

---
 CMakeLists.txt                               |  4 ++--
 src/ChatPage.cpp                             | 24 ++++++++++++--------
 src/ChatPage.h                               |  1 +
 src/notifications/MacNotificationDelegate.h  |  7 +++++-
 src/notifications/MacNotificationDelegate.mm | 20 ++++++++++++++--
 src/notifications/ManagerMac.mm              |  9 +++++++-
 src/notifications/NotificationManagerProxy.h | 19 ++++++++++++++++
 7 files changed, 68 insertions(+), 16 deletions(-)
 create mode 100644 src/notifications/NotificationManagerProxy.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8818fcaea..7732a7357 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -629,9 +629,9 @@ set(TRANSLATION_DEPS ${LANG_QRC} ${QRC} ${QM_SRC})
 
 if (APPLE)
 	set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa -framework UserNotifications")
-	set(SRC_FILES ${SRC_FILES} src/notifications/MacNotificationDelegate.h src/notifications/MacNotificationDelegate.mm src/notifications/ManagerMac.mm src/notifications/ManagerMac.cpp src/emoji/MacHelper.mm src/emoji/MacHelper.h)
+	set(SRC_FILES ${SRC_FILES} src/notifications/NotificationManagerProxy.h src/notifications/MacNotificationDelegate.h src/notifications/MacNotificationDelegate.mm src/notifications/ManagerMac.mm src/notifications/ManagerMac.cpp src/emoji/MacHelper.mm src/emoji/MacHelper.h)
 	if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0")
-		set_source_files_properties( src/notifications/MacNotificationDelegate.h src/notifications/MacNotificationDelegate.mm src/notifications/ManagerMac.mm src/emoji/MacHelper.mm src/emoji/MacHelper.h PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
+		set_source_files_properties( src/notifications/NotificationManagerProxy.h src/notifications/MacNotificationDelegate.h src/notifications/MacNotificationDelegate.mm src/notifications/ManagerMac.mm src/emoji/MacHelper.mm src/emoji/MacHelper.h PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
 	endif()
 elseif (WIN32)
 	file(DOWNLOAD
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index e40274cb8..f87c27380 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -152,16 +152,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QObject *parent)
     connect(notificationsManager,
             &NotificationsManager::sendNotificationReply,
             this,
-            [this](const QString &roomid, const QString &eventid, const QString &body) {
-                view_manager_->queueReply(roomid, eventid, body);
-                auto exWin = MainWindow::instance()->windowForRoom(roomid);
-                if (exWin) {
-                    exWin->requestActivate();
-                } else {
-                    view_manager_->rooms()->setCurrentRoom(roomid);
-                    MainWindow::instance()->requestActivate();
-                }
-            });
+            &ChatPage::sendNotificationReply);
 
     connect(
       this,
@@ -1583,6 +1574,19 @@ ChatPage::handleMatrixUri(QString uri)
     return false;
 }
 
+void
+ChatPage::sendNotificationReply(const QString &roomid, const QString &eventid, const QString &body)
+{
+    view_manager_->queueReply(roomid, eventid, body);
+    auto exWin = MainWindow::instance()->windowForRoom(roomid);
+    if (exWin) {
+        exWin->requestActivate();
+    } else {
+        view_manager_->rooms()->setCurrentRoom(roomid);
+        MainWindow::instance()->requestActivate();
+    }
+}
+
 bool
 ChatPage::handleMatrixUri(const QUrl &uri)
 {
diff --git a/src/ChatPage.h b/src/ChatPage.h
index 1bb25dc20..bae4401f8 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -105,6 +105,7 @@ public slots:
     void receivedSessionKey(const std::string &room_id, const std::string &session_id);
     void decryptDownloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescription keyDesc,
                                   const SecretsToDecrypt &secrets);
+    void sendNotificationReply(const QString &roomid, const QString &eventid, const QString &body);
 signals:
     void connectionLost();
     void connectionRestored();
diff --git a/src/notifications/MacNotificationDelegate.h b/src/notifications/MacNotificationDelegate.h
index 448ca2cb5..4a5c66d00 100644
--- a/src/notifications/MacNotificationDelegate.h
+++ b/src/notifications/MacNotificationDelegate.h
@@ -6,10 +6,15 @@
 #pragma once
 
 #include "notifications/Manager.h"
+#include "notifications/NotificationManagerProxy.h"
 #include <mtx/responses/notifications.hpp>
 
 #import <Foundation/Foundation.h>
 #import <UserNotifications/UserNotifications.h>
 
-@interface MacNotificationDelegate : NSObject <UNUserNotificationCenterDelegate>
+@interface MacNotificationDelegate : NSObject <UNUserNotificationCenterDelegate> {
+  std::unique_ptr<NotificationManagerProxy> mProxy;
+}
+
+- (id)initWithProxy:(std::unique_ptr<NotificationManagerProxy>&&)proxy;
 @end
diff --git a/src/notifications/MacNotificationDelegate.mm b/src/notifications/MacNotificationDelegate.mm
index b1969386b..9047efe3f 100644
--- a/src/notifications/MacNotificationDelegate.mm
+++ b/src/notifications/MacNotificationDelegate.mm
@@ -5,16 +5,32 @@
 
 #import "notifications/MacNotificationDelegate.h"
 
+#include <QString.h>
+
+#include "ChatPage.h"
+
 @implementation MacNotificationDelegate
 
+- (id)initWithProxy: (std::unique_ptr<NotificationManagerProxy>&&)proxy
+{
+    if(self = [super init]) {
+        mProxy = std::move(proxy);
+    }
+
+    return self;
+}
+
 - (void)userNotificationCenter:(UNUserNotificationCenter*)center
     didReceiveNotificationResponse:(UNNotificationResponse*)response
              withCompletionHandler:(void (^)())completionHandler
 {
     if ([response.actionIdentifier isEqualToString:@"ReplyAction"]) {
         if ([response respondsToSelector:@selector(userText)]) {
-            NSString* textValue = [(UNTextInputNotificationResponse*)response userText];
-            NSLog(@"%@", textValue);
+            UNTextInputNotificationResponse* textResponse = (UNTextInputNotificationResponse*)response;
+            NSString* textValue = [textResponse userText];
+            NSString* eventId = [[[textResponse notification] request] identifier];
+            NSString* roomId = [[[[textResponse notification] request] content] threadIdentifier];
+            mProxy->notificationReplied(QString::fromNSString(roomId), QString::fromNSString(eventId), QString::fromNSString(textValue));
         }
     }
     completionHandler();
diff --git a/src/notifications/ManagerMac.mm b/src/notifications/ManagerMac.mm
index ea910dd02..4865e30d3 100644
--- a/src/notifications/ManagerMac.mm
+++ b/src/notifications/ManagerMac.mm
@@ -3,9 +3,12 @@
 //
 // SPDX-License-Identifier: GPL-3.0-or-later
 
+#include "notifications/NotificationManagerProxy.h"
 #include "notifications/MacNotificationDelegate.h"
 #include "notifications/Manager.h"
 
+#include "ChatPage.h"
+
 #import <AppKit/NSImage.h>
 #import <Foundation/Foundation.h>
 #import <UserNotifications/UserNotifications.h>
@@ -165,7 +168,11 @@ void NotificationsManager::attachToMacNotifCenter()
     UNUserNotificationCenter* center =
         [UNUserNotificationCenter currentNotificationCenter];
 
-    MacNotificationDelegate* notifDelegate = [MacNotificationDelegate new];
+    std::unique_ptr<NotificationManagerProxy> proxy = std::make_unique<NotificationManagerProxy>();
+
+    connect(proxy.get(), &NotificationManagerProxy::notificationReplied, ChatPage::instance(), &ChatPage::sendNotificationReply);
+
+    MacNotificationDelegate* notifDelegate = [[MacNotificationDelegate alloc] initWithProxy:std::move(proxy)];
 
     center.delegate = notifDelegate;
 }
diff --git a/src/notifications/NotificationManagerProxy.h b/src/notifications/NotificationManagerProxy.h
new file mode 100644
index 000000000..0341519df
--- /dev/null
+++ b/src/notifications/NotificationManagerProxy.h
@@ -0,0 +1,19 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+// SPDX-FileCopyrightText: 2022 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QObject>
+#include <QString>
+
+class NotificationManagerProxy final : public QObject
+{
+    Q_OBJECT
+public:
+    NotificationManagerProxy(QObject *parent = nullptr): QObject(parent) {}
+
+signals:
+    void notificationReplied(const QString &room, const QString &event, const QString &reply);
+};
\ No newline at end of file
-- 
GitLab


From 9677eaa0933109840287e61cb593c8b2b06e124d Mon Sep 17 00:00:00 2001
From: Joseph Donofry <joedonofry@gmail.com>
Date: Fri, 28 Oct 2022 14:20:42 -0400
Subject: [PATCH 4/5] Appease the lint gods

---
 src/MainWindow.h                             | 5 ++++-
 src/notifications/MacNotificationDelegate.h  | 2 +-
 src/notifications/NotificationManagerProxy.h | 5 ++++-
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/MainWindow.h b/src/MainWindow.h
index f567c93e9..60dbdb14f 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -57,7 +57,10 @@ public:
     void showChatPage();
 
 #ifdef NHEKO_DBUS_SYS
-    bool dbusAvailable() const { return dbusAvailable_; }
+    bool dbusAvailable() const
+    {
+        return dbusAvailable_;
+    }
 #endif
 
     Q_INVOKABLE void addPerRoomWindow(const QString &room, QWindow *window);
diff --git a/src/notifications/MacNotificationDelegate.h b/src/notifications/MacNotificationDelegate.h
index 4a5c66d00..e5bbe23bc 100644
--- a/src/notifications/MacNotificationDelegate.h
+++ b/src/notifications/MacNotificationDelegate.h
@@ -13,7 +13,7 @@
 #import <UserNotifications/UserNotifications.h>
 
 @interface MacNotificationDelegate : NSObject <UNUserNotificationCenterDelegate> {
-  std::unique_ptr<NotificationManagerProxy> mProxy;
+    std::unique_ptr<NotificationManagerProxy> mProxy;
 }
 
 - (id)initWithProxy:(std::unique_ptr<NotificationManagerProxy>&&)proxy;
diff --git a/src/notifications/NotificationManagerProxy.h b/src/notifications/NotificationManagerProxy.h
index 0341519df..c7a2e2348 100644
--- a/src/notifications/NotificationManagerProxy.h
+++ b/src/notifications/NotificationManagerProxy.h
@@ -12,7 +12,10 @@ class NotificationManagerProxy final : public QObject
 {
     Q_OBJECT
 public:
-    NotificationManagerProxy(QObject *parent = nullptr): QObject(parent) {}
+    NotificationManagerProxy(QObject *parent = nullptr)
+      : QObject(parent)
+    {
+    }
 
 signals:
     void notificationReplied(const QString &room, const QString &event, const QString &reply);
-- 
GitLab


From 9c67f05d3f3d132aa4233eee613fb556e83e82b2 Mon Sep 17 00:00:00 2001
From: Joseph Donofry <joedonofry@gmail.com>
Date: Fri, 28 Oct 2022 18:02:51 -0400
Subject: [PATCH 5/5] Use correct styling since my clang format is old

---
 src/MainWindow.h | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/src/MainWindow.h b/src/MainWindow.h
index 60dbdb14f..f567c93e9 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -57,10 +57,7 @@ public:
     void showChatPage();
 
 #ifdef NHEKO_DBUS_SYS
-    bool dbusAvailable() const
-    {
-        return dbusAvailable_;
-    }
+    bool dbusAvailable() const { return dbusAvailable_; }
 #endif
 
     Q_INVOKABLE void addPerRoomWindow(const QString &room, QWindow *window);
-- 
GitLab