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)
{
|