summary refs log tree commit diff
path: root/src/timeline/CommunitiesModel.cpp
diff options
context:
space:
mode:
authorDeepBlueV7.X <nicolas.werner@hotmail.de>2022-07-16 01:10:07 +0000
committerGitHub <noreply@github.com>2022-07-16 01:10:07 +0000
commitf62cb7768048bf737d3ec3b483a14e2e18cc1017 (patch)
treefebc4d195b7fa3c9a4f593d33346790c7d43f933 /src/timeline/CommunitiesModel.cpp
parentMerge pull request #1110 from r0hit05/msc2746 (diff)
parentMake notification count calculation more efficient (diff)
downloadnheko-f62cb7768048bf737d3ec3b483a14e2e18cc1017.tar.xz
Merge pull request #1062 from Nheko-Reborn/perSpaceNotifs
Per-space notification bubbles
Diffstat (limited to 'src/timeline/CommunitiesModel.cpp')
-rw-r--r--src/timeline/CommunitiesModel.cpp231
1 files changed, 207 insertions, 24 deletions
diff --git a/src/timeline/CommunitiesModel.cpp b/src/timeline/CommunitiesModel.cpp

index 4f650f49..c75f4265 100644 --- a/src/timeline/CommunitiesModel.cpp +++ b/src/timeline/CommunitiesModel.cpp
@@ -9,11 +9,15 @@ #include "Cache.h" #include "Cache_p.h" +#include "ChatPage.h" #include "Logging.h" #include "UserSettingsPage.h" +#include "Utils.h" CommunitiesModel::CommunitiesModel(QObject *parent) : QAbstractListModel(parent) + , hiddenTagIds_{UserSettings::instance()->hiddenTags()} + , mutedTagIds_{UserSettings::instance()->mutedTags()} {} QHash<int, QByteArray> @@ -28,6 +32,9 @@ CommunitiesModel::roleNames() const {Hidden, "hidden"}, {Depth, "depth"}, {Id, "id"}, + {UnreadMessages, "unreadMessages"}, + {HasLoudNotification, "hasLoudNotification"}, + {Muted, "muted"}, }; } @@ -50,6 +57,13 @@ CommunitiesModel::setData(const QModelIndex &index, const QVariant &value, int r QVariant CommunitiesModel::data(const QModelIndex &index, int role) const { + if (role == CommunitiesModel::Roles::Muted) { + if (index.row() == 0) + return mutedTagIds_.contains(QStringLiteral("global")); + else + return mutedTagIds_.contains(data(index, CommunitiesModel::Roles::Id).toString()); + } + if (index.row() == 0) { switch (role) { case CommunitiesModel::Roles::AvatarUrl: @@ -70,6 +84,10 @@ CommunitiesModel::data(const QModelIndex &index, int role) const return 0; case CommunitiesModel::Roles::Id: return ""; + case CommunitiesModel::Roles::UnreadMessages: + return (int)globalUnreads.notification_count; + case CommunitiesModel::Roles::HasLoudNotification: + return globalUnreads.highlight_count > 0; } } else if (index.row() == 1) { switch (role) { @@ -84,16 +102,20 @@ CommunitiesModel::data(const QModelIndex &index, int role) const case CommunitiesModel::Roles::Collapsible: return false; case CommunitiesModel::Roles::Hidden: - return hiddentTagIds_.contains(QStringLiteral("dm")); + return hiddenTagIds_.contains(QStringLiteral("dm")); case CommunitiesModel::Roles::Parent: return ""; case CommunitiesModel::Roles::Depth: return 0; case CommunitiesModel::Roles::Id: return "dm"; + case CommunitiesModel::Roles::UnreadMessages: + return (int)dmUnreads.notification_count; + case CommunitiesModel::Roles::HasLoudNotification: + return dmUnreads.highlight_count > 0; } } else if (index.row() - 2 < spaceOrder_.size()) { - auto id = spaceOrder_.tree.at(index.row() - 2).name; + auto id = spaceOrder_.tree.at(index.row() - 2).id; switch (role) { case CommunitiesModel::Roles::AvatarUrl: return QString::fromStdString(spaces_.at(id).avatar_url); @@ -107,10 +129,10 @@ CommunitiesModel::data(const QModelIndex &index, int role) const return idx != spaceOrder_.lastChild(idx); } case CommunitiesModel::Roles::Hidden: - return hiddentTagIds_.contains("space:" + id); + return hiddenTagIds_.contains("space:" + id); case CommunitiesModel::Roles::Parent: { if (auto p = spaceOrder_.parent(index.row() - 2); p >= 0) - return spaceOrder_.tree[p].name; + return spaceOrder_.tree[p].id; return ""; } @@ -118,6 +140,20 @@ CommunitiesModel::data(const QModelIndex &index, int role) const return spaceOrder_.tree.at(index.row() - 2).depth; case CommunitiesModel::Roles::Id: return "space:" + id; + case CommunitiesModel::Roles::UnreadMessages: { + int count = 0; + auto end = spaceOrder_.lastChild(index.row() - 2); + for (int i = index.row() - 2; i <= end; i++) + count += spaceOrder_.tree[i].notificationCounts.notification_count; + return count; + } + case CommunitiesModel::Roles::HasLoudNotification: { + auto end = spaceOrder_.lastChild(index.row() - 2); + for (int i = index.row() - 2; i <= end; i++) + if (spaceOrder_.tree[i].notificationCounts.highlight_count > 0) + return true; + return false; + } } } else if (index.row() - 2 < tags_.size() + spaceOrder_.size()) { auto tag = tags_.at(index.row() - 2 - spaceOrder_.size()); @@ -160,7 +196,7 @@ CommunitiesModel::data(const QModelIndex &index, int role) const switch (role) { case CommunitiesModel::Roles::Hidden: - return hiddentTagIds_.contains("tag:" + tag); + return hiddenTagIds_.contains("tag:" + tag); case CommunitiesModel::Roles::Collapsed: return true; case CommunitiesModel::Roles::Collapsible: @@ -171,6 +207,16 @@ CommunitiesModel::data(const QModelIndex &index, int role) const return 0; case CommunitiesModel::Roles::Id: return "tag:" + tag; + case CommunitiesModel::Roles::UnreadMessages: + if (auto it = tagNotificationCache.find(tag); it != tagNotificationCache.end()) + return (int)it->second.notification_count; + else + return 0; + case CommunitiesModel::Roles::HasLoudNotification: + if (auto it = tagNotificationCache.find(tag); it != tagNotificationCache.end()) + return it->second.highlight_count > 0; + else + return 0; } } return QVariant(); @@ -225,6 +271,21 @@ CommunitiesModel::initializeSidebar() tags_.clear(); spaceOrder_.tree.clear(); spaces_.clear(); + tagNotificationCache.clear(); + globalUnreads.notification_count = {}; + dmUnreads.notification_count = {}; + + auto e = cache::client()->getAccountData(mtx::events::EventType::Direct); + if (e) { + if (auto event = + std::get_if<mtx::events::AccountDataEvent<mtx::events::account_data::Direct>>( + &e.value())) { + directMessages_.clear(); + for (const auto &[userId, roomIds] : event->content.user_to_rooms) + for (const auto &roomId : roomIds) + directMessages_.push_back(roomId); + } + } std::set<std::string> ts; @@ -244,6 +305,19 @@ CommunitiesModel::initializeSidebar() } } } + + for (const auto &t : it->tags) { + auto tagId = QString::fromStdString(t); + auto &tNs = tagNotificationCache[tagId]; + tNs.notification_count += it->notification_count; + tNs.highlight_count += it->highlight_count; + } + + auto &e = roomNotificationCache[it.key()]; + e.highlight_count = it->highlight_count; + e.notification_count = it->notification_count; + globalUnreads.notification_count += it->notification_count; + globalUnreads.highlight_count += it->highlight_count; } // NOTE(Nico): We build a forrest from the Directed Cyclic(!) Graph of spaces. To do that we @@ -277,8 +351,16 @@ CommunitiesModel::initializeSidebar() for (const auto &t : ts) tags_.push_back(QString::fromStdString(t)); - hiddentTagIds_ = UserSettings::instance()->hiddenTags(); spaceOrder_.restoreCollapsed(); + + for (auto &space : spaceOrder_.tree) { + for (const auto &c : cache::client()->getChildRoomIds(space.id.toStdString())) { + const auto &counts = roomNotificationCache[QString::fromStdString(c)]; + space.notificationCounts.highlight_count += counts.highlight_count; + space.notificationCounts.notification_count += counts.notification_count; + } + } + endResetModel(); emit tagsChanged(); @@ -298,12 +380,12 @@ CommunitiesModel::FlatTree::storeCollapsed() for (const auto &e : tree) { if (e.depth > depth) { - current.push_back(e.name); + current.push_back(e.id); } else if (e.depth == depth) { - current.back() = e.name; + current.back() = e.id; } else { current.pop_back(); - current.back() = e.name; + current.back() = e.id; } if (e.collapsed) @@ -323,12 +405,12 @@ CommunitiesModel::FlatTree::restoreCollapsed() for (auto &e : tree) { if (e.depth > depth) { - current.push_back(e.name); + current.push_back(e.id); } else if (e.depth == depth) { - current.back() = e.name; + current.back() = e.id; } else { current.pop_back(); - current.back() = e.name; + current.back() = e.id; } if (elements.contains(current)) @@ -353,7 +435,6 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_) bool tagsUpdated = false; for (const auto &[roomid, room] : sync_.rooms.join) { - (void)roomid; for (const auto &e : room.account_data.events) if (std::holds_alternative< mtx::events::AccountDataEvent<mtx::events::account_data::Tags>>(e)) { @@ -373,6 +454,78 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_) e)) { tagsUpdated = true; } + + auto roomId = QString::fromStdString(roomid); + auto &oldUnreads = roomNotificationCache[roomId]; + auto notificationCDiff = -static_cast<int64_t>(oldUnreads.notification_count) + + static_cast<int64_t>(room.unread_notifications.notification_count); + auto highlightCDiff = -static_cast<int64_t>(oldUnreads.highlight_count) + + static_cast<int64_t>(room.unread_notifications.highlight_count); + + auto applyDiff = [notificationCDiff, + highlightCDiff](mtx::responses::UnreadNotifications &n) { + n.highlight_count = static_cast<int64_t>(n.highlight_count) + highlightCDiff; + n.notification_count = static_cast<int64_t>(n.notification_count) + notificationCDiff; + }; + if (highlightCDiff || notificationCDiff) { + // bool hidden = hiddenTagIds_.contains(roomId); + applyDiff(globalUnreads); + emit dataChanged(index(0), + index(0), + { + UnreadMessages, + HasLoudNotification, + }); + if (std::find(begin(directMessages_), end(directMessages_), roomid) != + end(directMessages_)) { + applyDiff(dmUnreads); + emit dataChanged(index(1), + index(1), + { + UnreadMessages, + HasLoudNotification, + }); + } + + auto spaces = cache::client()->getParentRoomIds(roomid); + auto tags = cache::singleRoomInfo(roomid).tags; + + for (const auto &t : tags) { + auto tagId = QString::fromStdString(t); + applyDiff(tagNotificationCache[tagId]); + int idx = tags_.indexOf(tagId) + 2 + spaceOrder_.size(); + emit dataChanged(index(idx), + index(idx), + { + UnreadMessages, + HasLoudNotification, + }); + } + + for (const auto &s : spaces) { + auto spaceId = QString::fromStdString(s); + + for (int i = 0; i < spaceOrder_.size(); i++) { + if (spaceOrder_.tree[i].id != spaceId) + continue; + + applyDiff(spaceOrder_.tree[i].notificationCounts); + + int idx = i; + do { + emit dataChanged(index(idx + 2), + index(idx + 2), + { + UnreadMessages, + HasLoudNotification, + }); + idx = spaceOrder_.parent(idx); + } while (idx != -1); + } + } + } + + roomNotificationCache[roomId] = room.unread_notifications; } for (const auto &[roomid, room] : sync_.rooms.leave) { (void)room; @@ -380,8 +533,12 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_) tagsUpdated = true; } for (const auto &e : sync_.account_data.events) { - if (std::holds_alternative< - mtx::events::AccountDataEvent<mtx::events::account_data::Direct>>(e)) { + if (auto event = + std::get_if<mtx::events::AccountDataEvent<mtx::events::account_data::Direct>>(&e)) { + directMessages_.clear(); + for (const auto &[userId, roomIds] : event->content.user_to_rooms) + for (const auto &roomId : roomIds) + directMessages_.push_back(roomId); tagsUpdated = true; break; } @@ -392,7 +549,7 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_) } void -CommunitiesModel::setCurrentTagId(QString tagId) +CommunitiesModel::setCurrentTagId(const QString &tagId) { if (tagId.startsWith(QLatin1String("tag:"))) { auto tag = tagId.mid(4); @@ -406,7 +563,7 @@ CommunitiesModel::setCurrentTagId(QString tagId) } else if (tagId.startsWith(QLatin1String("space:"))) { auto tag = tagId.mid(6); for (const auto &t : spaceOrder_.tree) { - if (t.name == tag) { + if (t.id == tag) { this->currentTagId_ = tagId; emit currentTagIdChanged(currentTagId_); return; @@ -425,13 +582,11 @@ CommunitiesModel::setCurrentTagId(QString tagId) void CommunitiesModel::toggleTagId(QString tagId) { - if (hiddentTagIds_.contains(tagId)) { - hiddentTagIds_.removeOne(tagId); - UserSettings::instance()->setHiddenTags(hiddentTagIds_); - } else { - hiddentTagIds_.push_back(tagId); - UserSettings::instance()->setHiddenTags(hiddentTagIds_); - } + if (hiddenTagIds_.contains(tagId)) + hiddenTagIds_.removeOne(tagId); + else + hiddenTagIds_.push_back(tagId); + UserSettings::instance()->setHiddenTags(hiddenTagIds_); if (tagId.startsWith(QLatin1String("tag:"))) { auto idx = tags_.indexOf(tagId.mid(4)); @@ -449,6 +604,34 @@ CommunitiesModel::toggleTagId(QString tagId) emit hiddenTagsChanged(); } +void +CommunitiesModel::toggleTagMute(QString tagId) +{ + if (tagId.isEmpty()) + tagId = QStringLiteral("global"); + + if (mutedTagIds_.contains(tagId)) + mutedTagIds_.removeOne(tagId); + else + mutedTagIds_.push_back(tagId); + UserSettings::instance()->setMutedTags(mutedTagIds_); + + if (tagId.startsWith(QLatin1String("tag:"))) { + auto idx = tags_.indexOf(tagId.mid(4)); + if (idx != -1) + emit dataChanged(index(idx + 1 + spaceOrder_.size()), + index(idx + 1 + spaceOrder_.size())); + } else if (tagId.startsWith(QLatin1String("space:"))) { + auto idx = spaceOrder_.indexOf(tagId.mid(6)); + if (idx != -1) + emit dataChanged(index(idx + 1), index(idx + 1)); + } else if (tagId == QLatin1String("dm")) { + emit dataChanged(index(1), index(1)); + } else if (tagId == QLatin1String("global")) { + emit dataChanged(index(0), index(0)); + } +} + FilteredCommunitiesModel::FilteredCommunitiesModel(CommunitiesModel *model, QObject *parent) : QSortFilterProxyModel(parent) {