From f557ceda19a633c6e08c6d2d17407f322316a987 Mon Sep 17 00:00:00 2001 From: James Graham Date: Fri, 1 Mar 2024 17:56:13 +0000 Subject: [PATCH] Notification Consistency Make sure that the new rules for counting notifications for muted, mention and low priority rooms is applied consistently to the room list, space drawer and the task manager notification badge implements #644 --- src/controller.cpp | 47 ++++++++++++++ src/controller.h | 1 + src/models/roomlistmodel.cpp | 89 ++------------------------ src/models/roomlistmodel.h | 19 +----- src/models/roomtreemodel.cpp | 17 +++-- src/models/roomtreemodel.h | 4 +- src/models/sortfilterroomtreemodel.cpp | 4 +- src/neochatconnection.cpp | 88 ++++++++++++++++++------- src/neochatconnection.h | 21 ++++++ src/neochatroom.cpp | 26 ++++++++ src/neochatroom.h | 20 ++++++ src/qml/RoomDelegate.qml | 16 ++--- src/qml/SpaceDrawer.qml | 10 ++- src/spacehierarchycache.cpp | 25 +++++--- src/spacehierarchycache.h | 5 ++ 15 files changed, 238 insertions(+), 154 deletions(-) diff --git a/src/controller.cpp b/src/controller.cpp index e54d27052..09057312e 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -26,6 +26,7 @@ #include #include "neochatconfig.h" +#include "neochatconnection.h" #include "neochatroom.h" #include "notificationsmanager.h" #include "proxycontroller.h" @@ -37,6 +38,14 @@ #include "trayicon_sni.h" #endif +#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) +#ifndef Q_OS_ANDROID +#include +#include +#include +#endif +#endif + bool testMode = false; using namespace Quotient; @@ -149,6 +158,7 @@ void Controller::addConnection(NeoChatConnection *c) connect(c, &NeoChatConnection::loggedOut, this, [this, c] { dropConnection(c); }); + connect(c, &NeoChatConnection::badgeNotificationCountChanged, this, &Controller::updateBadgeNotificationCount); c->sync(); @@ -291,7 +301,14 @@ void Controller::setActiveConnection(NeoChatConnection *connection) if (connection == m_connection) { return; } + m_connection = connection; + + if (m_connection != nullptr) { + m_connection->refreshBadgeNotificationCount(); + updateBadgeNotificationCount(m_connection, m_connection->badgeNotificationCount()); + } + Q_EMIT activeConnectionChanged(); } @@ -316,6 +333,36 @@ void Controller::listenForNotifications() #endif } +void Controller::updateBadgeNotificationCount(NeoChatConnection *connection, int count) +{ + if (connection == m_connection) { +#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) +#ifndef Q_OS_ANDROID + // copied from Telegram desktop + const auto launcherUrl = "application://org.kde.neochat.desktop"_ls; + // Gnome requires that count is a 64bit integer + const qint64 counterSlice = std::min(count, 9999); + QVariantMap dbusUnityProperties; + + if (counterSlice > 0) { + dbusUnityProperties["count"_ls] = counterSlice; + dbusUnityProperties["count-visible"_ls] = true; + } else { + dbusUnityProperties["count-visible"_ls] = false; + } + + auto signal = QDBusMessage::createSignal("/com/canonical/unity/launcherentry/neochat"_ls, "com.canonical.Unity.LauncherEntry"_ls, "Update"_ls); + + signal.setArguments({launcherUrl, dbusUnityProperties}); + + QDBusConnection::sessionBus().send(signal); +#endif // Q_OS_ANDROID +#else + qGuiApp->setBadgeNumber(count); +#endif // QT_VERSION_CHECK(6, 6, 0) + } +} + bool Controller::isFlatpak() const { #ifdef NEOCHAT_FLATPAK diff --git a/src/controller.h b/src/controller.h index a2481e3a6..ba90d0dff 100644 --- a/src/controller.h +++ b/src/controller.h @@ -117,6 +117,7 @@ private: private Q_SLOTS: void invokeLogin(); void setQuitOnLastWindowClosed(); + void updateBadgeNotificationCount(NeoChatConnection *connection, int count); Q_SIGNALS: void errorOccured(const QString &error, const QString &detail); diff --git a/src/models/roomlistmodel.cpp b/src/models/roomlistmodel.cpp index c06d64aff..925539846 100644 --- a/src/models/roomlistmodel.cpp +++ b/src/models/roomlistmodel.cpp @@ -9,17 +9,7 @@ #include "roommanager.h" #include "spacehierarchycache.h" -#include -#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) -#ifndef Q_OS_ANDROID -#include -#include -#include -#endif -#endif - #include -#include using namespace Quotient; @@ -28,32 +18,6 @@ Q_DECLARE_METATYPE(Quotient::JoinState) RoomListModel::RoomListModel(QObject *parent) : QAbstractListModel(parent) { - connect(this, &RoomListModel::highlightCountChanged, this, [this]() { -#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) -#ifndef Q_OS_ANDROID - // copied from Telegram desktop - const auto launcherUrl = "application://org.kde.neochat.desktop"_ls; - // Gnome requires that count is a 64bit integer - const qint64 counterSlice = std::min(m_highlightCount, 9999); - QVariantMap dbusUnityProperties; - - if (counterSlice > 0) { - dbusUnityProperties["count"_ls] = counterSlice; - dbusUnityProperties["count-visible"_ls] = true; - } else { - dbusUnityProperties["count-visible"_ls] = false; - } - - auto signal = QDBusMessage::createSignal("/com/canonical/unity/launcherentry/neochat"_ls, "com.canonical.Unity.LauncherEntry"_ls, "Update"_ls); - - signal.setArguments({launcherUrl, dbusUnityProperties}); - - QDBusConnection::sessionBus().send(signal); -#endif // Q_OS_ANDROID -#else - qGuiApp->setBadgeNumber(m_highlightCount); -#endif // QT_VERSION_CHECK(6, 6, 0) - }); connect(&SpaceHierarchyCache::instance(), &SpaceHierarchyCache::spaceHierarchyChanged, this, [this]() { Q_EMIT dataChanged(index(0, 0), index(rowCount(), 0), {IsChildSpaceRole}); }); @@ -122,7 +86,6 @@ void RoomListModel::doResetModel() doAddRoom(room); } endResetModel(); - refreshNotificationCount(); } NeoChatRoom *RoomListModel::roomAt(int row) const @@ -148,7 +111,7 @@ void RoomListModel::connectRoomSignals(NeoChatRoom *room) refresh(room, {DisplayNameRole}); }); connect(room, &Room::unreadStatsChanged, this, [this, room] { - refresh(room, {NotificationCountRole, HighlightCountRole}); + refresh(room, {ContextNotificationCountRole, HasHighlightNotificationsRole}); }); connect(room, &Room::notificationCountChanged, this, [this, room] { refresh(room); @@ -171,44 +134,6 @@ void RoomListModel::connectRoomSignals(NeoChatRoom *room) connect(room, &Room::pendingEventMerged, this, [this, room] { refresh(room, {SubtitleTextRole}); }); - connect(room, &Room::unreadStatsChanged, this, &RoomListModel::refreshNotificationCount); - connect(room, &Room::highlightCountChanged, this, &RoomListModel::refreshHighlightCount); -} - -int RoomListModel::notificationCount() const -{ - return m_notificationCount; -} - -int RoomListModel::highlightCount() const -{ - return m_highlightCount; -} - -void RoomListModel::refreshNotificationCount() -{ - int count = 0; - for (auto room : std::as_const(m_rooms)) { - count += room->notificationCount(); - } - if (m_notificationCount == count) { - return; - } - m_notificationCount = count; - Q_EMIT notificationCountChanged(); -} - -void RoomListModel::refreshHighlightCount() -{ - int count = 0; - for (auto room : std::as_const(m_rooms)) { - count += room->highlightCount(); - } - if (m_highlightCount == count) { - return; - } - m_highlightCount = count; - Q_EMIT highlightCountChanged(); } void RoomListModel::updateRoom(Room *room, Room *prev) @@ -295,11 +220,11 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const if (role == CategoryRole) { return NeoChatRoomType::typeForRoom(room); } - if (role == NotificationCountRole) { - return room->notificationCount(); + if (role == ContextNotificationCountRole) { + return room->contextAwareNotificationCount(); } - if (role == HighlightCountRole) { - return room->highlightCount(); + if (role == HasHighlightNotificationsRole) { + return room->highlightCount() > 0 && room->contextAwareNotificationCount() > 0; } if (role == LastActiveTimeRole) { return room->lastActiveTime(); @@ -361,8 +286,8 @@ QHash RoomListModel::roleNames() const roles[CanonicalAliasRole] = "canonicalAlias"; roles[TopicRole] = "topic"; roles[CategoryRole] = "category"; - roles[NotificationCountRole] = "notificationCount"; - roles[HighlightCountRole] = "highlightCount"; + roles[ContextNotificationCountRole] = "contextNotificationCount"; + roles[HasHighlightNotificationsRole] = "hasHighlightNotifications"; roles[LastActiveTimeRole] = "lastActiveTime"; roles[JoinStateRole] = "joinState"; roles[CurrentRoomRole] = "currentRoom"; diff --git a/src/models/roomlistmodel.h b/src/models/roomlistmodel.h index 46bcea09d..8cdc2da66 100644 --- a/src/models/roomlistmodel.h +++ b/src/models/roomlistmodel.h @@ -31,11 +31,6 @@ class RoomListModel : public QAbstractListModel */ Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged) - /** - * @brief The total number of notifications for all the rooms. - */ - Q_PROPERTY(int notificationCount READ notificationCount NOTIFY notificationCountChanged) - public: /** * @brief Defines the model roles. @@ -46,8 +41,8 @@ public: CanonicalAliasRole, /**< The room canonical alias. */ TopicRole, /**< The room topic. */ CategoryRole, /**< The room category, e.g favourite. */ - NotificationCountRole, /**< The number of notifications in the room. */ - HighlightCountRole, /**< The number of highlighted messages in the room. */ + ContextNotificationCountRole, /**< The context aware notification count for the room. */ + HasHighlightNotificationsRole, /**< Whether there are any highlight notifications. */ LastActiveTimeRole, /**< The timestamp of the last event sent in the room. */ JoinStateRole, /**< The local user's join state in the room. */ CurrentRoomRole, /**< The room object for the room. */ @@ -67,9 +62,6 @@ public: [[nodiscard]] Quotient::Connection *connection() const; void setConnection(Quotient::Connection *connection); - [[nodiscard]] int notificationCount() const; - [[nodiscard]] int highlightCount() const; - /** * @brief Get the given role value at the given index. * @@ -114,23 +106,16 @@ private Q_SLOTS: void updateRoom(Quotient::Room *room, Quotient::Room *prev); void deleteRoom(Quotient::Room *room); void refresh(NeoChatRoom *room, const QList &roles = {}); - void refreshNotificationCount(); - void refreshHighlightCount(); private: Quotient::Connection *m_connection = nullptr; QList m_rooms; - int m_notificationCount = 0; - int m_highlightCount = 0; QString m_activeSpaceId; void connectRoomSignals(NeoChatRoom *room); Q_SIGNALS: void connectionChanged(); - void notificationCountChanged(); - void highlightCountChanged(); - void roomAdded(NeoChatRoom *_t1); }; diff --git a/src/models/roomtreemodel.cpp b/src/models/roomtreemodel.cpp index d78e644de..65c539a00 100644 --- a/src/models/roomtreemodel.cpp +++ b/src/models/roomtreemodel.cpp @@ -127,7 +127,7 @@ void RoomTreeModel::connectRoomSignals(NeoChatRoom *room) refreshRoomRoles(room, {DisplayNameRole}); }); connect(room, &Room::unreadStatsChanged, this, [this, room] { - refreshRoomRoles(room, {NotificationCountRole, HighlightCountRole}); + refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole}); }); connect(room, &Room::avatarChanged, this, [this, room] { refreshRoomRoles(room, {AvatarRole}); @@ -144,6 +144,9 @@ void RoomTreeModel::connectRoomSignals(NeoChatRoom *room) connect(room, &Room::pendingEventMerged, this, [this, room] { refreshRoomRoles(room, {SubtitleTextRole}); }); + connect(room, &NeoChatRoom::pushNotificationStateChanged, this, [this, room] { + refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole}); + }); } void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList &roles) @@ -208,8 +211,8 @@ QHash RoomTreeModel::roleNames() const roles[CanonicalAliasRole] = "canonicalAlias"; roles[TopicRole] = "topic"; roles[CategoryRole] = "category"; - roles[NotificationCountRole] = "notificationCount"; - roles[HighlightCountRole] = "highlightCount"; + roles[ContextNotificationCountRole] = "contextNotificationCount"; + roles[HasHighlightNotificationsRole] = "hasHighlightNotifications"; roles[LastActiveTimeRole] = "lastActiveTime"; roles[JoinStateRole] = "joinState"; roles[CurrentRoomRole] = "currentRoom"; @@ -271,11 +274,11 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const if (role == CategoryRole) { return NeoChatRoomType::typeForRoom(room); } - if (role == NotificationCountRole) { - return int(room->notificationCount()); + if (role == ContextNotificationCountRole) { + return int(room->contextAwareNotificationCount()); } - if (role == HighlightCountRole) { - return int(room->highlightCount()); + if (role == HasHighlightNotificationsRole) { + return room->highlightCount() > 0 && room->contextAwareNotificationCount() > 0; } if (role == LastActiveTimeRole) { return room->lastActiveTime(); diff --git a/src/models/roomtreemodel.h b/src/models/roomtreemodel.h index 2a28caa7a..4c3bae33a 100644 --- a/src/models/roomtreemodel.h +++ b/src/models/roomtreemodel.h @@ -33,8 +33,8 @@ public: CanonicalAliasRole, /**< The room canonical alias. */ TopicRole, /**< The room topic. */ CategoryRole, /**< The room category, e.g favourite. */ - NotificationCountRole, /**< The number of notifications in the room. */ - HighlightCountRole, /**< The number of highlighted messages in the room. */ + ContextNotificationCountRole, /**< The context aware notification count for the room. */ + HasHighlightNotificationsRole, /**< Whether there are any highlight notifications. */ LastActiveTimeRole, /**< The timestamp of the last event sent in the room. */ JoinStateRole, /**< The local user's join state in the room. */ CurrentRoomRole, /**< The room object for the room. */ diff --git a/src/models/sortfilterroomtreemodel.cpp b/src/models/sortfilterroomtreemodel.cpp index 539ebe3b5..5fb037e71 100644 --- a/src/models/sortfilterroomtreemodel.cpp +++ b/src/models/sortfilterroomtreemodel.cpp @@ -52,8 +52,8 @@ static const QVector activitySortPriorities{ // Anything useful at the top, quiet rooms at the bottom RoomTreeModel::AttentionRole, // Organize by highlights, notifications, unread favorites, all other unread, in that order - RoomTreeModel::HighlightCountRole, - RoomTreeModel::NotificationCountRole, + RoomTreeModel::HasHighlightNotificationsRole, + RoomTreeModel::ContextNotificationCountRole, RoomTreeModel::FavouriteRole, // Finally sort by last activity time RoomTreeModel::LastActiveTimeRole, diff --git a/src/neochatconnection.cpp b/src/neochatconnection.cpp index d9dddc396..1c1dce35c 100644 --- a/src/neochatconnection.cpp +++ b/src/neochatconnection.cpp @@ -77,7 +77,9 @@ void NeoChatConnection::connectSignals() for (const auto &chatId : additions) { if (const auto chat = room(chatId)) { connect(chat, &Room::unreadStatsChanged, this, [this]() { + refreshBadgeNotificationCount(); Q_EMIT directChatNotificationsChanged(); + Q_EMIT directChatsHaveHighlightNotificationsChanged(); }); } } @@ -91,29 +93,51 @@ void NeoChatConnection::connectSignals() if (room->isDirectChat()) { connect(room, &Room::unreadStatsChanged, this, [this]() { Q_EMIT directChatNotificationsChanged(); + Q_EMIT directChatsHaveHighlightNotificationsChanged(); }); } + connect(room, &Room::unreadStatsChanged, this, [this]() { + refreshBadgeNotificationCount(); + Q_EMIT homeNotificationsChanged(); + Q_EMIT homeHaveHighlightNotificationsChanged(); + }); }); connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) { Q_UNUSED(room) if (prev && prev->isDirectChat()) { Q_EMIT directChatInvitesChanged(); + Q_EMIT directChatNotificationsChanged(); + Q_EMIT directChatsHaveHighlightNotificationsChanged(); } + refreshBadgeNotificationCount(); + Q_EMIT homeNotificationsChanged(); + Q_EMIT homeHaveHighlightNotificationsChanged(); }); connect(&SpaceHierarchyCache::instance(), &SpaceHierarchyCache::spaceHierarchyChanged, this, [this]() { + refreshBadgeNotificationCount(); Q_EMIT homeNotificationsChanged(); + Q_EMIT homeHaveHighlightNotificationsChanged(); }); - for (const auto room : allRooms()) { - connect(room, &NeoChatRoom::unreadStatsChanged, this, [this, room]() { - if (room != nullptr) { - auto category = NeoChatRoomType::typeForRoom(static_cast(room)); - if (!SpaceHierarchyCache::instance().isChild(room->id()) && (category == NeoChatRoomType::Normal || category == NeoChatRoomType::Favorite) - && room->successorId().isEmpty()) { - Q_EMIT homeNotificationsChanged(); - } - } - }); +} + +int NeoChatConnection::badgeNotificationCount() const +{ + return m_badgeNotificationCount; +} + +void NeoChatConnection::refreshBadgeNotificationCount() +{ + int count = 0; + for (const auto &r : allRooms()) { + if (const auto room = static_cast(r)) { + count += room->contextAwareNotificationCount(); + } + } + + if (count != m_badgeNotificationCount) { + m_badgeNotificationCount = count; + Q_EMIT badgeNotificationCountChanged(this, m_badgeNotificationCount); } } @@ -329,7 +353,7 @@ qsizetype NeoChatConnection::directChatNotifications() const for (const auto &chatId : directChats()) { if (!added.contains(chatId)) { if (const auto chat = room(chatId)) { - notifications += chat->notificationCount(); + notifications += dynamic_cast(chat)->contextAwareNotificationCount(); added += chatId; } } @@ -337,29 +361,47 @@ qsizetype NeoChatConnection::directChatNotifications() const return notifications; } +bool NeoChatConnection::directChatsHaveHighlightNotifications() const +{ + for (const auto &childId : directChats()) { + if (const auto child = static_cast(room(childId))) { + if (child->highlightCount() > 0) { + return true; + } + } + } + return false; +} + qsizetype NeoChatConnection::homeNotifications() const { qsizetype notifications = 0; QStringList added; const auto &spaceHierarchyCache = SpaceHierarchyCache::instance(); - for (const auto &room : allRooms()) { - auto category = NeoChatRoomType::typeForRoom(static_cast(room)); - if (!added.contains(room->id()) && room->joinState() == JoinState::Join && !room->isDirectChat() && !spaceHierarchyCache.isChild(room->id()) - && room->successorId().isEmpty()) { - switch (category) { - case NeoChatRoomType::Normal: - case NeoChatRoomType::Favorite: - notifications += room->notificationCount(); - break; - default: - notifications += room->highlightCount(); + for (const auto &r : allRooms()) { + if (const auto room = static_cast(r)) { + if (!added.contains(room->id()) && !room->isDirectChat() && !spaceHierarchyCache.isChild(room->id())) { + notifications += dynamic_cast(room)->contextAwareNotificationCount(); + added += room->id(); } - added += room->id(); } } return notifications; } +bool NeoChatConnection::homeHaveHighlightNotifications() const +{ + const auto &spaceHierarchyCache = SpaceHierarchyCache::instance(); + for (const auto &r : allRooms()) { + if (const auto room = static_cast(r)) { + if (!room->isDirectChat() && !spaceHierarchyCache.isChild(room->id()) && room->highlightCount() > 0) { + return true; + } + } + } + return false; +} + bool NeoChatConnection::directChatInvites() const { auto inviteRooms = rooms(JoinState::Invite); diff --git a/src/neochatconnection.h b/src/neochatconnection.h index d42eacd7e..af03f74e2 100644 --- a/src/neochatconnection.h +++ b/src/neochatconnection.h @@ -32,11 +32,21 @@ class NeoChatConnection : public Quotient::Connection */ Q_PROPERTY(qsizetype directChatNotifications READ directChatNotifications NOTIFY directChatNotificationsChanged) + /** + * @brief Whether any direct chats have highlight notifications. + */ + Q_PROPERTY(bool directChatsHaveHighlightNotifications READ directChatsHaveHighlightNotifications NOTIFY directChatsHaveHighlightNotificationsChanged) + /** * @brief The total number of notifications for all rooms in the home tab. */ Q_PROPERTY(qsizetype homeNotifications READ homeNotifications NOTIFY homeNotificationsChanged) + /** + * @brief Whether any of the rooms in the home tab have highlight notifications. + */ + Q_PROPERTY(bool homeHaveHighlightNotifications READ homeHaveHighlightNotifications NOTIFY homeHaveHighlightNotificationsChanged) + /** * @brief Whether there is at least one invite to a direct chat. */ @@ -119,7 +129,13 @@ public: Q_INVOKABLE QString accountDataJsonString(const QString &type) const; qsizetype directChatNotifications() const; + bool directChatsHaveHighlightNotifications() const; qsizetype homeNotifications() const; + bool homeHaveHighlightNotifications() const; + + int badgeNotificationCount() const; + void refreshBadgeNotificationCount(); + bool directChatInvites() const; // note: this is intentionally a copied QString because @@ -134,15 +150,20 @@ public: Q_SIGNALS: void labelChanged(); void directChatNotificationsChanged(); + void directChatsHaveHighlightNotificationsChanged(); void homeNotificationsChanged(); + void homeHaveHighlightNotificationsChanged(); void directChatInvitesChanged(); void isOnlineChanged(); void passwordStatus(NeoChatConnection::PasswordStatus status); void userConsentRequired(QUrl url); + void badgeNotificationCountChanged(NeoChatConnection *connection, int count); private: bool m_isOnline = true; void setIsOnline(bool isOnline); void connectSignals(); + + int m_badgeNotificationCount = 0; }; diff --git a/src/neochatroom.cpp b/src/neochatroom.cpp index 57b998d0e..446efb9bb 100644 --- a/src/neochatroom.cpp +++ b/src/neochatroom.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -129,15 +130,32 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS connect(&SpaceHierarchyCache::instance(), &SpaceHierarchyCache::spaceHierarchyChanged, this, [this]() { if (isSpace()) { Q_EMIT childrenNotificationCountChanged(); + Q_EMIT childrenHaveHighlightNotificationsChanged(); } }); connect(&SpaceHierarchyCache::instance(), &SpaceHierarchyCache::spaceNotifcationCountChanged, this, [this](const QStringList &spaces) { if (spaces.contains(id())) { Q_EMIT childrenNotificationCountChanged(); + Q_EMIT childrenHaveHighlightNotificationsChanged(); } }); } +int NeoChatRoom::contextAwareNotificationCount() const +{ + // DOn't include spaces, rooms that the user hasn't joined and rooms where the user has joined the successor. + if (isSpace() || joinState() != JoinState::Join || successor(JoinState::Join) != nullptr) { + return 0; + } + if (m_currentPushNotificationState == PushNotificationState::Mute) { + return 0; + } + if (m_currentPushNotificationState == PushNotificationState::MentionKeyword || isLowPriority()) { + return int(highlightCount()); + } + return int(notificationCount()); +} + bool NeoChatRoom::hasFileUploading() const { return m_hasFileUploading; @@ -1300,6 +1318,14 @@ qsizetype NeoChatRoom::childrenNotificationCount() return SpaceHierarchyCache::instance().notificationCountForSpace(id()); } +bool NeoChatRoom::childrenHaveHighlightNotifications() const +{ + if (!isSpace()) { + return false; + } + return SpaceHierarchyCache::instance().spaceHasHighlightNotifications(id()); +} + void NeoChatRoom::addChild(const QString &childId, bool setChildParent, bool canonical, bool suggested) { if (!isSpace()) { diff --git a/src/neochatroom.h b/src/neochatroom.h index 989c60ac4..1821e0bdb 100644 --- a/src/neochatroom.h +++ b/src/neochatroom.h @@ -133,6 +133,13 @@ class NeoChatRoom : public Quotient::Room */ Q_PROPERTY(qsizetype childrenNotificationCount READ childrenNotificationCount NOTIFY childrenNotificationCountChanged) + /** + * @brief Whether this room's children have any highlight notifications. + * + * Will always return false if this is not a space. + */ + Q_PROPERTY(bool childrenHaveHighlightNotifications READ childrenHaveHighlightNotifications NOTIFY childrenHaveHighlightNotificationsChanged) + /** * @brief Whether the local user has an invite to the room. * @@ -405,6 +412,16 @@ public: */ [[nodiscard]] bool lastEventIsSpoiler() const; + /** + * @brief Return the notification count for the room accounting for tags and notification state. + * + * The following rules are observed: + * - Rooms tagged as low priority or mentions and keywords notification state + * only return the number of highlights. + * - Muted rooms always return 0. + */ + int contextAwareNotificationCount() const; + [[nodiscard]] bool hasFileUploading() const; void setHasFileUploading(bool value); @@ -535,6 +552,8 @@ public: qsizetype childrenNotificationCount(); + bool childrenHaveHighlightNotifications() const; + /** * @brief Add the given room as a child. * @@ -825,6 +844,7 @@ Q_SIGNALS: void canonicalParentChanged(); void lastActiveTimeChanged(); void childrenNotificationCountChanged(); + void childrenHaveHighlightNotificationsChanged(); void isInviteChanged(); void readOnlyChanged(); void displayNameChanged(); diff --git a/src/qml/RoomDelegate.qml b/src/qml/RoomDelegate.qml index 1ddf0d1fe..f8ffebd6b 100644 --- a/src/qml/RoomDelegate.qml +++ b/src/qml/RoomDelegate.qml @@ -18,8 +18,8 @@ Delegates.RoundedItemDelegate { id: root required property int index - required property int notificationCount - required property int highlightCount + required property int contextNotificationCount + required property bool hasHighlightNotifications required property NeoChatRoom currentRoom required property NeoChatConnection connection required property string avatar @@ -28,7 +28,7 @@ Delegates.RoundedItemDelegate { property bool collapsed: false - readonly property bool hasNotifications: currentRoom.pushNotificationState === PushNotificationState.MentionKeyword || currentRoom.isLowPriority ? highlightCount > 0 : notificationCount > 0 + readonly property bool hasNotifications: contextNotificationCount > 0 Accessible.name: root.displayName Accessible.onPressAction: clicked() @@ -106,16 +106,16 @@ Delegates.RoundedItemDelegate { QQC2.Label { id: notificationCountLabel - text: currentRoom.pushNotificationState === PushNotificationState.MentionKeyword || currentRoom.isLowPriority ? root.highlightCount : root.notificationCount - visible: root.hasNotifications && currentRoom.pushNotificationState !== PushNotificationState.Mute && !root.collapsed + text: root.contextNotificationCount + visible: root.hasNotifications && !root.collapsed color: Kirigami.Theme.textColor horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter background: Rectangle { visible: root.hasNotifications Kirigami.Theme.colorSet: Kirigami.Theme.Button - color: highlightCount > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.disabledTextColor - opacity: highlightCount > 0 ? 1 : 0.3 + Kirigami.Theme.inherit: false + color: root.hasHighlightNotifications > 0 ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor radius: height / 2 } @@ -141,7 +141,7 @@ Delegates.RoundedItemDelegate { } function createRoomListContextMenu() { - const component = Qt.createComponent(Qt.createComponent('org.kde.neochat', 'ContextMenu.qml')); + const component = Qt.createComponent('org.kde.neochat', 'ContextMenu.qml'); if (component.status === Component.Error) { console.error(component.errorString()); } diff --git a/src/qml/SpaceDrawer.qml b/src/qml/SpaceDrawer.qml index 12703f3a7..f1b8750ac 100644 --- a/src/qml/SpaceDrawer.qml +++ b/src/qml/SpaceDrawer.qml @@ -125,7 +125,8 @@ QQC2.Control { background: Rectangle { visible: true Kirigami.Theme.colorSet: Kirigami.Theme.Button - color: Kirigami.Theme.positiveTextColor + Kirigami.Theme.inherit: false + color: root.connection.homeHaveHighlightNotifications ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor radius: height / 2 } @@ -173,7 +174,8 @@ QQC2.Control { background: Rectangle { visible: true Kirigami.Theme.colorSet: Kirigami.Theme.Button - color: Kirigami.Theme.positiveTextColor + Kirigami.Theme.inherit: false + color: root.connection.directChatsHaveHighlightNotifications ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor radius: height / 2 } @@ -239,10 +241,12 @@ QQC2.Control { visible: spaceDelegate.currentRoom.childrenNotificationCount > 0 && root.selectedSpaceId != spaceDelegate.roomId color: Kirigami.Theme.textColor horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter background: Rectangle { visible: true Kirigami.Theme.colorSet: Kirigami.Theme.Button - color: Kirigami.Theme.positiveTextColor + Kirigami.Theme.inherit: false + color: spaceDelegate.currentRoom.childrenHaveHighlightNotifications ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.backgroundColor radius: height / 2 } diff --git a/src/spacehierarchycache.cpp b/src/spacehierarchycache.cpp index c24ab3a5b..4898ebad9 100644 --- a/src/spacehierarchycache.cpp +++ b/src/spacehierarchycache.cpp @@ -126,16 +126,8 @@ qsizetype SpaceHierarchyCache::notificationCountForSpace(const QString &spaceId) for (const auto &childId : children) { if (const auto child = static_cast(m_connection->room(childId))) { - auto category = NeoChatRoomType::typeForRoom(child); - if (!added.contains(child->id()) && child->successorId().isEmpty()) { - switch (category) { - case NeoChatRoomType::Normal: - case NeoChatRoomType::Favorite: - notifications += child->notificationCount(); - break; - default: - notifications += child->highlightCount(); - } + if (!added.contains(child->id())) { + notifications += child->contextAwareNotificationCount(); added += child->id(); } } @@ -143,6 +135,19 @@ qsizetype SpaceHierarchyCache::notificationCountForSpace(const QString &spaceId) return notifications; } +bool SpaceHierarchyCache::spaceHasHighlightNotifications(const QString &spaceId) +{ + auto children = m_spaceHierarchy[spaceId]; + for (const auto &childId : children) { + if (const auto child = static_cast(m_connection->room(childId))) { + if (child->highlightCount() > 0) { + return true; + } + } + } + return false; +} + bool SpaceHierarchyCache::isChild(const QString &roomId) const { const auto childrens = m_spaceHierarchy.values(); diff --git a/src/spacehierarchycache.h b/src/spacehierarchycache.h index 78ee31553..069e8f755 100644 --- a/src/spacehierarchycache.h +++ b/src/spacehierarchycache.h @@ -69,6 +69,11 @@ public: */ qsizetype notificationCountForSpace(const QString &spaceId); + /** + * @brief Whether any of the child rooms have highlight notifications. + */ + bool spaceHasHighlightNotifications(const QString &spaceId); + /** * @brief Returns whether the room is a child space of any space. *