From 2c1f2122c8a92c81e77f81ff639a5957974a400e Mon Sep 17 00:00:00 2001 From: Jared <7060603@hotmail.ca> Date: Sun, 12 Jan 2025 12:56:09 -0700 Subject: [PATCH 1/5] fix(Notify): Use notification categories on Linux Use categories as defined by FreeDesktop. Addresses #424 --- src/model/notificationdata.h | 1 + src/model/notificationgenerator.cpp | 23 +++++++++++++++++++ src/model/notificationgenerator.h | 1 + .../desktop_notifications/desktopnotify.cpp | 2 +- .../desktopnotifybackend.h | 4 ++-- .../desktopnotifybackend_dbus.cpp | 4 ++-- src/widget/widget.cpp | 14 +++++++---- 7 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/model/notificationdata.h b/src/model/notificationdata.h index f3dd87f6ea..0b6395b470 100644 --- a/src/model/notificationdata.h +++ b/src/model/notificationdata.h @@ -12,5 +12,6 @@ struct NotificationData { QString title; QString message; + QString category; QPixmap pixmap; }; diff --git a/src/model/notificationgenerator.cpp b/src/model/notificationgenerator.cpp index cc8ab76d27..de3c60e670 100644 --- a/src/model/notificationgenerator.cpp +++ b/src/model/notificationgenerator.cpp @@ -144,6 +144,26 @@ NotificationData NotificationGenerator::friendMessageNotification(const Friend* ret.title = generateTitle(friendNotifications, conferenceNotifications, f); ret.message = generateContent(friendNotifications, conferenceNotifications, message, f->getPublicKey()); + ret.category = "im.received"; + ret.pixmap = getSenderAvatar(profile, f->getPublicKey()); + + return ret; +} + +NotificationData NotificationGenerator::incomingCallNotification(const Friend* f) +{ + friendNotifications[f]++; + + NotificationData ret; + + if (notificationSettings.getNotifyHide()) { + ret.title = tr("Incoming call"); + return ret; + } + + ret.title = generateTitle(friendNotifications, conferenceNotifications, f); + ret.message = generateContent(friendNotifications, conferenceNotifications, "", f->getPublicKey()); + ret.category = "call.incoming"; ret.pixmap = getSenderAvatar(profile, f->getPublicKey()); return ret; @@ -195,6 +215,7 @@ NotificationData NotificationGenerator::fileTransferNotification(const Friend* f ret.message = filename + " (" + getHumanReadableSize(fileSize) + ")"; } + ret.category = "transfer"; ret.pixmap = getSenderAvatar(profile, f->getPublicKey()); return ret; @@ -211,6 +232,7 @@ NotificationData NotificationGenerator::conferenceInvitationNotification(const F ret.title = tr("%1 invites you to join a conference.").arg(from->getDisplayedName()); ret.message = ""; + ret.category = "im"; ret.pixmap = getSenderAvatar(profile, from->getPublicKey()); return ret; @@ -228,6 +250,7 @@ NotificationData NotificationGenerator::friendRequestNotification(const ToxPk& s ret.title = tr("Friend request received from %1").arg(sender.toString()); ret.message = message; + ret.category = "im"; return ret; } diff --git a/src/model/notificationgenerator.h b/src/model/notificationgenerator.h index 4769d21ce0..702e1423d3 100644 --- a/src/model/notificationgenerator.h +++ b/src/model/notificationgenerator.h @@ -33,6 +33,7 @@ class NotificationGenerator : public QObject NotificationGenerator& operator=(NotificationGenerator&&) = delete; NotificationData friendMessageNotification(const Friend* f, const QString& message); + NotificationData incomingCallNotification(const Friend* f); NotificationData conferenceMessageNotification(const Conference* c, const ToxPk& sender, const QString& message); NotificationData fileTransferNotification(const Friend* f, const QString& filename, diff --git a/src/platform/desktop_notifications/desktopnotify.cpp b/src/platform/desktop_notifications/desktopnotify.cpp index cc4518dee8..01b498076f 100644 --- a/src/platform/desktop_notifications/desktopnotify.cpp +++ b/src/platform/desktop_notifications/desktopnotify.cpp @@ -47,7 +47,7 @@ void DesktopNotify::notifyMessage(const NotificationData& notificationData) // Try system-backends first. if (d->settings.getNotifySystemBackend()) { if (d->dbus->showMessage(notificationData.title, notificationData.message, - notificationData.pixmap)) { + notificationData.category, notificationData.pixmap)) { return; } } diff --git a/src/platform/desktop_notifications/desktopnotifybackend.h b/src/platform/desktop_notifications/desktopnotifybackend.h index 623b1a4877..57f76e6240 100644 --- a/src/platform/desktop_notifications/desktopnotifybackend.h +++ b/src/platform/desktop_notifications/desktopnotifybackend.h @@ -15,8 +15,8 @@ class DesktopNotifyBackend : public QObject public: explicit DesktopNotifyBackend(QObject* parent); ~DesktopNotifyBackend() override; - - bool showMessage(const QString& title, const QString& message, const QPixmap& pixmap); + bool showMessage(const QString& title, const QString& message, const QString& category, + const QPixmap& pixmap); signals: void messageClicked(); diff --git a/src/platform/desktop_notifications/desktopnotifybackend_dbus.cpp b/src/platform/desktop_notifications/desktopnotifybackend_dbus.cpp index d939bd413e..1c4152478f 100644 --- a/src/platform/desktop_notifications/desktopnotifybackend_dbus.cpp +++ b/src/platform/desktop_notifications/desktopnotifybackend_dbus.cpp @@ -324,13 +324,13 @@ DesktopNotifyBackend::DesktopNotifyBackend(QObject* parent) DesktopNotifyBackend::~DesktopNotifyBackend() = default; bool DesktopNotifyBackend::showMessage(const QString& title, const QString& message, - const QPixmap& pixmap) + const QString& category, const QPixmap& pixmap) { // Try Notify first. if (d->notifyInterface.isValid()) { QVariantMap hints{ {QStringLiteral("action-icons"), true}, - {QStringLiteral("category"), QStringLiteral("im.received")}, + {QStringLiteral("category"), category}, {QStringLiteral("sender-pid"), QVariant::fromValue(QCoreApplication::applicationPid())}, }; diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 111dd71382..9fd7d37eb9 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -1566,10 +1566,16 @@ bool Widget::newFriendMessageAlert(const ToxPk& friendId, const QString& text, b widget->updateStatusLight(); ui->friendList->trackWidget(settings, style, widget); if (notifier != nullptr) { - auto notificationData = - filename.isEmpty() - ? notificationGenerator->friendMessageNotification(f, text) - : notificationGenerator->fileTransferNotification(f, filename, filesize); + NotificationData notificationData; + if (filename.isEmpty()) { + if (text.isEmpty()) { + notificationData = notificationGenerator->incomingCallNotification(f); + } else { + notificationData = notificationGenerator->friendMessageNotification(f, text); + } + } else { + notificationData = notificationGenerator->fileTransferNotification(f, filename, filesize); + } notifier->notifyMessage(notificationData); } From e7636842a2c3f557178fec0daa07057f1d2aa97a Mon Sep 17 00:00:00 2001 From: Jared <7060603@hotmail.ca> Date: Sun, 12 Jan 2025 13:22:41 -0700 Subject: [PATCH 2/5] fix(Notify): Ungroup dbus notifications Send a new notification for each event instead of reusing one notification. --- src/model/notificationgenerator.cpp | 117 ++---------------- .../desktopnotifybackend_dbus.cpp | 2 +- 2 files changed, 11 insertions(+), 108 deletions(-) diff --git a/src/model/notificationgenerator.cpp b/src/model/notificationgenerator.cpp index de3c60e670..17a643d492 100644 --- a/src/model/notificationgenerator.cpp +++ b/src/model/notificationgenerator.cpp @@ -10,62 +10,6 @@ #include namespace { -size_t getNumMessages(const QHash& friendNotifications, - const QHash& conferenceNotifications) -{ - auto numMessages = std::accumulate(friendNotifications.begin(), friendNotifications.end(), 0); - numMessages = - std::accumulate(conferenceNotifications.begin(), conferenceNotifications.end(), numMessages); - - return numMessages; -} - -size_t getNumChats(const QHash& friendNotifications, - const QHash& conferenceNotifications) -{ - return friendNotifications.size() + conferenceNotifications.size(); -} - -QString generateMultiChatTitle(size_t numChats, size_t numMessages) -{ - //: e.g. 3 messages from 2 chats - return QObject::tr("%1 message(s) from %2 chats").arg(numMessages).arg(numChats); -} - -template -QString generateSingleChatTitle(const QHash numNotifications, T contact) -{ - if (numNotifications[contact] > 1) { - //: e.g. 2 messages from Bob - return QObject::tr("%1 message(s) from %2") - .arg(numNotifications[contact]) - .arg(contact->getDisplayedName()); - } - return contact->getDisplayedName(); -} - -QString generateTitle(const QHash& friendNotifications, - const QHash& conferenceNotifications, const Friend* f) -{ - auto numChats = getNumChats(friendNotifications, conferenceNotifications); - if (numChats > 1) { - return generateMultiChatTitle(numChats, - getNumMessages(friendNotifications, conferenceNotifications)); - } - return generateSingleChatTitle(friendNotifications, f); -} - -QString generateTitle(const QHash& friendNotifications, - const QHash& conferenceNotifications, - const Conference* c) -{ - auto numChats = getNumChats(friendNotifications, conferenceNotifications); - if (numChats > 1) { - return generateMultiChatTitle(numChats, - getNumMessages(friendNotifications, conferenceNotifications)); - } - return generateSingleChatTitle(conferenceNotifications, c); -} QString generateContent(const QHash& friendNotifications, const QHash& conferenceNotifications, @@ -73,38 +17,6 @@ QString generateContent(const QHash& friendNotifications, { assert(!friendNotifications.empty() || !conferenceNotifications.empty()); - auto numChats = getNumChats(friendNotifications, conferenceNotifications); - if (numChats > 1) { - // Copy all names into a vector to simplify formatting logic between - // multiple lists - std::vector displayNames; - displayNames.reserve(numChats); - - for (auto it = friendNotifications.begin(); it != friendNotifications.end(); ++it) { - displayNames.push_back(it.key()->getDisplayedName()); - } - - for (auto it = conferenceNotifications.begin(); it != conferenceNotifications.end(); ++it) { - displayNames.push_back(it.key()->getDisplayedName()); - } - - assert(!displayNames.empty()); - - // Lexicographically sort all display names to ensure consistent formatting - QCollator collator; - std::sort(displayNames.begin(), displayNames.end(), - [&](const QString& a, const QString& b) { return collator.compare(a, b) < 1; }); - - auto it = displayNames.begin(); - - QString ret = *it; - - while (++it != displayNames.end()) { - ret += ", " + *it; - } - - return ret; - } if (conferenceNotifications.size() == 1) { auto it = conferenceNotifications.begin(); if (it == conferenceNotifications.end()) { @@ -141,9 +53,8 @@ NotificationData NotificationGenerator::friendMessageNotification(const Friend* return ret; } - ret.title = generateTitle(friendNotifications, conferenceNotifications, f); - ret.message = - generateContent(friendNotifications, conferenceNotifications, message, f->getPublicKey()); + ret.title = f->getDisplayedName(); + ret.message = message; ret.category = "im.received"; ret.pixmap = getSenderAvatar(profile, f->getPublicKey()); @@ -158,11 +69,12 @@ NotificationData NotificationGenerator::incomingCallNotification(const Friend* f if (notificationSettings.getNotifyHide()) { ret.title = tr("Incoming call"); + ret.category = "call.incoming"; return ret; } - ret.title = generateTitle(friendNotifications, conferenceNotifications, f); - ret.message = generateContent(friendNotifications, conferenceNotifications, "", f->getPublicKey()); + ret.title = f->getDisplayedName(); + ret.message = tr("Incoming call"); ret.category = "call.incoming"; ret.pixmap = getSenderAvatar(profile, f->getPublicKey()); @@ -182,8 +94,9 @@ NotificationData NotificationGenerator::conferenceMessageNotification(const Conf return ret; } - ret.title = generateTitle(friendNotifications, conferenceNotifications, c); + ret.title = c->getDisplayedName(); ret.message = generateContent(friendNotifications, conferenceNotifications, message, sender); + ret.category = "im.received"; ret.pixmap = getSenderAvatar(profile, sender); return ret; @@ -202,19 +115,9 @@ NotificationData NotificationGenerator::fileTransferNotification(const Friend* f return ret; } - auto numChats = getNumChats(friendNotifications, conferenceNotifications); - auto numMessages = getNumMessages(friendNotifications, conferenceNotifications); - - if (numChats > 1 || numMessages > 1) { - ret.title = generateTitle(friendNotifications, conferenceNotifications, f); - ret.message = generateContent(friendNotifications, conferenceNotifications, - tr("Incoming file transfer"), f->getPublicKey()); - } else { - //: e.g. Bob - file transfer - ret.title = tr("%1 - file transfer").arg(f->getDisplayedName()); - ret.message = filename + " (" + getHumanReadableSize(fileSize) + ")"; - } - + //: e.g. Bob - file transfer + ret.title = tr("%1 - file transfer").arg(f->getDisplayedName()); + ret.message = filename + " (" + getHumanReadableSize(fileSize) + ")"; ret.category = "transfer"; ret.pixmap = getSenderAvatar(profile, f->getPublicKey()); diff --git a/src/platform/desktop_notifications/desktopnotifybackend_dbus.cpp b/src/platform/desktop_notifications/desktopnotifybackend_dbus.cpp index 1c4152478f..f7244a9159 100644 --- a/src/platform/desktop_notifications/desktopnotifybackend_dbus.cpp +++ b/src/platform/desktop_notifications/desktopnotifybackend_dbus.cpp @@ -342,7 +342,7 @@ bool DesktopNotifyBackend::showMessage(const QString& title, const QString& mess // app_name QApplication::applicationName(), // replaces_id - d->id, + static_cast(0), // app_icon QStringLiteral("dialog-password"), // summary From 0c3e65362c18742c857dfc86e048f6d8fa095ede Mon Sep 17 00:00:00 2001 From: Jared <7060603@hotmail.ca> Date: Sun, 12 Jan 2025 13:31:13 -0700 Subject: [PATCH 3/5] fix(Notify): Notify sound setting disables all sounds --- src/widget/widget.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/widget/widget.cpp b/src/widget/widget.cpp index 9fd7d37eb9..dd029a7a11 100644 --- a/src/widget/widget.cpp +++ b/src/widget/widget.cpp @@ -1048,7 +1048,11 @@ void Widget::setStatusMessage(const QString& statusMessage) */ void Widget::playNotificationSound(IAudioSink::Sound sound, bool loop) { - if (!settings.getAudioOutDevEnabled()) { + bool isBusy = core->getStatus() == Status::Status::Busy; + bool busySound = settings.getBusySound(); + bool notifySound = settings.getNotifySound(); + + if (!settings.getAudioOutDevEnabled() || !(notifySound && (!isBusy || busySound))) { // don't try to play sounds if audio is disabled return; } @@ -1574,7 +1578,8 @@ bool Widget::newFriendMessageAlert(const ToxPk& friendId, const QString& text, b notificationData = notificationGenerator->friendMessageNotification(f, text); } } else { - notificationData = notificationGenerator->fileTransferNotification(f, filename, filesize); + notificationData = + notificationGenerator->fileTransferNotification(f, filename, filesize); } notifier->notifyMessage(notificationData); } @@ -1674,11 +1679,8 @@ bool Widget::newMessageAlert(QWidget* currentWindow, bool isActive, bool sound, } eventFlag = true; } - const bool isBusy = core->getStatus() == Status::Status::Busy; - const bool busySound = settings.getBusySound(); - const bool notifySound = settings.getNotifySound(); - if (notifySound && sound && (!isBusy || busySound)) { + if (sound) { playNotificationSound(IAudioSink::Sound::NewMessage); } } From 8d2eb697f16eed40a204a4afadcf4a850f637575 Mon Sep 17 00:00:00 2001 From: Jared <7060603@hotmail.ca> Date: Mon, 13 Jan 2025 21:12:52 -0700 Subject: [PATCH 4/5] cleanup(Notify): Remove unused parameter --- src/model/notificationgenerator.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/model/notificationgenerator.cpp b/src/model/notificationgenerator.cpp index 17a643d492..34a158ce53 100644 --- a/src/model/notificationgenerator.cpp +++ b/src/model/notificationgenerator.cpp @@ -11,11 +11,10 @@ namespace { -QString generateContent(const QHash& friendNotifications, - const QHash& conferenceNotifications, +QString generateContent(const QHash& conferenceNotifications, QString lastMessage, const ToxPk& sender) { - assert(!friendNotifications.empty() || !conferenceNotifications.empty()); + assert(!conferenceNotifications.empty()); if (conferenceNotifications.size() == 1) { auto it = conferenceNotifications.begin(); @@ -95,7 +94,7 @@ NotificationData NotificationGenerator::conferenceMessageNotification(const Conf } ret.title = c->getDisplayedName(); - ret.message = generateContent(friendNotifications, conferenceNotifications, message, sender); + ret.message = generateContent(conferenceNotifications, message, sender); ret.category = "im.received"; ret.pixmap = getSenderAvatar(profile, sender); From b94060557683d2c29c00ee7950c1500b53563342 Mon Sep 17 00:00:00 2001 From: Jared <7060603@hotmail.ca> Date: Thu, 16 Jan 2025 22:25:01 -0700 Subject: [PATCH 5/5] cleanup(Notify): Fix showMessage declaration in desktopnotifybackend_noop.cpp --- .../desktop_notifications/desktopnotifybackend_noop.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/platform/desktop_notifications/desktopnotifybackend_noop.cpp b/src/platform/desktop_notifications/desktopnotifybackend_noop.cpp index 2d38f37d56..d79f3b5627 100644 --- a/src/platform/desktop_notifications/desktopnotifybackend_noop.cpp +++ b/src/platform/desktop_notifications/desktopnotifybackend_noop.cpp @@ -16,10 +16,11 @@ DesktopNotifyBackend::DesktopNotifyBackend(QObject* parent) DesktopNotifyBackend::~DesktopNotifyBackend() = default; bool DesktopNotifyBackend::showMessage(const QString& title, const QString& message, - const QPixmap& pixmap) + const QString& category, const QPixmap& pixmap) { std::ignore = title; std::ignore = message; + std::ignore = category; std::ignore = pixmap; // Always fail, fall back to QSystemTrayIcon. return false;