Space notification count

Show the number of notifications for a space if it isn't selected. This respects choices like low priority only adding highlights.
This commit is contained in:
James Graham
2024-02-18 11:04:56 +00:00
parent fcf64a7e1b
commit 1b59917f16
9 changed files with 217 additions and 29 deletions

View File

@@ -298,30 +298,7 @@ QVariant RoomListModel::data(const QModelIndex &index, int role) const
return room->topic();
}
if (role == CategoryRole) {
if (room->joinState() == JoinState::Invite) {
return NeoChatRoomType::Invited;
}
if (room->isFavourite()) {
return NeoChatRoomType::Favorite;
}
if (room->isLowPriority()) {
return NeoChatRoomType::Deprioritized;
}
if (room->isDirectChat()) {
return NeoChatRoomType::Direct;
}
const RoomCreateEvent *creationEvent = room->creation();
if (!creationEvent) {
return NeoChatRoomType::Normal;
}
QJsonObject contentJson = creationEvent->contentJson();
QJsonObject::const_iterator typeIter = contentJson.find("type"_ls);
if (typeIter != contentJson.end()) {
if (typeIter.value().toString() == "m.space"_ls) {
return NeoChatRoomType::Space;
}
}
return NeoChatRoomType::Normal;
return category(room);
}
if (role == NotificationCountRole) {
return room->notificationCount();
@@ -406,6 +383,26 @@ QHash<int, QByteArray> RoomListModel::roleNames() const
return roles;
}
NeoChatRoomType::Types RoomListModel::category(NeoChatRoom *room)
{
if (room->isSpace()) {
return NeoChatRoomType::Space;
}
if (room->joinState() == JoinState::Invite) {
return NeoChatRoomType::Invited;
}
if (room->isFavourite()) {
return NeoChatRoomType::Favorite;
}
if (room->isLowPriority()) {
return NeoChatRoomType::Deprioritized;
}
if (room->isDirectChat()) {
return NeoChatRoomType::Direct;
}
return NeoChatRoomType::Normal;
}
QString RoomListModel::categoryName(int category)
{
switch (category) {

View File

@@ -116,6 +116,11 @@ public:
*/
Q_INVOKABLE [[nodiscard]] NeoChatRoom *roomAt(int row) const;
/**
* @brief The category for the given room.
*/
static NeoChatRoomType::Types category(NeoChatRoom *room);
/**
* @brief Return a string to represent the given room category.
*/

View File

@@ -8,7 +8,9 @@
#include "controller.h"
#include "jobs/neochatchangepasswordjob.h"
#include "jobs/neochatdeactivateaccountjob.h"
#include "neochatroom.h"
#include "roommanager.h"
#include "spacehierarchycache.h"
#include <Quotient/connection.h>
#include <Quotient/quotient_common.h>
@@ -97,6 +99,21 @@ void NeoChatConnection::connectSignals()
Q_EMIT directChatInvitesChanged();
}
});
connect(&SpaceHierarchyCache::instance(), &SpaceHierarchyCache::spaceHierarchyChanged, this, [this]() {
Q_EMIT homeNotificationsChanged();
});
for (const auto room : allRooms()) {
connect(room, &NeoChatRoom::unreadStatsChanged, this, [this, room]() {
if (room != nullptr) {
auto category = RoomListModel::category(static_cast<NeoChatRoom *>(room));
if (!SpaceHierarchyCache::instance().isChild(room->id()) && (category == NeoChatRoomType::Normal || category == NeoChatRoomType::Favorite)
&& room->successorId().isEmpty()) {
Q_EMIT homeNotificationsChanged();
}
}
});
}
}
void NeoChatConnection::logout(bool serverSideLogout)
@@ -319,6 +336,29 @@ qsizetype NeoChatConnection::directChatNotifications() const
return notifications;
}
qsizetype NeoChatConnection::homeNotifications() const
{
qsizetype notifications = 0;
QStringList added;
const auto &spaceHierarchyCache = SpaceHierarchyCache::instance();
for (const auto &room : allRooms()) {
auto category = RoomListModel::category(static_cast<NeoChatRoom *>(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();
}
added += room->id();
}
}
return notifications;
}
bool NeoChatConnection::directChatInvites() const
{
auto inviteRooms = rooms(JoinState::Invite);

View File

@@ -32,6 +32,11 @@ class NeoChatConnection : public Quotient::Connection
*/
Q_PROPERTY(qsizetype directChatNotifications READ directChatNotifications NOTIFY directChatNotificationsChanged)
/**
* @brief The total number of notifications for all rooms in the home tab.
*/
Q_PROPERTY(qsizetype homeNotifications READ homeNotifications NOTIFY homeNotificationsChanged)
/**
* @brief Whether there is at least one invite to a direct chat.
*/
@@ -109,6 +114,7 @@ public:
Q_INVOKABLE void openOrCreateDirectChat(Quotient::User *user);
qsizetype directChatNotifications() const;
qsizetype homeNotifications() const;
bool directChatInvites() const;
// note: this is intentionally a copied QString because
@@ -123,6 +129,7 @@ public:
Q_SIGNALS:
void labelChanged();
void directChatNotificationsChanged();
void homeNotificationsChanged();
void directChatInvitesChanged();
void isOnlineChanged();
void passwordStatus(NeoChatConnection::PasswordStatus status);

View File

@@ -42,6 +42,7 @@
#include "neochatconfig.h"
#include "notificationsmanager.h"
#include "roomlastmessageprovider.h"
#include "spacehierarchycache.h"
#include "texthandler.h"
#include "urlhelper.h"
#include "utils.h"
@@ -125,6 +126,16 @@ NeoChatRoom::NeoChatRoom(Connection *connection, QString roomId, JoinState joinS
Q_EMIT urlPreviewEnabledChanged();
}
});
connect(&SpaceHierarchyCache::instance(), &SpaceHierarchyCache::spaceHierarchyChanged, this, [this]() {
if (isSpace()) {
Q_EMIT childrenNotificationCountChanged();
}
});
connect(&SpaceHierarchyCache::instance(), &SpaceHierarchyCache::spaceNotifcationCountChanged, this, [this](const QStringList &spaces) {
if (spaces.contains(id())) {
Q_EMIT childrenNotificationCountChanged();
}
});
}
bool NeoChatRoom::hasFileUploading() const
@@ -1278,6 +1289,14 @@ bool NeoChatRoom::isSpace()
return creationEvent->roomType() == RoomType::Space;
}
qsizetype NeoChatRoom::childrenNotificationCount()
{
if (!isSpace()) {
return 0;
}
return SpaceHierarchyCache::instance().notificationCountForSpace(id());
}
void NeoChatRoom::addChild(const QString &childId, bool setChildParent, bool canonical, bool suggested)
{
if (!isSpace()) {

View File

@@ -126,6 +126,13 @@ class NeoChatRoom : public Quotient::Room
*/
Q_PROPERTY(bool isSpace READ isSpace CONSTANT)
/**
* @brief The number of notifications in this room's children.
*
* Will always return 0 if this is not a space.
*/
Q_PROPERTY(qsizetype childrenNotificationCount READ childrenNotificationCount NOTIFY childrenNotificationCountChanged)
/**
* @brief Whether the local user has an invite to the room.
*
@@ -526,6 +533,8 @@ public:
[[nodiscard]] bool isSpace();
qsizetype childrenNotificationCount();
/**
* @brief Add the given room as a child.
*
@@ -815,6 +824,7 @@ Q_SIGNALS:
void parentIdsChanged();
void canonicalParentChanged();
void lastActiveTimeChanged();
void childrenNotificationCountChanged();
void isInviteChanged();
void readOnlyChanged();
void displayNameChanged();

View File

@@ -108,6 +108,32 @@ QQC2.Control {
RoomManager.lastSpaceId = "";
root.selectionChanged();
}
QQC2.Label {
id: homeNotificationCountLabel
anchors.top: parent.top
anchors.right: parent.right
anchors.rightMargin: Kirigami.Units.smallSpacing / 2
z: 1
width: Math.max(homeNotificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
height: Kirigami.Units.iconSizes.smallMedium
text: root.connection.homeNotifications > 0 ? root.connection.homeNotifications : ""
visible: root.connection.homeNotifications > 0 && (root.selectedSpaceId !== "" || root.showDirectChats === true)
color: Kirigami.Theme.textColor
horizontalAlignment: Text.AlignHCenter
background: Rectangle {
visible: true
Kirigami.Theme.colorSet: Kirigami.Theme.Button
color: Kirigami.Theme.positiveTextColor
radius: height / 2
}
TextMetrics {
id: homeNotificationCountTextMetrics
text: homeNotificationCountLabel.text
}
}
}
AvatarTabButton {
id: directChatButton
@@ -132,16 +158,16 @@ QQC2.Control {
}
QQC2.Label {
id: notificationCountLabel
id: directChatNotificationCountLabel
anchors.top: parent.top
anchors.right: parent.right
anchors.rightMargin: Kirigami.Units.smallSpacing / 2
z: 1
width: Math.max(notificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
width: Math.max(directChatNotificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
height: Kirigami.Units.iconSizes.smallMedium
text: root.connection.directChatNotifications > 0 ? root.connection.directChatNotifications : ""
visible: root.connection.directChatNotifications > 0 || root.connection.directChatInvites
visible: (root.connection.directChatNotifications > 0 || root.connection.directChatInvites) && root.showDirectChats === false
color: Kirigami.Theme.textColor
horizontalAlignment: Text.AlignHCenter
background: Rectangle {
@@ -152,8 +178,8 @@ QQC2.Control {
}
TextMetrics {
id: notificationCountTextMetrics
text: notificationCountLabel.text
id: directChatNotificationCountTextMetrics
text: directChatNotificationCountLabel.text
}
}
}
@@ -199,6 +225,32 @@ QQC2.Control {
}
checked: root.selectedSpaceId === roomId
onContextMenuRequested: root.createContextMenu(currentRoom)
QQC2.Label {
id: notificationCountLabel
anchors.top: parent.top
anchors.right: parent.right
anchors.rightMargin: Kirigami.Units.smallSpacing / 2
z: 1
width: Math.max(notificationCountTextMetrics.advanceWidth + Kirigami.Units.smallSpacing * 2, height)
height: Kirigami.Units.iconSizes.smallMedium
text: spaceDelegate.currentRoom.childrenNotificationCount > 0 ? spaceDelegate.currentRoom.childrenNotificationCount : ""
visible: spaceDelegate.currentRoom.childrenNotificationCount > 0 && root.selectedSpaceId != spaceDelegate.roomId
color: Kirigami.Theme.textColor
horizontalAlignment: Text.AlignHCenter
background: Rectangle {
visible: true
Kirigami.Theme.colorSet: Kirigami.Theme.Button
color: Kirigami.Theme.positiveTextColor
radius: height / 2
}
TextMetrics {
id: notificationCountTextMetrics
text: notificationCountLabel.text
}
}
}
}

View File

@@ -7,6 +7,7 @@
#include <Quotient/qt_connection_util.h>
#include "neochatroom.h"
#include "roomlistmodel.h"
using namespace Quotient;
@@ -33,6 +34,15 @@ void SpaceHierarchyCache::cacheSpaceHierarchy()
}
});
}
connect(neoChatRoom, &NeoChatRoom::unreadStatsChanged, this, [this, neoChatRoom]() {
if (neoChatRoom != nullptr) {
const auto parents = parentSpaces(neoChatRoom->id());
if (parents.count() > 0) {
Q_EMIT spaceNotifcationCountChanged(parents);
}
}
});
}
}
@@ -74,6 +84,18 @@ void SpaceHierarchyCache::removeSpaceFromHierarchy(Quotient::Room *room)
}
}
QStringList SpaceHierarchyCache::parentSpaces(const QString &roomId)
{
auto spaces = m_spaceHierarchy.keys();
QStringList parents;
for (const auto &space : spaces) {
if (m_spaceHierarchy[space].contains(roomId)) {
parents += space;
}
}
return parents;
}
bool SpaceHierarchyCache::isSpaceChild(const QString &spaceId, const QString &roomId)
{
return getRoomListForSpace(spaceId, false).contains(roomId);
@@ -87,6 +109,31 @@ QList<QString> &SpaceHierarchyCache::getRoomListForSpace(const QString &spaceId,
return m_spaceHierarchy[spaceId];
}
qsizetype SpaceHierarchyCache::notificationCountForSpace(const QString &spaceId)
{
qsizetype notifications = 0;
auto children = m_spaceHierarchy[spaceId];
QStringList added;
for (const auto &childId : children) {
if (const auto child = static_cast<NeoChatRoom *>(m_connection->room(childId))) {
auto category = RoomListModel::category(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();
}
added += child->id();
}
}
}
return notifications;
}
bool SpaceHierarchyCache::isChild(const QString &roomId) const
{
const auto childrens = m_spaceHierarchy.values();

View File

@@ -44,6 +44,11 @@ public:
return &instance();
}
/**
* @brief Returns the list of parent spaces for a child if any.
*/
QStringList parentSpaces(const QString &roomId);
/**
* @brief Whether the given room is a member of the given space.
*/
@@ -54,6 +59,11 @@ public:
*/
[[nodiscard]] QList<QString> &getRoomListForSpace(const QString &spaceId, bool updateCache);
/**
* @brief Return the number of notifications for the child rooms in a given space ID.
*/
qsizetype notificationCountForSpace(const QString &spaceId);
/**
* @brief Returns whether the room is a child space of any space.
*
@@ -69,6 +79,7 @@ public:
Q_SIGNALS:
void spaceHierarchyChanged();
void connectionChanged();
void spaceNotifcationCountChanged(const QStringList &spaces);
private Q_SLOTS:
void addSpaceToHierarchy(Quotient::Room *room);