Преглед на файлове

Differentiate between normal normal and Talk notifications, invoke macOS text reply notifications

Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
Claudio Cambra преди 3 години
родител
ревизия
8913f88c50
променени са 4 файла, в които са добавени 104 реда и са изтрити 30 реда
  1. 12 0
      src/gui/systray.cpp
  2. 2 0
      src/gui/systray.h
  3. 80 27
      src/gui/tray/usermodel.cpp
  4. 10 3
      src/gui/tray/usermodel.h

+ 12 - 0
src/gui/systray.cpp

@@ -486,6 +486,18 @@ void Systray::showUpdateMessage(const QString &title, const QString &message, co
 #endif
 }
 
+void Systray::showTalkMessage(const QString &title, const QString &message, const QString &token, const QString &replyTo, const AccountStatePtr &accountState)
+{
+#if defined(Q_OS_MACOS) && defined(BUILD_OWNCLOUD_OSX_BUNDLE)
+    sendOsXTalkNotification(title, message, token, replyTo, accountState);
+#else // TODO: Implement custom notifications (i.e. actionable) for other OSes
+    Q_UNUSED(replyTo);
+    Q_UNUSED(token);
+    Q_UNUSED(accountState);
+    showMessage(title, message);
+#endif
+}
+
 void Systray::setToolTip(const QString &tip)
 {
     QSystemTrayIcon::setToolTip(tr("%1: %2").arg(Theme::instance()->appNameGUI(), tip));

+ 2 - 0
src/gui/systray.h

@@ -51,6 +51,7 @@ void registerNotificationCategories(const QString &localizedDownloadString);
 bool canOsXSendUserNotification();
 void sendOsXUserNotification(const QString &title, const QString &message);
 void sendOsXUpdateNotification(const QString &title, const QString &message, const QUrl &webUrl);
+void sendOsXTalkNotification(const QString &title, const QString &message, const QString &token, const QString &replyTo, const AccountStatePtr accountState);
 void setTrayWindowLevelAndVisibleOnAllSpaces(QWindow *window);
 double menuBarThickness();
 #endif
@@ -113,6 +114,7 @@ public slots:
 
     void showMessage(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon icon = Information);
     void showUpdateMessage(const QString &title, const QString &message, const QUrl &webUrl);
+    void showTalkMessage(const QString &title, const QString &message, const QString &replyTo, const QString &token, const AccountStatePtr &accountState);
     void setToolTip(const QString &tip);
 
     void createCallDialog(const OCC::Activity &callNotification, const OCC::AccountStatePtr accountState);

+ 80 - 27
src/gui/tray/usermodel.cpp

@@ -82,8 +82,60 @@ User::User(AccountStatePtr &account, const bool &isCurrent, QObject *parent)
     connect(this, &User::sendReplyMessage, this, &User::slotSendReplyMessage);
 }
 
