From 029ae18a07c8c8692e7ae8836fcdb6ccfbe94e84 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 13 Feb 2021 13:48:37 -0500 Subject: Format markdown as HTML in notifications --- src/notifications/ManagerWin.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/notifications/ManagerWin.cpp') diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index 3152d84f..5a1a867c 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -10,6 +10,7 @@ #include "MatrixClient.h" #include "Utils.h" #include +#include using namespace WinToastLib; @@ -53,6 +54,7 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if cache::displayName(QString::fromStdString(notification.room_id), QString::fromStdString(mtx::accessors::sender(notification.event))); const auto text = utils::event_body(notification.event); + const auto formattedText = cmark_markdown_to_html(text.toStdString().c_str(), text.length(), CMARK_OPT_UNSAFE); if (!isInitialized) init(); @@ -66,10 +68,10 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if WinToastTemplate::FirstLine); if (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) templ.setTextField( - QString("* ").append(sender).append(" ").append(text).toStdWString(), + QString("* ").append(sender).append(" ").append(formattedText).toStdWString(), WinToastTemplate::SecondLine); else - templ.setTextField(QString("%1").arg(text).toStdWString(), + templ.setTextField(QString("%1").arg(formattedText).toStdWString(), WinToastTemplate::SecondLine); // TODO: implement room or user avatar // templ.setImagePath(L"C:/example.png"); -- cgit 1.5.1 From 8d3e463fa60d91ee0876c199bd3d0810e8074821 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 15 Feb 2021 07:57:25 -0500 Subject: Use plaintext for Windows notifications --- src/notifications/ManagerWin.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/notifications/ManagerWin.cpp') diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index 5a1a867c..2ddb48cc 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -5,6 +5,8 @@ #include "notifications/Manager.h" #include "wintoastlib.h" +#include + #include "Cache.h" #include "EventAccessors.h" #include "MatrixClient.h" @@ -54,7 +56,7 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if cache::displayName(QString::fromStdString(notification.room_id), QString::fromStdString(mtx::accessors::sender(notification.event))); const auto text = utils::event_body(notification.event); - const auto formattedText = cmark_markdown_to_html(text.toStdString().c_str(), text.length(), CMARK_OPT_UNSAFE); + const auto formattedText = QTextDocumentFragment::fromHtml(utils::markdownToHtml(text)).toPlainText(); if (!isInitialized) init(); -- cgit 1.5.1 From 648844089c1786f1439ea026d1f3f5d0b757a414 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 15 Feb 2021 18:36:10 -0500 Subject: Move data parsing into a dedicated function Actually posting the notification is now the responsibility of a private function --- CMakeLists.txt | 5 +++- src/notifications/Manager.cpp | 27 ++++++++++++++++++++ src/notifications/Manager.h | 10 ++++++++ src/notifications/ManagerLinux.cpp | 50 +++++++++++++++++--------------------- src/notifications/ManagerMac.cpp | 11 +++++++++ src/notifications/ManagerMac.mm | 30 +++++++++-------------- src/notifications/ManagerWin.cpp | 44 ++++++++++++++------------------- 7 files changed, 103 insertions(+), 74 deletions(-) create mode 100644 src/notifications/Manager.cpp create mode 100644 src/notifications/ManagerMac.cpp (limited to 'src/notifications/ManagerWin.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 905e6159..e0d9b5a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -297,6 +297,9 @@ set(SRC_FILES src/ui/UserProfile.cpp src/ui/RoomSettings.cpp + # Generic notification stuff + src/notifications/Manager.cpp + src/AvatarProvider.cpp src/BlurhashProvider.cpp src/Cache.cpp @@ -557,7 +560,7 @@ set(TRANSLATION_DEPS ${LANG_QRC} ${QRC} ${QM_SRC}) if (APPLE) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa") - set(SRC_FILES ${SRC_FILES} src/notifications/ManagerMac.mm src/emoji/MacHelper.mm) + set(SRC_FILES ${SRC_FILES} src/notifications/ManagerMac.mm src/notifications/ManagerMac.cpp src/emoji/MacHelper.mm) if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.16.0") set_source_files_properties( src/notifications/ManagerMac.mm src/emoji/MacHelper.mm PROPERTIES SKIP_PRECOMPILE_HEADERS ON) endif() diff --git a/src/notifications/Manager.cpp b/src/notifications/Manager.cpp new file mode 100644 index 00000000..03ce345f --- /dev/null +++ b/src/notifications/Manager.cpp @@ -0,0 +1,27 @@ +#include "notifications/Manager.h" + +#include "Cache.h" +#include "EventAccessors.h" +#include "Utils.h" +#include + +void +NotificationsManager::postNotification(const mtx::responses::Notification ¬ification, + const QImage &icon) +{ + const auto room_id = QString::fromStdString(notification.room_id); + const auto event_id = QString::fromStdString(mtx::accessors::event_id(notification.event)); + const auto room_name = + QString::fromStdString(cache::singleRoomInfo(notification.room_id).name); + const auto sender = cache::displayName( + room_id, QString::fromStdString(mtx::accessors::sender(notification.event))); + + QString text; + if (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) + text = + "* " + sender + " " + formatNotification(utils::event_body(notification.event)); + else + text = sender + ":" + formatNotification(utils::event_body(notification.event)); + + systemPostNotification(room_id, event_id, room_name, sender, text, icon); +} diff --git a/src/notifications/Manager.h b/src/notifications/Manager.h index e2b3236a..372e4998 100644 --- a/src/notifications/Manager.h +++ b/src/notifications/Manager.h @@ -42,6 +42,16 @@ signals: public slots: void removeNotification(const QString &roomId, const QString &eventId); +private: + void systemPostNotification(const QString &room_id, + const QString &event_id, + const QString &roomName, + const QString &sender, + const QString &text, + const QImage &icon); + + QString formatNotification(const QString &text); + #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_HAIKU) public: void closeNotifications(QString roomId); diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp index e81500a0..35975f53 100644 --- a/src/notifications/ManagerLinux.cpp +++ b/src/notifications/ManagerLinux.cpp @@ -9,12 +9,7 @@ #include #include -#include "Cache.h" -#include "EventAccessors.h" -#include "MatrixClient.h" #include "Utils.h" -#include -#include NotificationsManager::NotificationsManager(QObject *parent) : QObject(parent) @@ -52,35 +47,24 @@ NotificationsManager::NotificationsManager(QObject *parent) // SPDX-License-Identifier: GPL-3.0-or-later void -NotificationsManager::postNotification(const mtx::responses::Notification ¬ification, - const QImage &icon) +NotificationsManager::systemPostNotification(const QString &room_id, + const QString &event_id, + const QString &roomName, + const QString &sender, + const QString &text, + const QImage &icon) { - const auto room_id = QString::fromStdString(notification.room_id); - const auto event_id = QString::fromStdString(mtx::accessors::event_id(notification.event)); - const auto sender = cache::displayName( - room_id, QString::fromStdString(mtx::accessors::sender(notification.event))); - const auto text = utils::event_body(notification.event); - auto formattedText = utils::markdownToHtml(text); - - auto capabilites = dbus.call("GetCapabilites"); - if (!capabilites.arguments().contains("body-markup")) - formattedText = QTextDocumentFragment::fromHtml(formattedText).toPlainText(); + Q_UNUSED(sender) QVariantMap hints; hints["image-data"] = icon; hints["sound-name"] = "message-new-instant"; QList argumentList; - argumentList << "nheko"; // app_name - argumentList << (uint)0; // replace_id - argumentList << ""; // app_icon - argumentList << QString::fromStdString( - cache::singleRoomInfo(notification.room_id).name); // summary - - // body - if (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) - argumentList << "* " + sender + " " + formattedText; - else - argumentList << sender + ": " + formattedText; + argumentList << "nheko"; // app_name + argumentList << (uint)0; // replace_id + argumentList << ""; // app_icon + argumentList << roomName; // 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. @@ -167,6 +151,16 @@ NotificationsManager::notificationClosed(uint id, uint reason) notificationIds.remove(id); } +QString +NotificationsManager::formatNotification(const QString &text) +{ + static auto capabilites = dbus.call("GetCapabilites"); + if (capabilites.arguments().contains("body-markup")) + return utils::markdownToHtml(text); + else + return QTextDocumentFragment::fromHtml(utils::markdownToHtml(text)).toPlainText(); +} + /** * Automatic marshaling of a QImage for org.freedesktop.Notifications.Notify * diff --git a/src/notifications/ManagerMac.cpp b/src/notifications/ManagerMac.cpp new file mode 100644 index 00000000..41aea0d1 --- /dev/null +++ b/src/notifications/ManagerMac.cpp @@ -0,0 +1,11 @@ +#include "Manager.h" + +#include + +#include "Utils.h" + +QString +NotificationsManager::formatNotification(const QString &text) +{ + return utils::markdownToHtml(text); +} diff --git a/src/notifications/ManagerMac.mm b/src/notifications/ManagerMac.mm index af0b5a02..3372c5af 100644 --- a/src/notifications/ManagerMac.mm +++ b/src/notifications/ManagerMac.mm @@ -3,13 +3,6 @@ #include #include -#include "Cache.h" -#include "EventAccessors.h" -#include "MatrixClient.h" -#include "Utils.h" -#include -#include - @interface NSUserNotification (CFIPrivate) - (void)set_identityImage:(NSImage *)image; @end @@ -20,23 +13,22 @@ NotificationsManager::NotificationsManager(QObject *parent): QObject(parent) } void -NotificationsManager::postNotification(const mtx::responses::Notification ¬ification, - const QImage &icon) +NotificationsManager::systemPostNotification(const QString &room_id, + const QString &event_id, + const QString &roomName, + const QString &sender, + const QString &text, + const QImage &icon) { - Q_UNUSED(icon); - - const auto sender = cache::displayName(QString::fromStdString(notification.room_id), QString::fromStdString(mtx::accessors::sender(notification.event))); - const auto text = utils::event_body(notification.event); - const auto formattedText = cmark_markdown_to_html(text.toStdString().c_str(), text.length(), CMARK_OPT_UNSAFE); + Q_UNUSED(room_id) + Q_UNUSED(event_id) + Q_UNUSED(icon) NSUserNotification * notif = [[NSUserNotification alloc] init]; - notif.title = QString::fromStdString(cache::singleRoomInfo(notification.room_id).name).toNSString(); + notif.title = roomName.toNSString(); notif.subtitle = QString("%1 sent a message").arg(sender).toNSString(); - if (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) - notif.informativeText = QString("* ").append(sender).append(" ").append(formattedText).toNSString(); - else - notif.informativeText = formattedText.toNSString(); + notif.informativeText = text.toNSString(); notif.soundName = NSUserNotificationDefaultSoundName; [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification: notif]; diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index 2ddb48cc..42289b84 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -7,12 +7,7 @@ #include -#include "Cache.h" -#include "EventAccessors.h" -#include "MatrixClient.h" #include "Utils.h" -#include -#include using namespace WinToastLib; @@ -45,36 +40,27 @@ NotificationsManager::NotificationsManager(QObject *parent) {} void -NotificationsManager::postNotification(const mtx::responses::Notification ¬ification, - const QImage &icon) +NotificationsManager::systemPostNotification(const QString &room_id, + const QString &event_id, + const QString &roomName, + const QString &sender, + const QString &text, + const QImage &icon) { + Q_UNUSED(room_id) + Q_UNUSED(event_id) Q_UNUSED(icon) - const auto room_name = - QString::fromStdString(cache::singleRoomInfo(notification.room_id).name); - const auto sender = - cache::displayName(QString::fromStdString(notification.room_id), - QString::fromStdString(mtx::accessors::sender(notification.event))); - const auto text = utils::event_body(notification.event); - const auto formattedText = QTextDocumentFragment::fromHtml(utils::markdownToHtml(text)).toPlainText(); - if (!isInitialized) init(); auto templ = WinToastTemplate(WinToastTemplate::ImageAndText02); - if (room_name != sender) - templ.setTextField(QString("%1 - %2").arg(sender).arg(room_name).toStdWString(), - WinToastTemplate::FirstLine); - else - templ.setTextField(QString("%1").arg(sender).toStdWString(), + if (roomName != sender) + templ.setTextField(QString("%1 - %2").arg(sender).arg(roomName).toStdWString(), WinToastTemplate::FirstLine); - if (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) - templ.setTextField( - QString("* ").append(sender).append(" ").append(formattedText).toStdWString(), - WinToastTemplate::SecondLine); else - templ.setTextField(QString("%1").arg(formattedText).toStdWString(), - WinToastTemplate::SecondLine); + templ.setTextField(sender.toStdWString(), WinToastTemplate::FirstLine); + templ.setTextField(text.toStdWString(), WinToastTemplate::SecondLine); // TODO: implement room or user avatar // templ.setImagePath(L"C:/example.png"); @@ -89,3 +75,9 @@ void NotificationsManager::notificationClosed(uint, uint) {} void NotificationsManager::removeNotification(const QString &, const QString &) {} + +QString +NotificationsManager::formatNotification(const QString &text) +{ + return QTextDocumentFragment::fromHtml(utils::markdownToHtml(text)).toPlainText(); +} -- cgit 1.5.1 From 01bbec88dd8c409ec339e7adf4fe2657c05299b6 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 15 Feb 2021 19:30:04 -0500 Subject: Don't run markdownToHtml on messages --- src/notifications/ManagerLinux.cpp | 4 ++-- src/notifications/ManagerMac.cpp | 2 +- src/notifications/ManagerWin.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/notifications/ManagerWin.cpp') diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp index 35975f53..df4722be 100644 --- a/src/notifications/ManagerLinux.cpp +++ b/src/notifications/ManagerLinux.cpp @@ -156,9 +156,9 @@ NotificationsManager::formatNotification(const QString &text) { static auto capabilites = dbus.call("GetCapabilites"); if (capabilites.arguments().contains("body-markup")) - return utils::markdownToHtml(text); + return text; else - return QTextDocumentFragment::fromHtml(utils::markdownToHtml(text)).toPlainText(); + return QTextDocumentFragment::fromHtml(text).toPlainText(); } /** diff --git a/src/notifications/ManagerMac.cpp b/src/notifications/ManagerMac.cpp index 04044d86..c75d2283 100644 --- a/src/notifications/ManagerMac.cpp +++ b/src/notifications/ManagerMac.cpp @@ -7,5 +7,5 @@ QString NotificationsManager::formatNotification(const QString &text) { - return QTextDocumentFragment::fromHtml(utils::markdownToHtml(text)).toPlainText(); + return QTextDocumentFragment::fromHtml(text).toPlainText(); } diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index 42289b84..3ab85a5f 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -79,5 +79,5 @@ NotificationsManager::removeNotification(const QString &, const QString &) QString NotificationsManager::formatNotification(const QString &text) { - return QTextDocumentFragment::fromHtml(utils::markdownToHtml(text)).toPlainText(); + return QTextDocumentFragment::fromHtml(text).toPlainText(); } -- cgit 1.5.1 From df998ef67194bdd633df163eef53ec628fc8e090 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 20 Feb 2021 13:16:43 -0500 Subject: Get event text in event parser function --- src/notifications/Manager.cpp | 2 +- src/notifications/Manager.h | 2 +- src/notifications/ManagerLinux.cpp | 9 ++++++--- src/notifications/ManagerMac.cpp | 5 +++-- src/notifications/ManagerWin.cpp | 6 ++++-- 5 files changed, 15 insertions(+), 9 deletions(-) (limited to 'src/notifications/ManagerWin.cpp') diff --git a/src/notifications/Manager.cpp b/src/notifications/Manager.cpp index eee695a5..e1f59cb1 100644 --- a/src/notifications/Manager.cpp +++ b/src/notifications/Manager.cpp @@ -27,7 +27,7 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if ((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) ? "* " + sender + " " : sender + reply + ": ") + - formatNotification(mtx::accessors::formattedBodyWithFallback(notification.event)); + formatNotification(notification.event); systemPostNotification(room_id, event_id, room_name, sender, text, icon); } diff --git a/src/notifications/Manager.h b/src/notifications/Manager.h index 372e4998..449a609f 100644 --- a/src/notifications/Manager.h +++ b/src/notifications/Manager.h @@ -50,7 +50,7 @@ private: const QString &text, const QImage &icon); - QString formatNotification(const QString &text); + QString formatNotification(const mtx::events::collections::TimelineEvents &e); #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_HAIKU) public: diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp index ab4a6d93..202f2a9d 100644 --- a/src/notifications/ManagerLinux.cpp +++ b/src/notifications/ManagerLinux.cpp @@ -11,6 +11,7 @@ #include +#include "EventAccessors.h" #include "Utils.h" NotificationsManager::NotificationsManager(QObject *parent) @@ -161,7 +162,7 @@ NotificationsManager::notificationClosed(uint id, uint reason) * specified at https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/Markup/ */ QString -NotificationsManager::formatNotification(const QString &text) +NotificationsManager::formatNotification(const mtx::events::collections::TimelineEvents &e) { static const auto hasMarkup = std::invoke([this]() -> bool { for (auto x : dbus.call("GetCapabilities").arguments()) @@ -169,14 +170,16 @@ NotificationsManager::formatNotification(const QString &text) return true; return false; }); + if (hasMarkup) - return QString(text) + return mtx::accessors::formattedBodyWithFallback(e) .replace("", "") .replace("", "") .replace("", "") .replace("", ""); - return QTextDocumentFragment::fromHtml(text).toPlainText(); + return QTextDocumentFragment::fromHtml(mtx::accessors::formattedBodyWithFallback(e)) + .toPlainText(); } /** diff --git a/src/notifications/ManagerMac.cpp b/src/notifications/ManagerMac.cpp index c75d2283..a74df2c7 100644 --- a/src/notifications/ManagerMac.cpp +++ b/src/notifications/ManagerMac.cpp @@ -2,10 +2,11 @@ #include +#include "EventAccessors.h" #include "Utils.h" QString -NotificationsManager::formatNotification(const QString &text) +NotificationsManager::formatNotification(const mtx::events::collections::TimelineEvents &e) { - return QTextDocumentFragment::fromHtml(text).toPlainText(); + return QTextDocumentFragment::fromHtml(mtx::accessors::formattedBodyWithFallback(e)).toPlainText(); } diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index 3ab85a5f..47b4c178 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -7,6 +7,7 @@ #include +#include "EventAccessors.h" #include "Utils.h" using namespace WinToastLib; @@ -77,7 +78,8 @@ NotificationsManager::removeNotification(const QString &, const QString &) {} QString -NotificationsManager::formatNotification(const QString &text) +NotificationsManager::formatNotification(const mtx::events::collections::TimelineEvents &e) { - return QTextDocumentFragment::fromHtml(text).toPlainText(); + return QTextDocumentFragment::fromHtml(mtx::accessors::formattedBodyWithFallback(e)).toPlainText(); } + -- cgit 1.5.1 From 37acdad92822c3d56026bfc425a4576152d89a4e Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 20 Feb 2021 14:00:13 -0500 Subject: Add regex to remove replies in notifications --- src/notifications/ManagerLinux.cpp | 8 ++++++-- src/notifications/ManagerMac.cpp | 6 +++++- src/notifications/ManagerWin.cpp | 7 +++++-- 3 files changed, 16 insertions(+), 5 deletions(-) (limited to 'src/notifications/ManagerWin.cpp') diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp index 202f2a9d..9bcda1b2 100644 --- a/src/notifications/ManagerLinux.cpp +++ b/src/notifications/ManagerLinux.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -176,9 +177,12 @@ NotificationsManager::formatNotification(const mtx::events::collections::Timelin .replace("", "") .replace("", "") .replace("", "") - .replace("", ""); + .replace("", "") + .replace(QRegularExpression("(.+\\<\\/mx-reply\\>)"), ""); - return QTextDocumentFragment::fromHtml(mtx::accessors::formattedBodyWithFallback(e)) + return QTextDocumentFragment::fromHtml( + mtx::accessors::formattedBodyWithFallback(e).replace( + QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) .toPlainText(); } diff --git a/src/notifications/ManagerMac.cpp b/src/notifications/ManagerMac.cpp index a74df2c7..c9678638 100644 --- a/src/notifications/ManagerMac.cpp +++ b/src/notifications/ManagerMac.cpp @@ -1,5 +1,6 @@ #include "Manager.h" +#include #include #include "EventAccessors.h" @@ -8,5 +9,8 @@ QString NotificationsManager::formatNotification(const mtx::events::collections::TimelineEvents &e) { - return QTextDocumentFragment::fromHtml(mtx::accessors::formattedBodyWithFallback(e)).toPlainText(); + return QTextDocumentFragment::fromHtml( + mtx::accessors::formattedBodyWithFallback(e).replace( + QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) + .toPlainText(); } diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index 47b4c178..026c912f 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -5,6 +5,7 @@ #include "notifications/Manager.h" #include "wintoastlib.h" +#include #include #include "EventAccessors.h" @@ -80,6 +81,8 @@ NotificationsManager::removeNotification(const QString &, const QString &) QString NotificationsManager::formatNotification(const mtx::events::collections::TimelineEvents &e) { - return QTextDocumentFragment::fromHtml(mtx::accessors::formattedBodyWithFallback(e)).toPlainText(); + return QTextDocumentFragment::fromHtml( + mtx::accessors::formattedBodyWithFallback(e).replace( + QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) + .toPlainText(); } - -- cgit 1.5.1 From f578272a0d645bcfae5d70f6e4aa1dc4649511f1 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Wed, 17 Mar 2021 19:17:57 +0100 Subject: Rewrite notification posting logic This does away with the nice abstraction layers in order to easily get the best-looking notifications for each platform. --- src/notifications/Manager.cpp | 94 ++++++++++++++++++++++++++++---------- src/notifications/Manager.h | 46 +++++++++++++++---- src/notifications/ManagerLinux.cpp | 83 +++++++++++++++++++++++++-------- src/notifications/ManagerMac.cpp | 50 ++++++++++++++++++-- src/notifications/ManagerMac.mm | 31 +++++++------ src/notifications/ManagerWin.cpp | 46 +++++++++++++------ 6 files changed, 264 insertions(+), 86 deletions(-) (limited to 'src/notifications/ManagerWin.cpp') diff --git a/src/notifications/Manager.cpp b/src/notifications/Manager.cpp index 1e4d8167..6fa06cb2 100644 --- a/src/notifications/Manager.cpp +++ b/src/notifications/Manager.cpp @@ -2,32 +2,76 @@ #include "Cache.h" #include "EventAccessors.h" +#include "Logging.h" +#include "MatrixClient.h" #include "Utils.h" -#include -void -NotificationsManager::postNotification(const mtx::responses::Notification ¬ification, - const QImage &icon) +#include +#include +#include + +#include + +QString +NotificationsManager::cacheImage(const mtx::events::collections::TimelineEvents &event) { - const auto room_id = QString::fromStdString(notification.room_id); - const auto event_id = QString::fromStdString(mtx::accessors::event_id(notification.event)); - const auto room_name = - QString::fromStdString(cache::singleRoomInfo(notification.room_id).name); - const auto sender = cache::displayName( - room_id, QString::fromStdString(mtx::accessors::sender(notification.event))); - - const QString reply = (utils::isReply(notification.event) - ? tr(" replied", - "Used to denote that this message is a reply to another " - "message. Displayed as 'foo replied: message'.") - : ""); - - // the "replied" is only added if this message is not an emote message - QString text = - ((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) - ? "* " + sender + " " - : sender + reply + ": ") + - formatNotification(notification.event); - - systemPostNotification(room_id, event_id, room_name, sender, text, icon); + const auto url = mtx::accessors::url(event); + auto encryptionInfo = mtx::accessors::file(event); + + auto filename = QString::fromStdString(mtx::accessors::body(event)); + QString path{QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + + filename}; + + http::client()->download( + url, + [path, url, encryptionInfo](const std::string &data, + const std::string &, + const std::string &, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to retrieve image {}: {} {}", + url, + err->matrix_error.error, + static_cast(err->status_code)); + return; + } + + try { + auto temp = data; + if (encryptionInfo) + temp = mtx::crypto::to_string( + mtx::crypto::decrypt_file(temp, encryptionInfo.value())); + + QFile file{path}; + + if (!file.open(QIODevice::WriteOnly)) + return; + + // delete any existing file content + file.resize(0); + file.write(QByteArray(temp.data(), (int)temp.size())); + + // resize the image (really inefficient, I know, but I can't find any + // better way right off + QImage img{path}; + + // delete existing contents + file.resize(0); + + // make sure to save as PNG (because Plasma doesn't do JPEG in + // notifications) + // if (!file.fileName().endsWith(".png")) + // file.rename(file.fileName() + ".png"); + + img.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation) + .save(&file); + file.close(); + + return; + } catch (const std::exception &e) { + nhlog::ui()->warn("Error while caching file to: {}", e.what()); + } + }); + + return path.toHtmlEscaped(); } diff --git a/src/notifications/Manager.h b/src/notifications/Manager.h index 449a609f..ef049914 100644 --- a/src/notifications/Manager.h +++ b/src/notifications/Manager.h @@ -10,7 +10,12 @@ #include +// convenience definition #if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_HAIKU) +#define NHEKO_DBUS_SYS +#endif + +#if defined(NHEKO_DBUS_SYS) #include #include #endif @@ -43,25 +48,46 @@ public slots: void removeNotification(const QString &roomId, const QString &eventId); private: - void systemPostNotification(const QString &room_id, - const QString &event_id, - const QString &roomName, - const QString &sender, - const QString &text, - const QImage &icon); - - QString formatNotification(const mtx::events::collections::TimelineEvents &e); + QString cacheImage(const mtx::events::collections::TimelineEvents &event); + QString formatNotification(const mtx::responses::Notification ¬ification); -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_HAIKU) +#if defined(NHEKO_DBUS_SYS) public: void closeNotifications(QString roomId); private: QDBusInterface dbus; + + void systemPostNotification(const QString &room_id, + const QString &event_id, + const QString &roomName, + const QString &text, + const QImage &icon); void closeNotification(uint id); // notification ID to (room ID, event ID) QMap notificationIds; + + const bool hasMarkup_; + const bool hasImages_; +#endif + +#if defined(Q_OS_MACOS) +private: + // Objective-C(++) doesn't like to do lots of regular C++, so the actual notification + // posting is split out + void objCxxPostNotification(const QString &title, + const QString &subtitle, + const QString &informativeText, + const QImage *bodyImage); +#endif + +#if defined(Q_OS_WINDOWS) +private: + void systemPostNotification(const QString &roomName, + const QString &sender, + const QString &text, + const QImage &icon); #endif // these slots are platform specific (D-Bus only) @@ -72,7 +98,7 @@ private slots: void notificationReplied(uint id, QString reply); }; -#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_HAIKU) +#if defined(NHEKO_DBUS_SYS) QDBusArgument & operator<<(QDBusArgument &arg, const QImage &image); const QDBusArgument & diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp index 9bcda1b2..ae6fdbee 100644 --- a/src/notifications/ManagerLinux.cpp +++ b/src/notifications/ManagerLinux.cpp @@ -12,6 +12,9 @@ #include +#include + +#include "Cache.h" #include "EventAccessors.h" #include "Utils.h" @@ -22,6 +25,18 @@ NotificationsManager::NotificationsManager(QObject *parent) "org.freedesktop.Notifications", QDBusConnection::sessionBus(), this) + , hasMarkup_{std::invoke([this]() -> bool { + for (auto x : dbus.call("GetCapabilities").arguments()) + if (x.toStringList().contains("body-markup")) + return true; + return false; + })} + , hasImages_{std::invoke([this]() -> bool { + for (auto x : dbus.call("GetCapabilities").arguments()) + if (x.toStringList().contains("body-images")) + return true; + return false; + })} { qDBusRegisterMetaType(); @@ -45,21 +60,32 @@ NotificationsManager::NotificationsManager(QObject *parent) SLOT(notificationReplied(uint, QString))); } -// SPDX-FileCopyrightText: 2012 Roland Hieber -// SPDX-FileCopyrightText: 2021 Nheko Contributors -// -// SPDX-License-Identifier: GPL-3.0-or-later +void +NotificationsManager::postNotification(const mtx::responses::Notification ¬ification, + const QImage &icon) +{ + const auto room_id = QString::fromStdString(notification.room_id); + const auto event_id = QString::fromStdString(mtx::accessors::event_id(notification.event)); + const auto room_name = + QString::fromStdString(cache::singleRoomInfo(notification.room_id).name); + const auto text = formatNotification(notification); + + systemPostNotification(room_id, event_id, room_name, text, icon); +} +/** + * This function is based on code from + * https://github.com/rohieb/StratumsphereTrayIcon + * Copyright (C) 2012 Roland Hieber + * Licensed under the GNU General Public License, version 3 + */ void NotificationsManager::systemPostNotification(const QString &room_id, const QString &event_id, const QString &roomName, - const QString &sender, const QString &text, const QImage &icon) { - Q_UNUSED(sender) - QVariantMap hints; hints["image-data"] = icon; hints["sound-name"] = "message-new-instant"; @@ -163,27 +189,46 @@ NotificationsManager::notificationClosed(uint id, uint reason) * specified at https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/Markup/ */ QString -NotificationsManager::formatNotification(const mtx::events::collections::TimelineEvents &e) +NotificationsManager::formatNotification(const mtx::responses::Notification ¬ification) { - static const auto hasMarkup = std::invoke([this]() -> bool { - for (auto x : dbus.call("GetCapabilities").arguments()) - if (x.toStringList().contains("body-markup")) - return true; - return false; - }); + const auto sender = + cache::displayName(QString::fromStdString(notification.room_id), + QString::fromStdString(mtx::accessors::sender(notification.event))); + const auto messageLeadIn = + ((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) + ? "* " + sender + " " + : sender + + (utils::isReply(notification.event) + ? tr(" replied", + "Used to denote that this message is a reply to another " + "message. Displayed as 'foo replied: message'.") + : "") + + ": "); - if (hasMarkup) - return mtx::accessors::formattedBodyWithFallback(e) + if (hasMarkup_) { + if (hasImages_ && + mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Image) + return QString( + "\""") + .prepend(messageLeadIn); + + return mtx::accessors::formattedBodyWithFallback(notification.event) + .prepend(messageLeadIn) .replace("", "") .replace("", "") .replace("", "") .replace("", "") .replace(QRegularExpression("(.+\\<\\/mx-reply\\>)"), ""); + } return QTextDocumentFragment::fromHtml( - mtx::accessors::formattedBodyWithFallback(e).replace( - QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) - .toPlainText(); + mtx::accessors::formattedBodyWithFallback(notification.event) + .replace(QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) + .toPlainText() + .prepend(messageLeadIn); } /** diff --git a/src/notifications/ManagerMac.cpp b/src/notifications/ManagerMac.cpp index c9678638..12d8ab6f 100644 --- a/src/notifications/ManagerMac.cpp +++ b/src/notifications/ManagerMac.cpp @@ -3,14 +3,56 @@ #include #include +#include "Cache.h" #include "EventAccessors.h" #include "Utils.h" +#include + QString -NotificationsManager::formatNotification(const mtx::events::collections::TimelineEvents &e) +NotificationsManager::formatNotification(const mtx::responses::Notification ¬ification) { + const auto sender = + cache::displayName(QString::fromStdString(notification.room_id), + QString::fromStdString(mtx::accessors::sender(notification.event))); + return QTextDocumentFragment::fromHtml( - mtx::accessors::formattedBodyWithFallback(e).replace( - QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) - .toPlainText(); + mtx::accessors::formattedBodyWithFallback(notification.event) + .replace(QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) + .toPlainText() + .prepend((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) + ? "* " + sender + " " + : ""); +} + +void +NotificationsManager::postNotification(const mtx::responses::Notification ¬ification, + const QImage &icon) +{ + Q_UNUSED(icon) + + const auto room_name = + QString::fromStdString(cache::singleRoomInfo(notification.room_id).name); + const auto sender = + cache::displayName(QString::fromStdString(notification.room_id), + QString::fromStdString(mtx::accessors::sender(notification.event))); + + const QString messageInfo = + QString("%1 %2 a message") + .arg(sender) + .arg((utils::isReply(notification.event) + ? tr("replied to", + "Used to denote that this message is a reply to another " + "message. Displayed as 'foo replied to a message'.") + : tr("sent", + "Used to denote that this message is a normal message. Displayed as 'foo " + "sent a message'."))); + + QString text = formatNotification(notification); + + QImage *image = nullptr; + if (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Image) + image = new QImage{cacheImage(notification.event)}; + + objCxxPostNotification(room_name, messageInfo, text, image); } diff --git a/src/notifications/ManagerMac.mm b/src/notifications/ManagerMac.mm index 3372c5af..226bcce0 100644 --- a/src/notifications/ManagerMac.mm +++ b/src/notifications/ManagerMac.mm @@ -1,7 +1,10 @@ #include "notifications/Manager.h" -#include +#import +#import + #include +#include @interface NSUserNotification (CFIPrivate) - (void)set_identityImage:(NSImage *)image; @@ -13,24 +16,22 @@ NotificationsManager::NotificationsManager(QObject *parent): QObject(parent) } void -NotificationsManager::systemPostNotification(const QString &room_id, - const QString &event_id, - const QString &roomName, - const QString &sender, - const QString &text, - const QImage &icon) +NotificationsManager::objCxxPostNotification(const QString &title, + const QString &subtitle, + const QString &informativeText, + const QImage *bodyImage) { - Q_UNUSED(room_id) - Q_UNUSED(event_id) - Q_UNUSED(icon) - NSUserNotification * notif = [[NSUserNotification alloc] init]; + NSUserNotification *notif = [[NSUserNotification alloc] init]; - notif.title = roomName.toNSString(); - notif.subtitle = QString("%1 sent a message").arg(sender).toNSString(); - notif.informativeText = text.toNSString(); + notif.title = title.toNSString(); + notif.subtitle = subtitle.toNSString(); + notif.informativeText = informativeText.toNSString(); notif.soundName = NSUserNotificationDefaultSoundName; + if (bodyImage != nullptr) + notif.contentImage = [[NSImage alloc] initWithCGImage: bodyImage->toCGImage() size: NSZeroSize]; + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification: notif]; [notif autorelease]; } @@ -39,7 +40,7 @@ NotificationsManager::systemPostNotification(const QString &room_id, void NotificationsManager::actionInvoked(uint, QString) { - } +} void NotificationsManager::notificationReplied(uint, QString) diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index 026c912f..b17c6e3b 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -6,8 +6,10 @@ #include "wintoastlib.h" #include +#include #include +#include "Cache.h" #include "EventAccessors.h" #include "Utils.h" @@ -42,17 +44,25 @@ NotificationsManager::NotificationsManager(QObject *parent) {} void -NotificationsManager::systemPostNotification(const QString &room_id, - const QString &event_id, - const QString &roomName, +NotificationsManager::postNotification(const mtx::responses::Notification ¬ification, + const QImage &icon) +{ + const auto room_name = + QString::fromStdString(cache::singleRoomInfo(notification.room_id).name); + const auto sender = + cache::displayName(QString::fromStdString(notification.room_id), + QString::fromStdString(mtx::accessors::sender(notification.event))); + const auto text = formatNotification(notification); + + systemPostNotification(room_name, sender, text, icon); +} + +void +NotificationsManager::systemPostNotification(const QString &roomName, const QString &sender, const QString &text, const QImage &icon) { - Q_UNUSED(room_id) - Q_UNUSED(event_id) - Q_UNUSED(icon) - if (!isInitialized) init(); @@ -63,8 +73,11 @@ NotificationsManager::systemPostNotification(const QString &room_id, else templ.setTextField(sender.toStdWString(), WinToastTemplate::FirstLine); templ.setTextField(text.toStdWString(), WinToastTemplate::SecondLine); - // TODO: implement room or user avatar - // templ.setImagePath(L"C:/example.png"); + + auto iconPath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + roomName + + "-room-avatar.png"; + if (icon.save(iconPath)) + templ.setImagePath(iconPath.toStdWString()); WinToast::instance()->showToast(templ, new CustomHandler()); } @@ -79,10 +92,17 @@ NotificationsManager::removeNotification(const QString &, const QString &) {} QString -NotificationsManager::formatNotification(const mtx::events::collections::TimelineEvents &e) +NotificationsManager::formatNotification(const mtx::responses::Notification ¬ification) { + const auto sender = + cache::displayName(QString::fromStdString(notification.room_id), + QString::fromStdString(mtx::accessors::sender(notification.event))); + return QTextDocumentFragment::fromHtml( - mtx::accessors::formattedBodyWithFallback(e).replace( - QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) - .toPlainText(); + mtx::accessors::formattedBodyWithFallback(notification.event) + .replace(QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) + .toPlainText() + .prepend((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) + ? "* " + sender + " " + : ""); } -- cgit 1.5.1 From 2192e8bea85e78435830fcb9c8a98c50637c86f8 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 26 Feb 2021 15:33:27 -0500 Subject: Better handle encrypted notifications --- src/notifications/ManagerLinux.cpp | 8 ++++++++ src/notifications/ManagerMac.cpp | 33 +++++++++++++++++++-------------- src/notifications/ManagerWin.cpp | 33 +++++++++++++++++++++++++++++---- 3 files changed, 56 insertions(+), 18 deletions(-) (limited to 'src/notifications/ManagerWin.cpp') diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp index ae6fdbee..56fc8221 100644 --- a/src/notifications/ManagerLinux.cpp +++ b/src/notifications/ManagerLinux.cpp @@ -11,6 +11,7 @@ #include #include +#include #include @@ -194,6 +195,13 @@ NotificationsManager::formatNotification(const mtx::responses::Notification ¬ const auto sender = cache::displayName(QString::fromStdString(notification.room_id), QString::fromStdString(mtx::accessors::sender(notification.event))); + + // TODO: decrypt this message if the decryption setting is on in the UserSettings + if (auto msg = std::get_if>( + ¬ification.event); + msg != nullptr) + return tr("%1 sent an encrypted message").arg(sender); + const auto messageLeadIn = ((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) ? "* " + sender + " " diff --git a/src/notifications/ManagerMac.cpp b/src/notifications/ManagerMac.cpp index 12d8ab6f..973afdc4 100644 --- a/src/notifications/ManagerMac.cpp +++ b/src/notifications/ManagerMac.cpp @@ -9,6 +9,8 @@ #include +#include + QString NotificationsManager::formatNotification(const mtx::responses::Notification ¬ification) { @@ -37,22 +39,25 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if cache::displayName(QString::fromStdString(notification.room_id), QString::fromStdString(mtx::accessors::sender(notification.event))); - const QString messageInfo = - QString("%1 %2 a message") - .arg(sender) - .arg((utils::isReply(notification.event) - ? tr("replied to", - "Used to denote that this message is a reply to another " - "message. Displayed as 'foo replied to a message'.") - : tr("sent", - "Used to denote that this message is a normal message. Displayed as 'foo " - "sent a message'."))); - - QString text = formatNotification(notification); - QImage *image = nullptr; if (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Image) image = new QImage{cacheImage(notification.event)}; - objCxxPostNotification(room_name, messageInfo, text, image); + const auto isEncrypted = + std::get_if>( + ¬ification.event) != nullptr; + const auto isReply = utils::isReply(notification.event); + + 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, messageInfo, "", image); + } else { + const QString messageInfo = + (isReply ? tr("%1 replied to a message") : tr("%1 sent a message")).arg(sender); + objCxxPostNotification( + room_name, messageInfo, formatNotification(notification), image); + } } diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index b17c6e3b..5b134c0b 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include "Cache.h" #include "EventAccessors.h" #include "Utils.h" @@ -52,7 +54,21 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if const auto sender = cache::displayName(QString::fromStdString(notification.room_id), QString::fromStdString(mtx::accessors::sender(notification.event))); - const auto text = formatNotification(notification); + + const auto isEncrypted = + std::get_if>( + ¬ification.event) != nullptr; + const auto isReply = utils::isReply(notification.event); + + if (isEncrypted) { + // TODO: decrypt this message if the decryption setting is on in the UserSettings + const QString text = (isReply ? tr("%1 replied with an encrypted message") + : tr("%1 sent an encrypted message")) + .arg(sender); + systemPostNotification(room_name, sender, text, icon); + } else { + systemPostNotification(room_name, sender, formatNotification(notification), icon); + } systemPostNotification(room_name, sender, text, icon); } @@ -98,11 +114,20 @@ NotificationsManager::formatNotification(const mtx::responses::Notification ¬ cache::displayName(QString::fromStdString(notification.room_id), QString::fromStdString(mtx::accessors::sender(notification.event))); + const auto messageLeadIn = + ((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) + ? "* " + sender + " " + : sender + + (utils::isReply(notification.event) + ? tr(" replied", + "Used to denote that this message is a reply to another " + "message. Displayed as 'foo replied: message'.") + : "") + + ": "); + return QTextDocumentFragment::fromHtml( mtx::accessors::formattedBodyWithFallback(notification.event) .replace(QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) .toPlainText() - .prepend((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) - ? "* " + sender + " " - : ""); + .prepend(messageLeadIn); } -- cgit 1.5.1 From 3748d7853e196d0d34571caf8cd1957ea001e68f Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 26 Feb 2021 15:38:15 -0500 Subject: Simplify formatting on Windows --- src/notifications/Manager.h | 7 +++---- src/notifications/ManagerWin.cpp | 44 +++++++++++++++++----------------------- 2 files changed, 22 insertions(+), 29 deletions(-) (limited to 'src/notifications/ManagerWin.cpp') diff --git a/src/notifications/Manager.h b/src/notifications/Manager.h index ef049914..d7820b18 100644 --- a/src/notifications/Manager.h +++ b/src/notifications/Manager.h @@ -84,10 +84,9 @@ private: #if defined(Q_OS_WINDOWS) private: - void systemPostNotification(const QString &roomName, - const QString &sender, - const QString &text, - const QImage &icon); + void systemPostNotification(const QString &line1, + const QString &line2, + const QString &iconPath); #endif // these slots are platform specific (D-Bus only) diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index 5b134c0b..9c45d166 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -60,39 +60,33 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if ¬ification.event) != nullptr; const auto isReply = utils::isReply(notification.event); - if (isEncrypted) { - // TODO: decrypt this message if the decryption setting is on in the UserSettings - const QString text = (isReply ? tr("%1 replied with an encrypted message") - : tr("%1 sent an encrypted message")) - .arg(sender); - systemPostNotification(room_name, sender, text, icon); - } else { - systemPostNotification(room_name, sender, formatNotification(notification), icon); - } - - systemPostNotification(room_name, sender, text, icon); + const auto line1 = + (room_name == sender) ? sender : QString("%1 - %2").arg(sender).arg(room_name); + const auto line2 = (isEncrypted ? (isReply ? tr("%1 replied with an encrypted message") + : tr("%1 sent an encrypted message")) + : formatNotification(notification)); + + auto iconPath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + + room_name + "-room-avatar.png"; + if (!icon.save(iconPath)) + iconPath.clear(); + + systemPostNotification(line1, line2, iconPath); } void -NotificationsManager::systemPostNotification(const QString &roomName, - const QString &sender, - const QString &text, - const QImage &icon) +NotificationsManager::systemPostNotification(const QString &line1, + const QString &line2, + const QString &iconPath) { if (!isInitialized) init(); auto templ = WinToastTemplate(WinToastTemplate::ImageAndText02); - if (roomName != sender) - templ.setTextField(QString("%1 - %2").arg(sender).arg(roomName).toStdWString(), - WinToastTemplate::FirstLine); - else - templ.setTextField(sender.toStdWString(), WinToastTemplate::FirstLine); - templ.setTextField(text.toStdWString(), WinToastTemplate::SecondLine); - - auto iconPath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + roomName + - "-room-avatar.png"; - if (icon.save(iconPath)) + templ.setTextField(line1.toStdWString(), WinToastTemplate::FirstLine); + templ.setTextField(line2.toStdWString(), WinToastTemplate::SecondLine); + + if (!iconPath.isNull()) templ.setImagePath(iconPath.toStdWString()); WinToast::instance()->showToast(templ, new CustomHandler()); -- cgit 1.5.1 From 8b33b1f08b941fe6ab5cf2431695284e21088223 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 26 Feb 2021 15:38:46 -0500 Subject: Simplify regex --- src/notifications/ManagerLinux.cpp | 2 +- src/notifications/ManagerMac.cpp | 2 +- src/notifications/ManagerWin.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/notifications/ManagerWin.cpp') diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp index 56fc8221..cd9d6fb8 100644 --- a/src/notifications/ManagerLinux.cpp +++ b/src/notifications/ManagerLinux.cpp @@ -234,7 +234,7 @@ NotificationsManager::formatNotification(const mtx::responses::Notification ¬ return QTextDocumentFragment::fromHtml( mtx::accessors::formattedBodyWithFallback(notification.event) - .replace(QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) + .replace(QRegularExpression(".+"), "")) .toPlainText() .prepend(messageLeadIn); } diff --git a/src/notifications/ManagerMac.cpp b/src/notifications/ManagerMac.cpp index 973afdc4..8b6b3bd9 100644 --- a/src/notifications/ManagerMac.cpp +++ b/src/notifications/ManagerMac.cpp @@ -20,7 +20,7 @@ NotificationsManager::formatNotification(const mtx::responses::Notification ¬ return QTextDocumentFragment::fromHtml( mtx::accessors::formattedBodyWithFallback(notification.event) - .replace(QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) + .replace(QRegularExpression(".+"), "")) .toPlainText() .prepend((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) ? "* " + sender + " " diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index 9c45d166..baafb6dc 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -121,7 +121,7 @@ NotificationsManager::formatNotification(const mtx::responses::Notification ¬ return QTextDocumentFragment::fromHtml( mtx::accessors::formattedBodyWithFallback(notification.event) - .replace(QRegularExpression("(.+\\<\\/mx-reply\\>)"), "")) + .replace(QRegularExpression(".+"), "")) .toPlainText() .prepend(messageLeadIn); } -- cgit 1.5.1 From e5d75c814b2175dc37beabff3b0421de59a3e93e Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Wed, 17 Mar 2021 19:08:17 +0100 Subject: Clean up notification code a bit --- src/AvatarProvider.cpp | 65 +++++++-------- src/AvatarProvider.h | 8 +- src/Cache.cpp | 161 +------------------------------------ src/Cache.h | 22 ----- src/CacheStructs.h | 1 - src/Cache_p.h | 10 --- src/CommunitiesList.cpp | 36 ++------- src/MxcImageProvider.cpp | 18 +++-- src/Utils.cpp | 29 +++++++ src/Utils.h | 3 + src/notifications/Manager.cpp | 108 +++++++------------------ src/notifications/Manager.h | 12 ++- src/notifications/ManagerLinux.cpp | 126 ++++++++++++++--------------- src/notifications/ManagerMac.cpp | 37 +++++---- src/notifications/ManagerWin.cpp | 23 ++---- src/timeline/TimelineModel.cpp | 25 +----- 16 files changed, 207 insertions(+), 477 deletions(-) (limited to 'src/notifications/ManagerWin.cpp') diff --git a/src/AvatarProvider.cpp b/src/AvatarProvider.cpp index f64f6859..8cc1144f 100644 --- a/src/AvatarProvider.cpp +++ b/src/AvatarProvider.cpp @@ -12,13 +12,14 @@ #include "Cache.h" #include "Logging.h" #include "MatrixClient.h" +#include "MxcImageProvider.h" #include "Utils.h" static QPixmapCache avatar_cache; namespace AvatarProvider { void -resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback callback) +resolve(QString avatarUrl, int size, QObject *receiver, AvatarCallback callback) { const auto cacheKey = QString("%1_size_%2").arg(avatarUrl).arg(size); @@ -33,44 +34,32 @@ resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback ca return; } - auto data = cache::image(cacheKey); - if (!data.isNull()) { - pixmap = QPixmap::fromImage(utils::readImage(data)); - avatar_cache.insert(cacheKey, pixmap); - callback(pixmap); - return; - } - - auto proxy = std::make_shared(); - QObject::connect(proxy.get(), - &AvatarProxy::avatarDownloaded, - receiver, - [callback, cacheKey](QByteArray data) { - QPixmap pm = QPixmap::fromImage(utils::readImage(data)); - avatar_cache.insert(cacheKey, pm); - callback(pm); - }); + MxcImageProvider::download(avatarUrl.remove(QStringLiteral("mxc://")), + QSize(size, size), + [callback, cacheKey, recv = QPointer(receiver)]( + QString, QSize, QImage img, QString) { + if (!recv) + return; - mtx::http::ThumbOpts opts; - opts.width = size; - opts.height = size; - opts.mxc_url = avatarUrl.toStdString(); + auto proxy = std::make_shared(); + QObject::connect(proxy.get(), + &AvatarProxy::avatarDownloaded, + recv, + [callback, cacheKey](QPixmap pm) { + if (!pm.isNull()) + avatar_cache.insert( + cacheKey, pm); + callback(pm); + }); - http::client()->get_thumbnail( - opts, - [opts, cacheKey, proxy = std::move(proxy)](const std::string &res, - mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to download avatar: {} - ({} {})", - opts.mxc_url, - mtx::errors::to_string(err->matrix_error.errcode), - err->matrix_error.error); - } else { - cache::saveImage(cacheKey.toStdString(), res); - } + if (img.isNull()) { + emit proxy->avatarDownloaded(QPixmap{}); + return; + } - emit proxy->avatarDownloaded(QByteArray(res.data(), (int)res.size())); - }); + auto pm = QPixmap::fromImage(std::move(img)); + emit proxy->avatarDownloaded(pm); + }); } void @@ -80,8 +69,8 @@ resolve(const QString &room_id, QObject *receiver, AvatarCallback callback) { - const auto avatarUrl = cache::avatarUrl(room_id, user_id); + auto avatarUrl = cache::avatarUrl(room_id, user_id); - resolve(avatarUrl, size, receiver, callback); + resolve(std::move(avatarUrl), size, receiver, callback); } } diff --git a/src/AvatarProvider.h b/src/AvatarProvider.h index 0bea1a8f..173a2fba 100644 --- a/src/AvatarProvider.h +++ b/src/AvatarProvider.h @@ -8,19 +8,19 @@ #include #include +using AvatarCallback = std::function; + class AvatarProxy : public QObject { Q_OBJECT signals: - void avatarDownloaded(const QByteArray &data); + void avatarDownloaded(QPixmap pm); }; -using AvatarCallback = std::function; - namespace AvatarProvider { void -resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback cb); +resolve(QString avatarUrl, int size, QObject *receiver, AvatarCallback cb); void resolve(const QString &room_id, const QString &user_id, diff --git a/src/Cache.cpp b/src/Cache.cpp index ec0f2858..4423b21f 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -55,9 +55,6 @@ constexpr auto BATCH_SIZE = 100; //! Format: room_id -> RoomInfo constexpr auto ROOMS_DB("rooms"); constexpr auto INVITES_DB("invites"); -//! Keeps already downloaded media for reuse. -//! Format: matrix_url -> binary data. -constexpr auto MEDIA_DB("media"); //! Information that must be kept between sync requests. constexpr auto SYNC_STATE_DB("sync_state"); //! Read receipts per room/event. @@ -244,7 +241,6 @@ Cache::setup() syncStateDb_ = lmdb::dbi::open(txn, SYNC_STATE_DB, MDB_CREATE); roomsDb_ = lmdb::dbi::open(txn, ROOMS_DB, MDB_CREATE); invitesDb_ = lmdb::dbi::open(txn, INVITES_DB, MDB_CREATE); - mediaDb_ = lmdb::dbi::open(txn, MEDIA_DB, MDB_CREATE); readReceiptsDb_ = lmdb::dbi::open(txn, READ_RECEIPTS_DB, MDB_CREATE); notificationsDb_ = lmdb::dbi::open(txn, NOTIFICATIONS_DB, MDB_CREATE); @@ -700,82 +696,6 @@ Cache::secret(const std::string &name) return secret.toStdString(); } -// -// Media Management -// - -void -Cache::saveImage(const std::string &url, const std::string &img_data) -{ - if (url.empty() || img_data.empty()) - return; - - try { - auto txn = lmdb::txn::begin(env_); - - mediaDb_.put(txn, url, img_data); - - txn.commit(); - } catch (const lmdb::error &e) { - nhlog::db()->critical("saveImage: {}", e.what()); - } -} - -void -Cache::saveImage(const QString &url, const QByteArray &image) -{ - saveImage(url.toStdString(), std::string(image.constData(), image.length())); -} - -QByteArray -Cache::image(lmdb::txn &txn, const std::string &url) -{ - if (url.empty()) - return QByteArray(); - - try { - std::string_view image; - bool res = mediaDb_.get(txn, url, image); - - if (!res) - return QByteArray(); - - return QByteArray(image.data(), (int)image.size()); - } catch (const lmdb::error &e) { - nhlog::db()->critical("image: {}, {}", e.what(), url); - } - - return QByteArray(); -} - -QByteArray -Cache::image(const QString &url) -{ - if (url.isEmpty()) - return QByteArray(); - - auto key = url.toStdString(); - - try { - auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - - std::string_view image; - - bool res = mediaDb_.get(txn, key, image); - - txn.commit(); - - if (!res) - return QByteArray(); - - return QByteArray(image.data(), (int)image.size()); - } catch (const lmdb::error &e) { - nhlog::db()->critical("image: {} {}", e.what(), url.toStdString()); - } - - return QByteArray(); -} - void Cache::removeInvite(lmdb::txn &txn, const std::string &room_id) { @@ -860,7 +780,6 @@ Cache::deleteData() lmdb::dbi_close(env_, syncStateDb_); lmdb::dbi_close(env_, roomsDb_); lmdb::dbi_close(env_, invitesDb_); - lmdb::dbi_close(env_, mediaDb_); lmdb::dbi_close(env_, readReceiptsDb_); lmdb::dbi_close(env_, notificationsDb_); @@ -2470,50 +2389,6 @@ Cache::getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &db) return QString(); } -QImage -Cache::getRoomAvatar(const QString &room_id) -{ - return getRoomAvatar(room_id.toStdString()); -} - -QImage -Cache::getRoomAvatar(const std::string &room_id) -{ - auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY); - - std::string_view response; - - if (!roomsDb_.get(txn, room_id, response)) { - txn.commit(); - return QImage(); - } - - std::string media_url; - - try { - RoomInfo info = json::parse(response); - media_url = std::move(info.avatar_url); - - if (media_url.empty()) { - txn.commit(); - return QImage(); - } - } catch (const json::exception &e) { - nhlog::db()->warn("failed to parse room info: {}, {}", - e.what(), - std::string(response.data(), response.size())); - } - - if (!mediaDb_.get(txn, media_url, response)) { - txn.commit(); - return QImage(); - } - - txn.commit(); - - return QImage::fromData(QByteArray(response.data(), (int)response.size())); -} - std::vector Cache::joinedRooms() { @@ -2615,8 +2490,7 @@ Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_ MemberInfo tmp = json::parse(user_data); members.emplace_back( RoomMember{QString::fromStdString(std::string(user_id)), - QString::fromStdString(tmp.name), - QImage::fromData(image(txn, tmp.avatar_url))}); + QString::fromStdString(tmp.name)}); } catch (const json::exception &e) { nhlog::db()->warn("{}", e.what()); } @@ -4240,18 +4114,6 @@ hasEnoughPowerLevel(const std::vector &eventTypes, return instance_->hasEnoughPowerLevel(eventTypes, room_id, user_id); } -//! Retrieves the saved room avatar. -QImage -getRoomAvatar(const QString &id) -{ - return instance_->getRoomAvatar(id); -} -QImage -getRoomAvatar(const std::string &id) -{ - return instance_->getRoomAvatar(id); -} - void updateReadReceipt(lmdb::txn &txn, const std::string &room_id, const Receipts &receipts) { @@ -4276,27 +4138,6 @@ lastInvisibleEventAfter(const std::string &room_id, std::string_view event_id) return instance_->lastInvisibleEventAfter(room_id, event_id); } -QByteArray -image(const QString &url) -{ - return instance_->image(url); -} -QByteArray -image(lmdb::txn &txn, const std::string &url) -{ - return instance_->image(txn, url); -} -void -saveImage(const std::string &url, const std::string &data) -{ - instance_->saveImage(url, data); -} -void -saveImage(const QString &url, const QByteArray &data) -{ - instance_->saveImage(url, data); -} - RoomInfo singleRoomInfo(const std::string &room_id) { diff --git a/src/Cache.h b/src/Cache.h index f7e5f749..e795b32a 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -6,8 +6,6 @@ #pragma once #include -#include -#include #include #if __has_include() @@ -135,12 +133,6 @@ hasEnoughPowerLevel(const std::vector &eventTypes, const std::string &room_id, const std::string &user_id); -//! Retrieves the saved room avatar. -QImage -getRoomAvatar(const QString &id); -QImage -getRoomAvatar(const std::string &id); - //! Adds a user to the read list for the given event. //! //! There should be only one user id present in a receipt list per room. @@ -162,20 +154,6 @@ getEventIndex(const std::string &room_id, std::string_view event_id); std::optional> lastInvisibleEventAfter(const std::string &room_id, std::string_view event_id); -QByteArray -image(const QString &url); -QByteArray -image(lmdb::txn &txn, const std::string &url); -inline QByteArray -image(const std::string &url) -{ - return image(QString::fromStdString(url)); -} -void -saveImage(const std::string &url, const std::string &data); -void -saveImage(const QString &url, const QByteArray &data); - RoomInfo singleRoomInfo(const std::string &room_id); std::map diff --git a/src/CacheStructs.h b/src/CacheStructs.h index ad9aab98..c449f013 100644 --- a/src/CacheStructs.h +++ b/src/CacheStructs.h @@ -25,7 +25,6 @@ struct RoomMember { QString user_id; QString display_name; - QImage avatar; }; //! Used to uniquely identify a list of read receipts. diff --git a/src/Cache_p.h b/src/Cache_p.h index 473c6319..14b13e43 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -118,10 +118,6 @@ public: const std::string &room_id, const std::string &user_id); - //! Retrieves the saved room avatar. - QImage getRoomAvatar(const QString &id); - QImage getRoomAvatar(const std::string &id); - //! Adds a user to the read list for the given event. //! //! There should be only one user id present in a receipt list per room. @@ -137,11 +133,6 @@ public: using UserReceipts = std::multimap>; UserReceipts readReceipts(const QString &event_id, const QString &room_id); - QByteArray image(const QString &url); - QByteArray image(lmdb::txn &txn, const std::string &url); - void saveImage(const std::string &url, const std::string &data); - void saveImage(const QString &url, const QByteArray &data); - RoomInfo singleRoomInfo(const std::string &room_id); std::vector roomsWithStateUpdates(const mtx::responses::Sync &res); std::vector roomsWithTagUpdates(const mtx::responses::Sync &res); @@ -528,7 +519,6 @@ private: lmdb::dbi syncStateDb_; lmdb::dbi roomsDb_; lmdb::dbi invitesDb_; - lmdb::dbi mediaDb_; lmdb::dbi readReceiptsDb_; lmdb::dbi notificationsDb_; diff --git a/src/CommunitiesList.cpp b/src/CommunitiesList.cpp index f644ebee..7cc5d10e 100644 --- a/src/CommunitiesList.cpp +++ b/src/CommunitiesList.cpp @@ -6,6 +6,7 @@ #include "Cache.h" #include "Logging.h" #include "MatrixClient.h" +#include "MxcImageProvider.h" #include "Splitter.h" #include "UserSettingsPage.h" @@ -253,37 +254,16 @@ CommunitiesList::highlightSelectedCommunity(const QString &community_id) void CommunitiesList::fetchCommunityAvatar(const QString &id, const QString &avatarUrl) { - auto savedImgData = cache::image(avatarUrl); - if (!savedImgData.isNull()) { - QPixmap pix; - pix.loadFromData(savedImgData); - emit avatarRetrieved(id, pix); - return; - } - - if (avatarUrl.isEmpty()) - return; - - mtx::http::ThumbOpts opts; - opts.mxc_url = avatarUrl.toStdString(); - http::client()->get_thumbnail( - opts, [this, opts, id](const std::string &res, mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to download avatar: {} - ({} {})", - opts.mxc_url, - mtx::errors::to_string(err->matrix_error.errcode), - err->matrix_error.error); + MxcImageProvider::download( + QString(avatarUrl).remove(QStringLiteral("mxc://")), + QSize(96, 96), + [this, id](QString, QSize, QImage img, QString) { + if (img.isNull()) { + nhlog::net()->warn("failed to download avatar: {})", id.toStdString()); return; } - cache::saveImage(opts.mxc_url, res); - - auto data = QByteArray(res.data(), (int)res.size()); - - QPixmap pix; - pix.loadFromData(data); - - emit avatarRetrieved(id, pix); + emit avatarRetrieved(id, QPixmap::fromImage(img)); }); } diff --git a/src/MxcImageProvider.cpp b/src/MxcImageProvider.cpp index db0f72c9..023d0e57 100644 --- a/src/MxcImageProvider.cpp +++ b/src/MxcImageProvider.cpp @@ -9,10 +9,10 @@ #include #include +#include #include #include -#include "Cache.h" #include "Logging.h" #include "MatrixClient.h" #include "Utils.h" @@ -60,12 +60,13 @@ MxcImageProvider::download(const QString &id, QString fileName = QString("%1_%2x%3_crop") .arg(QString::fromUtf8(id.toUtf8().toBase64(QByteArray::Base64UrlEncoding | - QByteArray::OmitTrailingEquals)), - requestedSize.width(), - requestedSize.height()); + QByteArray::OmitTrailingEquals))) + .arg(requestedSize.width()) + .arg(requestedSize.height()); QFileInfo fileInfo(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/media_cache", fileName); + QDir().mkpath(fileInfo.absolutePath()); if (fileInfo.exists()) { QImage image(fileInfo.absoluteFilePath()); @@ -102,7 +103,12 @@ MxcImageProvider::download(const QString &id, requestedSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); } image.setText("mxc url", "mxc://" + id); - image.save(fileInfo.absoluteFilePath()); + if (image.save(fileInfo.absoluteFilePath(), "png")) + nhlog::ui()->debug("Wrote: {}", + fileInfo.absoluteFilePath().toStdString()); + else + nhlog::ui()->debug("Failed to write: {}", + fileInfo.absoluteFilePath().toStdString()); then(id, requestedSize, image, fileInfo.absoluteFilePath()); }); @@ -114,6 +120,7 @@ MxcImageProvider::download(const QString &id, QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/media_cache", fileName); + QDir().mkpath(fileInfo.absolutePath()); if (fileInfo.exists()) { if (encryptionInfo) { @@ -145,7 +152,6 @@ MxcImageProvider::download(const QString &id, } } } - auto data = cache::image(id); http::client()->download( "mxc://" + id.toStdString(), diff --git a/src/Utils.cpp b/src/Utils.cpp index 4d24c786..8a3b9e4c 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -51,6 +51,35 @@ createDescriptionInfo(const Event &event, const QString &localUser, const QStrin ts}; } +RelatedInfo +utils::stripReplyFallbacks(const TimelineEvent &event, std::string id, QString room_id_) +{ + RelatedInfo related = {}; + related.quoted_user = QString::fromStdString(mtx::accessors::sender(event)); + related.related_event = std::move(id); + related.type = mtx::accessors::msg_type(event); + + // get body, strip reply fallback, then transform the event to text, if it is a media event + // etc + related.quoted_body = QString::fromStdString(mtx::accessors::body(event)); + QRegularExpression plainQuote("^>.*?$\n?", QRegularExpression::MultilineOption); + while (related.quoted_body.startsWith(">")) + related.quoted_body.remove(plainQuote); + if (related.quoted_body.startsWith("\n")) + related.quoted_body.remove(0, 1); + related.quoted_body = utils::getQuoteBody(related); + related.quoted_body.replace("@room", QString::fromUtf8("@\u2060room")); + + // get quoted body and strip reply fallback + related.quoted_formatted_body = mtx::accessors::formattedBodyWithFallback(event); + related.quoted_formatted_body.remove(QRegularExpression( + ".*", QRegularExpression::DotMatchesEverythingOption)); + related.quoted_formatted_body.replace("@room", "@\u2060aroom"); + related.room = room_id_; + + return related; +} + QString utils::localUser() { diff --git a/src/Utils.h b/src/Utils.h index eb09172e..f8ead68c 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -40,6 +40,9 @@ namespace utils { using TimelineEvent = mtx::events::collections::TimelineEvents; +RelatedInfo +stripReplyFallbacks(const TimelineEvent &event, std::string id, QString room_id_); + bool codepointIsEmoji(uint code); diff --git a/src/notifications/Manager.cpp b/src/notifications/Manager.cpp index 30e74d33..322213dd 100644 --- a/src/notifications/Manager.cpp +++ b/src/notifications/Manager.cpp @@ -2,89 +2,35 @@ #include "Cache.h" #include "EventAccessors.h" -#include "Logging.h" -#include "MatrixClient.h" #include "Utils.h" -#include -#include -#include - -#include - QString -NotificationsManager::cacheImage(const mtx::events::collections::TimelineEvents &event) +NotificationsManager::getMessageTemplate(const mtx::responses::Notification ¬ification) { - const auto url = mtx::accessors::url(event); - auto encryptionInfo = mtx::accessors::file(event); - - auto filename = QString::fromStdString(mtx::accessors::body(event)); - QString path{QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + - filename}; - - bool downloadComplete = false; - - http::client()->download( - url, - [&downloadComplete, &path, url, encryptionInfo](const std::string &data, - const std::string &, - const std::string &, - mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to retrieve image {}: {} {}", - url, - err->matrix_error.error, - static_cast(err->status_code)); - // the image doesn't exist, so delete the path - path.clear(); - downloadComplete = true; - return; - } - - try { - auto temp = data; - if (encryptionInfo) - temp = mtx::crypto::to_string( - mtx::crypto::decrypt_file(temp, encryptionInfo.value())); - - QFile file{path}; - - if (!file.open(QIODevice::WriteOnly)) { - path.clear(); - downloadComplete = true; - return; - } - - // delete any existing file content - file.resize(0); - - // resize the image - QImage img{utils::readImage(QByteArray{temp.data()})}; - - if (img.isNull()) { - path.clear(); - downloadComplete = true; - return; - } - -#ifdef NHEKO_DBUS_SYS // the images in D-Bus notifications are to be 200x100 max - img.scaled(200, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation) - .save(&file); -#else - img.save(&file); -#endif // NHEKO_DBUS_SYS - - file.close(); - - downloadComplete = true; - return; - } catch (const std::exception &e) { - nhlog::ui()->warn("Error while caching file to: {}", e.what()); - } - }); - - while (!downloadComplete) - continue; - - return path.toHtmlEscaped(); + const auto sender = + cache::displayName(QString::fromStdString(notification.room_id), + QString::fromStdString(mtx::accessors::sender(notification.event))); + + // TODO: decrypt this message if the decryption setting is on in the UserSettings + if (auto msg = std::get_if>( + ¬ification.event); + msg != nullptr) { + return tr("%1 sent an encrypted message").arg(sender); + } + + if (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) { + return tr("* %1 %2", + "Format an emote message in a notification, %1 is the sender, %2 the " + "message") + .arg(sender); + } else if (utils::isReply(notification.event)) { + return tr("%1 replied: %2", + "Format a reply in a notification. %1 is the sender, %2 the message") + .arg(sender); + } else { + return tr("%1: %2", + "Format a normal message in a notification. %1 is the sender, %2 the " + "message") + .arg(sender); + } } diff --git a/src/notifications/Manager.h b/src/notifications/Manager.h index a1ef9f98..416530e0 100644 --- a/src/notifications/Manager.h +++ b/src/notifications/Manager.h @@ -43,14 +43,15 @@ public: signals: void notificationClicked(const QString roomId, const QString eventId); void sendNotificationReply(const QString roomId, const QString eventId, const QString body); + void systemPostNotificationCb(const QString &room_id, + const QString &event_id, + const QString &roomName, + const QString &text, + const QImage &icon); public slots: void removeNotification(const QString &roomId, const QString &eventId); -private: - QString cacheImage(const mtx::events::collections::TimelineEvents &event); - QString formatNotification(const mtx::responses::Notification ¬ification); - #if defined(NHEKO_DBUS_SYS) public: void closeNotifications(QString roomId); @@ -95,6 +96,9 @@ private slots: void actionInvoked(uint id, QString action); void notificationClosed(uint id, uint reason); void notificationReplied(uint id, QString reply); + +private: + QString getMessageTemplate(const mtx::responses::Notification ¬ification); }; #if defined(NHEKO_DBUS_SYS) diff --git a/src/notifications/ManagerLinux.cpp b/src/notifications/ManagerLinux.cpp index 5581252d..2b0e56e2 100644 --- a/src/notifications/ManagerLinux.cpp +++ b/src/notifications/ManagerLinux.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,7 @@ #include "Cache.h" #include "EventAccessors.h" +#include "MxcImageProvider.h" #include "Utils.h" NotificationsManager::NotificationsManager(QObject *parent) @@ -59,6 +61,12 @@ NotificationsManager::NotificationsManager(QObject *parent) "NotificationReplied", this, SLOT(notificationReplied(uint, QString))); + + connect(this, + &NotificationsManager::systemPostNotificationCb, + this, + &NotificationsManager::systemPostNotification, + Qt::QueuedConnection); } void @@ -69,9 +77,61 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if const auto event_id = QString::fromStdString(mtx::accessors::event_id(notification.event)); const auto room_name = QString::fromStdString(cache::singleRoomInfo(notification.room_id).name); - const auto text = formatNotification(notification); - systemPostNotification(room_id, event_id, room_name, text, icon); + auto postNotif = [this, room_id, event_id, room_name, icon](QString text) { + emit systemPostNotificationCb(room_id, event_id, room_name, text, icon); + }; + + QString template_ = getMessageTemplate(notification); + // TODO: decrypt this message if the decryption setting is on in the UserSettings + if (std::holds_alternative>( + notification.event)) { + postNotif(template_); + return; + } + + if (hasMarkup_) { + if (hasImages_ && mtx::accessors::msg_type(notification.event) == + mtx::events::MessageType::Image) { + MxcImageProvider::download( + QString::fromStdString(mtx::accessors::url(notification.event)) + .remove("mxc://"), + QSize(200, 80), + [postNotif, notification, template_]( + QString, QSize, QImage, QString imgPath) { + if (imgPath.isEmpty()) + postNotif(template_ + .arg(utils::stripReplyFallbacks( + notification.event, {}, {}) + .quoted_formatted_body) + .replace("", "") + .replace("", "") + .replace("", "") + .replace("", "")); + else + postNotif(template_.arg( + QStringLiteral("
\""")); + }); + return; + } + + postNotif( + template_ + .arg( + utils::stripReplyFallbacks(notification.event, {}, {}).quoted_formatted_body) + .replace("", "") + .replace("", "") + .replace("", "") + .replace("", "")); + return; + } + + postNotif( + template_.arg(utils::stripReplyFallbacks(notification.event, {}, {}).quoted_body)); } /** @@ -182,68 +242,6 @@ NotificationsManager::notificationClosed(uint id, uint reason) notificationIds.remove(id); } -/** - * @param text This should be an HTML-formatted string. - * - * If D-Bus says that notifications can have body markup, this function will - * automatically format the notification to follow the supported HTML subset - * specified at https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/Markup/ - */ -QString -NotificationsManager::formatNotification(const mtx::responses::Notification ¬ification) -{ - const auto sender = - cache::displayName(QString::fromStdString(notification.room_id), - QString::fromStdString(mtx::accessors::sender(notification.event))); - - // TODO: decrypt this message if the decryption setting is on in the UserSettings - if (auto msg = std::get_if>( - ¬ification.event); - msg != nullptr) - return tr("%1 sent an encrypted message").arg(sender); - - const auto messageLeadIn = - ((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) - ? "* " + sender + " " - : sender + - (utils::isReply(notification.event) - ? tr(" replied", - "Used to denote that this message is a reply to another " - "message. Displayed as 'foo replied: message'.") - : "") + - ": "); - - if (hasMarkup_) { - if (hasImages_ && mtx::accessors::msg_type(notification.event) == - mtx::events::MessageType::Image) { - QString imgPath = cacheImage(notification.event); - if (imgPath.isNull()) - return mtx::accessors::formattedBodyWithFallback(notification.event) - .prepend(messageLeadIn); - else - return QString("\""") - .prepend(messageLeadIn); - } - - return mtx::accessors::formattedBodyWithFallback(notification.event) - .prepend(messageLeadIn) - .replace("", "") - .replace("", "") - .replace("", "") - .replace("", "") - .replace(QRegularExpression("(.+\\<\\/mx-reply\\>)"), ""); - } - - return QTextDocumentFragment::fromHtml( - mtx::accessors::formattedBodyWithFallback(notification.event) - .replace(QRegularExpression(".+"), "")) - .toPlainText() - .prepend(messageLeadIn); -} - /** * Automatic marshaling of a QImage for org.freedesktop.Notifications.Notify * diff --git a/src/notifications/ManagerMac.cpp b/src/notifications/ManagerMac.cpp index de5d0875..3a6becad 100644 --- a/src/notifications/ManagerMac.cpp +++ b/src/notifications/ManagerMac.cpp @@ -5,6 +5,7 @@ #include "Cache.h" #include "EventAccessors.h" +#include "MxcImageProvider.h" #include "Utils.h" #include @@ -14,17 +15,7 @@ QString NotificationsManager::formatNotification(const mtx::responses::Notification ¬ification) { - const auto sender = - cache::displayName(QString::fromStdString(notification.room_id), - QString::fromStdString(mtx::accessors::sender(notification.event))); - - return QTextDocumentFragment::fromHtml( - mtx::accessors::formattedBodyWithFallback(notification.event) - .replace(QRegularExpression(".+"), "")) - .toPlainText() - .prepend((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) - ? "* " + sender + " " - : ""); + return utils::stripReplyFallbacks(notification.event, {}, {}).quoted_body; } void @@ -39,25 +30,33 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if cache::displayName(QString::fromStdString(notification.room_id), QString::fromStdString(mtx::accessors::sender(notification.event))); - QImage image; - if (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Image) - image = QImage{cacheImage(notification.event)}; - const auto isEncrypted = std::get_if>( ¬ification.event) != nullptr; const auto isReply = utils::isReply(notification.event); - 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, messageInfo, "", image); + objCxxPostNotification(room_name, messageInfo, "", QImage()); } else { const QString messageInfo = (isReply ? tr("%1 replied to a message") : tr("%1 sent a message")).arg(sender); - objCxxPostNotification( - room_name, messageInfo, formatNotification(notification), image); + if (mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Image) + MxcImageProvider::download( + QString::fromStdString(mtx::accessors::url(notification.event)) + .remove("mxc://"), + QSize(200, 80), + [this, notification, room_name, messageInfo]( + QString, QSize, QImage image, QString) { + objCxxPostNotification(room_name, + messageInfo, + formatNotification(notification), + image); + }); + else + objCxxPostNotification( + room_name, messageInfo, formatNotification(notification), QImage()); } } diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index baafb6dc..d37bff67 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -108,20 +108,11 @@ NotificationsManager::formatNotification(const mtx::responses::Notification ¬ cache::displayName(QString::fromStdString(notification.room_id), QString::fromStdString(mtx::accessors::sender(notification.event))); - const auto messageLeadIn = - ((mtx::accessors::msg_type(notification.event) == mtx::events::MessageType::Emote) - ? "* " + sender + " " - : sender + - (utils::isReply(notification.event) - ? tr(" replied", - "Used to denote that this message is a reply to another " - "message. Displayed as 'foo replied: message'.") - : "") + - ": "); - - return QTextDocumentFragment::fromHtml( - mtx::accessors::formattedBodyWithFallback(notification.event) - .replace(QRegularExpression(".+"), "")) - .toPlainText() - .prepend(messageLeadIn); + const auto template_ = getMessageTemplate(notification); + if (std::holds_alternative>( + notification.event)) { + return template_; + } + + return template_.arg(utils::stripReplyFallbacks(notification.event, {}, {}).quoted_body); } diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index cfca626a..8e96cb3e 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -870,30 +870,7 @@ TimelineModel::relatedInfo(QString id) if (!event) return {}; - RelatedInfo related = {}; - related.quoted_user = QString::fromStdString(mtx::accessors::sender(*event)); - related.related_event = id.toStdString(); - related.type = mtx::accessors::msg_type(*event); - - // get body, strip reply fallback, then transform the event to text, if it is a media event - // etc - related.quoted_body = QString::fromStdString(mtx::accessors::body(*event)); - QRegularExpression plainQuote("^>.*?$\n?", QRegularExpression::MultilineOption); - while (related.quoted_body.startsWith(">")) - related.quoted_body.remove(plainQuote); - if (related.quoted_body.startsWith("\n")) - related.quoted_body.remove(0, 1); - related.quoted_body = utils::getQuoteBody(related); - related.quoted_body.replace("@room", QString::fromUtf8("@\u2060room")); - - // get quoted body and strip reply fallback - related.quoted_formatted_body = mtx::accessors::formattedBodyWithFallback(*event); - related.quoted_formatted_body.remove(QRegularExpression( - ".*", QRegularExpression::DotMatchesEverythingOption)); - related.quoted_formatted_body.replace("@room", "@\u2060aroom"); - related.room = room_id_; - - return related; + return utils::stripReplyFallbacks(*event, id.toStdString(), room_id_); } void -- cgit 1.5.1 From 1408b1a97d37a732ec06916489a60cd9e4d9f6dd Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Wed, 17 Mar 2021 22:13:12 +0100 Subject: Make CI happy --- src/notifications/ManagerMac.cpp | 4 ++-- src/notifications/ManagerWin.cpp | 30 +++++++++++++----------------- 2 files changed, 15 insertions(+), 19 deletions(-) (limited to 'src/notifications/ManagerWin.cpp') diff --git a/src/notifications/ManagerMac.cpp b/src/notifications/ManagerMac.cpp index be56585c..8e36985c 100644 --- a/src/notifications/ManagerMac.cpp +++ b/src/notifications/ManagerMac.cpp @@ -16,8 +16,8 @@ #include -QString -NotificationsManager::formatNotification(const mtx::responses::Notification ¬ification) +static QString +formatNotification(const mtx::responses::Notification ¬ification) { return utils::stripReplyFallbacks(notification.event, {}, {}).quoted_body; } diff --git a/src/notifications/ManagerWin.cpp b/src/notifications/ManagerWin.cpp index d37bff67..fe7830a7 100644 --- a/src/notifications/ManagerWin.cpp +++ b/src/notifications/ManagerWin.cpp @@ -60,11 +60,23 @@ NotificationsManager::postNotification(const mtx::responses::Notification ¬if ¬ification.event) != nullptr; const auto isReply = utils::isReply(notification.event); + auto formatNotification = [this, notification, sender] { + const auto template_ = getMessageTemplate(notification); + if (std::holds_alternative< + mtx::events::EncryptedEvent>( + notification.event)) { + return template_; + } + + return template_.arg( + utils::stripReplyFallbacks(notification.event, {}, {}).quoted_body); + }; + const auto line1 = (room_name == sender) ? sender : QString("%1 - %2").arg(sender).arg(room_name); const auto line2 = (isEncrypted ? (isReply ? tr("%1 replied with an encrypted message") : tr("%1 sent an encrypted message")) - : formatNotification(notification)); + : formatNotification()); auto iconPath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + room_name + "-room-avatar.png"; @@ -100,19 +112,3 @@ void NotificationsManager::notificationClosed(uint, uint) {} void NotificationsManager::removeNotification(const QString &, const QString &) {} - -QString -NotificationsManager::formatNotification(const mtx::responses::Notification ¬ification) -{ - const auto sender = - cache::displayName(QString::fromStdString(notification.room_id), - QString::fromStdString(mtx::accessors::sender(notification.event))); - - const auto template_ = getMessageTemplate(notification); - if (std::holds_alternative>( - notification.event)) { - return template_; - } - - return template_.arg(utils::stripReplyFallbacks(notification.event, {}, {}).quoted_body); -} -- cgit 1.5.1