+void User::checkNotifiedNotifications()
+{
+    // after one hour, clear the gui log notification store
+    constexpr qint64 clearGuiLogInterval = 60 * 60 * 1000;
+    if (_guiLogTimer.elapsed() > clearGuiLogInterval) {
+        _notifiedNotifications.clear();
+    }
+}
+
+bool User::notificationAlreadyShown(const long notificationId)
+{
+    checkNotifiedNotifications();
+    return _notifiedNotifications.contains(notificationId);
+}
+
+bool User::canShowNotification(const long notificationId)
+{
+    ConfigFile cfg;
+    return cfg.optionalServerNotifications() &&
+            isDesktopNotificationsAllowed() &&
+            !notificationAlreadyShown(notificationId);
+}
+
 void User::showDesktopNotification(const QString &title, const QString &message, const long notificationId)
 {
+    if(!canShowNotification(notificationId)) {
+        return;
+    }
+
+    _notifiedNotifications.insert(notificationId);
+    Logger::instance()->postGuiLog(title, message);
+    // restart the gui log timer now that we show a new notification
+    _guiLogTimer.start();
+}
+
+void User::showDesktopNotification(const Activity &activity)
+{
+    const auto notificationId = activity._id;
+    const auto message = AccountManager::instance()->accounts().count() == 1 ? "" : activity._accName;
+    showDesktopNotification(activity._subject, message, notificationId);
+}
+
+void User::showDesktopNotification(const ActivityList &activityList)
+{
+    const auto subject = QStringLiteral("%1 notifications").arg(activityList.count());
+    const auto notificationId = -static_cast<int>(qHash(subject));
+
+    if (!canShowNotification(notificationId)) {
+        return;
+    }
+
+    const auto multipleAccounts = AccountManager::instance()->accounts().count() > 1;
+    const auto message = multipleAccounts ? activityList.constFirst()._accName : QString();
+
     // Notification ids are uints, which are 4 bytes. Error activities don't have ids, however, so we generate one.
     // To avoid possible collisions between the activity ids which are actually the notification ids received from
     // the server (which are always positive) and our "fake" error activity ids, we assign a negative id to the
@@ -91,30 +143,40 @@ void User::showDesktopNotification(const QString &title, const QString &message,
     //
     // To ensure that we can still treat an unsigned int as normal, we use a long, which is 8 bytes.
 
-    ConfigFile cfg;
-    if (!cfg.optionalServerNotifications() || !isDesktopNotificationsAllowed()) {
-        return;
+    Logger::instance()->postGuiLog(subject, message);
+
+    for(const auto &activity : activityList) {
+        _notifiedNotifications.insert(activity._id);
+        _activityModel->addNotificationToActivityList(activity);
     }
+}
 
-    // after one hour, clear the gui log notification store
-    constexpr qint64 clearGuiLogInterval = 60 * 60 * 1000;
-    if (_guiLogTimer.elapsed() > clearGuiLogInterval) {
-        _notifiedNotifications.clear();
+void User::showDesktopTalkNotification(const Activity &activity)
+{
+    const auto notificationId = activity._id;
+
+    if (!canShowNotification(notificationId)) {
+        return;
     }
 
-    if (_notifiedNotifications.contains(notificationId)) {
+    if (activity._talkNotificationData.messageId.isEmpty()) {
+        showDesktopNotification(activity._subject, activity._message, notificationId);
         return;
     }
 
     _notifiedNotifications.insert(notificationId);
-    Logger::instance()->postGuiLog(title, message);
-    // restart the gui log timer now that we show a new notification
+    _activityModel->addNotificationToActivityList(activity);
+
+    Systray::instance()->showTalkMessage(activity._subject,
+                                         activity._message,
+                                         activity._talkNotificationData.conversationToken,
+                                         activity._talkNotificationData.messageId,
+                                         _account);
     _guiLogTimer.start();
 }
 
 void User::slotBuildNotificationDisplay(const ActivityList &list)
 {
-    const auto multipleAccounts = AccountManager::instance()->accounts().count() > 1;
     ActivityList toNotifyList;
 
     std::copy_if(list.constBegin(), list.constEnd(), std::back_inserter(toNotifyList), [&](const Activity &activity) {
@@ -131,25 +193,16 @@ void User::slotBuildNotificationDisplay(const ActivityList &list)
     });
 
     if(toNotifyList.count() > 2) {
-        const auto subject = QStringLiteral("%1 notifications").arg(toNotifyList.count());
-        const auto message = multipleAccounts ? toNotifyList.constFirst()._accName : QString();
-        showDesktopNotification(subject, message, -static_cast<int>(qHash(subject)));
-
-        // Set these activities as notified here, rather than in showDesktopNotification
-        for(const auto &activity : qAsConst(toNotifyList)) {
-            _notifiedNotifications.insert(activity._id);
-            _activityModel->addNotificationToActivityList(activity);
-        }
-
+        showDesktopNotification(toNotifyList);
         return;
     }
 
     for(const auto &activity : qAsConst(toNotifyList)) {
-        const auto message = activity._objectType == QStringLiteral("chat")
-            ? activity._message : AccountManager::instance()->accounts().count() == 1 ? "" : activity._accName;
-
-        showDesktopNotification(activity._subject, message, activity._id); // We assigned the notif. id to the activity id
-        _activityModel->addNotificationToActivityList(activity);
+        if (activity._objectType == QStringLiteral("chat")) {
+            showDesktopTalkNotification(activity);
+        } else {
+            showDesktopNotification(activity);
+        }
     }
 }
 
@@ -548,7 +601,7 @@ void User::slotAddErrorToGui(const QString &folderAlias, SyncFileItem::Status st
         // add 'other errors' to activity list
         _activityModel->addErrorToActivityList(activity);
 
-        showDesktopNotification(activity._subject, activity._message, activity._id);
+        showDesktopNotification(activity);
 
         if (!_expiredActivitiesCheckTimer.isActive()) {
             _expiredActivitiesCheckTimer.start(expiredActivitiesCheckIntervalMsecs);

+ 10 - 3
src/gui/tray/usermodel.h

@@ -110,22 +110,29 @@ public slots:
     void slotSendReplyMessage(const int activityIndex, const QString &conversationToken, const QString &message, const QString &replyTo);
     void forceSyncNow() const;
 
-private:
+private slots:
     void slotPushNotificationsReady();
     void slotDisconnectPushNotifications();
     void slotReceivedPushNotification(Account *account);
     void slotReceivedPushActivity(Account *account);
     void slotCheckExpiredActivities();
 
+    void checkNotifiedNotifications();
+    void showDesktopNotification(const QString &title, const QString &message, const long notificationId);
+    void showDesktopNotification(const Activity &activity);
+    void showDesktopNotification(const ActivityList &activityList);
+    void showDesktopTalkNotification(const Activity &activity);
+
+private:
     void connectPushNotifications() const;
     [[nodiscard]] bool checkPushNotificationsAreReady() const;
 
     bool isActivityOfCurrentAccount(const Folder *folder) const;
     [[nodiscard]] bool isUnsolvableConflict(const SyncFileItemPtr &item) const;
 
-    void showDesktopNotification(const QString &title, const QString &message, const long notificationId);
+    bool notificationAlreadyShown(const long notificationId);
+    bool canShowNotification(const long notificationId);
 
-private:
     AccountStatePtr _account;
     bool _isCurrentUser;
     ActivityListModel *_activityModel;