Make notification count calculation more efficient

This commit is contained in:
Nicolas Werner 2022-07-16 03:07:00 +02:00
parent f76fd4ca83
commit 7b33d14277
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
6 changed files with 103 additions and 89 deletions

View file

@ -71,30 +71,20 @@ Page {
property color unimportantText: Nheko.colors.buttonText property color unimportantText: Nheko.colors.buttonText
property color bubbleBackground: Nheko.colors.highlight property color bubbleBackground: Nheko.colors.highlight
property color bubbleText: Nheko.colors.highlightedText property color bubbleText: Nheko.colors.highlightedText
required property string avatarUrl required property var model
required property string displayName
required property string tooltip
required property bool collapsed
required property bool collapsible
required property bool hidden
required property int depth
required property string id
required property int unreadMessages
required property bool hasLoudNotification
required property bool muted
height: avatarSize + 2 * Nheko.paddingMedium height: avatarSize + 2 * Nheko.paddingMedium
width: ListView.view.width width: ListView.view.width
state: "normal" state: "normal"
ToolTip.visible: hovered && collapsed ToolTip.visible: hovered && collapsed
ToolTip.text: communityItem.tooltip ToolTip.text: model.tooltip
ToolTip.delay: Nheko.tooltipDelay ToolTip.delay: Nheko.tooltipDelay
onClicked: Communities.setCurrentTagId(communityItem.id) onClicked: Communities.setCurrentTagId(model.id)
onPressAndHold: communityContextMenu.show(communityItem.id, communityItem.hidden, communityItem.muted) onPressAndHold: communityContextMenu.show(model.id, model.hidden, model.muted)
states: [ states: [
State { State {
name: "highlight" name: "highlight"
when: (communityItem.hovered || communityItem.hidden) && !(Communities.currentTagId === communityItem.id) when: (communityItem.hovered || model.hidden) && !(Communities.currentTagId === model.id)
PropertyChanges { PropertyChanges {
target: communityItem target: communityItem
@ -108,7 +98,7 @@ Page {
}, },
State { State {
name: "selected" name: "selected"
when: Communities.currentTagId == communityItem.id when: Communities.currentTagId == model.id
PropertyChanges { PropertyChanges {
target: communityItem target: communityItem
@ -127,7 +117,7 @@ Page {
TapHandler { TapHandler {
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
onSingleTapped: communityContextMenu.show(communityItem.id, communityItem.hidden, communityItem.muted) onSingleTapped: communityContextMenu.show(model.id, model.hidden, model.muted)
gesturePolicy: TapHandler.ReleaseWithinBounds gesturePolicy: TapHandler.ReleaseWithinBounds
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
} }
@ -139,27 +129,27 @@ Page {
spacing: Nheko.paddingMedium spacing: Nheko.paddingMedium
anchors.fill: parent anchors.fill: parent
anchors.margins: Nheko.paddingMedium anchors.margins: Nheko.paddingMedium
anchors.leftMargin: Nheko.paddingMedium + (communitySidebar.collapsed ? 0 : (fontMetrics.lineSpacing * communityItem.depth)) anchors.leftMargin: Nheko.paddingMedium + (communitySidebar.collapsed ? 0 : (fontMetrics.lineSpacing * model.depth))
ImageButton { ImageButton {
visible: !communitySidebar.collapsed && communityItem.collapsible visible: !communitySidebar.collapsed && model.collapsible
Layout.preferredHeight: fontMetrics.lineSpacing Layout.preferredHeight: fontMetrics.lineSpacing
Layout.preferredWidth: fontMetrics.lineSpacing Layout.preferredWidth: fontMetrics.lineSpacing
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
height: fontMetrics.lineSpacing height: fontMetrics.lineSpacing
width: fontMetrics.lineSpacing width: fontMetrics.lineSpacing
image: communityItem.collapsed ? ":/icons/icons/ui/collapsed.svg" : ":/icons/icons/ui/expanded.svg" image: model.collapsed ? ":/icons/icons/ui/collapsed.svg" : ":/icons/icons/ui/expanded.svg"
ToolTip.visible: hovered ToolTip.visible: hovered
ToolTip.delay: Nheko.tooltipDelay ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: communityItem.collapsed ? qsTr("Expand") : qsTr("Collapse") ToolTip.text: model.collapsed ? qsTr("Expand") : qsTr("Collapse")
hoverEnabled: true hoverEnabled: true
onClicked: communityItem.collapsed = !communityItem.collapsed onClicked: model.collapsed = !model.collapsed
} }
Item { Item {
Layout.preferredWidth: fontMetrics.lineSpacing Layout.preferredWidth: fontMetrics.lineSpacing
visible: !communitySidebar.collapsed && !communityItem.collapsible && Communities.containsSubspaces visible: !communitySidebar.collapsed && !model.collapsible && Communities.containsSubspaces
} }
Avatar { Avatar {
@ -170,22 +160,22 @@ Page {
height: avatarSize height: avatarSize
width: avatarSize width: avatarSize
url: { url: {
if (communityItem.avatarUrl.startsWith("mxc://")) if (model.avatarUrl.startsWith("mxc://"))
return communityItem.avatarUrl.replace("mxc://", "image://MxcImage/"); return model.avatarUrl.replace("mxc://", "image://MxcImage/");
else else
return "image://colorimage/" + communityItem.avatarUrl + "?" + communityItem.unimportantText; return "image://colorimage/" + model.avatarUrl + "?" + communityItem.unimportantText;
} }
roomid: communityItem.id roomid: model.id
displayName: communityItem.displayName displayName: model.displayName
color: communityItem.backgroundColor color: communityItem.backgroundColor
NotificationBubble { NotificationBubble {
notificationCount: communityItem.unreadMessages notificationCount: model.unreadMessages
hasLoudNotification: communityItem.hasLoudNotification hasLoudNotification: model.hasLoudNotification
bubbleBackgroundColor: communityItem.bubbleBackground bubbleBackgroundColor: communityItem.bubbleBackground
bubbleTextColor: communityItem.bubbleText bubbleTextColor: communityItem.bubbleText
font.pixelSize: fontMetrics.font.pixelSize * 0.6 font.pixelSize: fontMetrics.font.pixelSize * 0.6
mayBeVisible: communitySidebar.collapsed && !communityItem.muted && Settings.spaceNotifications mayBeVisible: communitySidebar.collapsed && !model.muted && Settings.spaceNotifications
anchors.right: avatar.right anchors.right: avatar.right
anchors.bottom: avatar.bottom anchors.bottom: avatar.bottom
anchors.margins: -Nheko.paddingSmall anchors.margins: -Nheko.paddingSmall
@ -199,7 +189,7 @@ Page {
color: communityItem.importantText color: communityItem.importantText
Layout.fillWidth: true Layout.fillWidth: true
elideWidth: width elideWidth: width
fullText: communityItem.displayName fullText: model.displayName
textFormat: Text.PlainText textFormat: Text.PlainText
} }
@ -208,11 +198,11 @@ Page {
} }
NotificationBubble { NotificationBubble {
notificationCount: communityItem.unreadMessages notificationCount: model.unreadMessages
hasLoudNotification: communityItem.hasLoudNotification hasLoudNotification: model.hasLoudNotification
bubbleBackgroundColor: communityItem.bubbleBackground bubbleBackgroundColor: communityItem.bubbleBackground
bubbleTextColor: communityItem.bubbleText bubbleTextColor: communityItem.bubbleText
mayBeVisible: !communitySidebar.collapsed && !communityItem.muted && Settings.spaceNotifications mayBeVisible: !communitySidebar.collapsed && !model.muted && Settings.spaceNotifications
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
Layout.leftMargin: Nheko.paddingSmall Layout.leftMargin: Nheko.paddingSmall
} }

View file

@ -881,21 +881,3 @@ utils::markRoomAsDirect(QString roomid, std::vector<RoomMember> members)
}); });
}); });
} }
QPair<int, int>
utils::getChildNotificationsForSpace(const QString &spaceId)
{
auto children = cache::getRoomInfo(cache::client()->getChildRoomIds(spaceId.toStdString()));
QPair<int, int> retVal;
for (const auto &[childId, child] : children) {
if (child.is_space) {
auto temp{utils::getChildNotificationsForSpace(childId)};
retVal.first += temp.first;
retVal.second += temp.second;
} else {
retVal.first += child.notification_count;
retVal.second += child.highlight_count;
}
}
return retVal;
}

View file

@ -311,9 +311,4 @@ removeDirectFromRoom(QString roomid);
void void
markRoomAsDirect(QString roomid, std::vector<RoomMember> members); markRoomAsDirect(QString roomid, std::vector<RoomMember> members);
//! Returns a pair of integers representing the unread notifications in a space and how many of them
//! are loud notifications, respectively.
QPair<int, int>
getChildNotificationsForSpace(const QString &spaceId);
} }

View file

@ -208,9 +208,15 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
case CommunitiesModel::Roles::Id: case CommunitiesModel::Roles::Id:
return "tag:" + tag; return "tag:" + tag;
case CommunitiesModel::Roles::UnreadMessages: case CommunitiesModel::Roles::UnreadMessages:
return (int)tagNotificationCache.at(tag).notification_count; if (auto it = tagNotificationCache.find(tag); it != tagNotificationCache.end())
return (int)it->second.notification_count;
else
return 0;
case CommunitiesModel::Roles::HasLoudNotification: case CommunitiesModel::Roles::HasLoudNotification:
return (int)tagNotificationCache.at(tag).highlight_count > 0; if (auto it = tagNotificationCache.find(tag); it != tagNotificationCache.end())
return it->second.highlight_count > 0;
else
return 0;
} }
} }
return QVariant(); return QVariant();
@ -265,6 +271,21 @@ CommunitiesModel::initializeSidebar()
tags_.clear(); tags_.clear();
spaceOrder_.tree.clear(); spaceOrder_.tree.clear();
spaces_.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; std::set<std::string> ts;
@ -284,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 // NOTE(Nico): We build a forrest from the Directed Cyclic(!) Graph of spaces. To do that we
@ -319,6 +353,14 @@ CommunitiesModel::initializeSidebar()
spaceOrder_.restoreCollapsed(); 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(); endResetModel();
emit tagsChanged(); emit tagsChanged();
@ -414,15 +456,20 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_)
} }
auto roomId = QString::fromStdString(roomid); auto roomId = QString::fromStdString(roomid);
auto oldUnreads = roomNotificationCache[roomId]; auto &oldUnreads = roomNotificationCache[roomId];
int notificationCDiff = -static_cast<int64_t>(oldUnreads.highlight_count) + auto notificationCDiff = -static_cast<int64_t>(oldUnreads.notification_count) +
static_cast<int64_t>(room.unread_notifications.highlight_count); static_cast<int64_t>(room.unread_notifications.notification_count);
int highlightCDiff = -static_cast<int64_t>(oldUnreads.highlight_count) + auto highlightCDiff = -static_cast<int64_t>(oldUnreads.highlight_count) +
static_cast<int64_t>(room.unread_notifications.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) { if (highlightCDiff || notificationCDiff) {
// bool hidden = hiddenTagIds_.contains(roomId); // bool hidden = hiddenTagIds_.contains(roomId);
globalUnreads.notification_count += notificationCDiff; applyDiff(globalUnreads);
globalUnreads.highlight_count += highlightCDiff;
emit dataChanged(index(0), emit dataChanged(index(0),
index(0), index(0),
{ {
@ -431,8 +478,7 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_)
}); });
if (std::find(begin(directMessages_), end(directMessages_), roomid) != if (std::find(begin(directMessages_), end(directMessages_), roomid) !=
end(directMessages_)) { end(directMessages_)) {
dmUnreads.notification_count += notificationCDiff; applyDiff(dmUnreads);
dmUnreads.highlight_count += highlightCDiff;
emit dataChanged(index(1), emit dataChanged(index(1),
index(1), index(1),
{ {
@ -446,11 +492,8 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_)
for (const auto &t : tags) { for (const auto &t : tags) {
auto tagId = QString::fromStdString(t); auto tagId = QString::fromStdString(t);
auto &tNs = tagNotificationCache[tagId]; applyDiff(tagNotificationCache[tagId]);
tNs.notification_count += notificationCDiff;
tNs.highlight_count += highlightCDiff;
int idx = tags_.indexOf(tagId) + 2 + spaceOrder_.size(); int idx = tags_.indexOf(tagId) + 2 + spaceOrder_.size();
;
emit dataChanged(index(idx), emit dataChanged(index(idx),
index(idx), index(idx),
{ {
@ -463,8 +506,10 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_)
auto spaceId = QString::fromStdString(s); auto spaceId = QString::fromStdString(s);
for (int i = 0; i < spaceOrder_.size(); i++) { for (int i = 0; i < spaceOrder_.size(); i++) {
spaceOrder_.tree[i].notificationCounts.notification_count += notificationCDiff; if (spaceOrder_.tree[i].id != spaceId)
spaceOrder_.tree[i].notificationCounts.highlight_count += highlightCDiff; continue;
applyDiff(spaceOrder_.tree[i].notificationCounts);
int idx = i; int idx = i;
do { do {
@ -474,10 +519,13 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_)
UnreadMessages, UnreadMessages,
HasLoudNotification, HasLoudNotification,
}); });
idx = spaceOrder_.parent(idx);
} while (idx != -1); } while (idx != -1);
} }
} }
} }
roomNotificationCache[roomId] = room.unread_notifications;
} }
for (const auto &[roomid, room] : sync_.rooms.leave) { for (const auto &[roomid, room] : sync_.rooms.leave) {
(void)room; (void)room;

View file

@ -642,15 +642,18 @@ RoomlistModel::clear()
} }
void void
RoomlistModel::joinPreview(QString roomid, QString parentSpace) RoomlistModel::joinPreview(QString roomid)
{ {
if (previewedRooms.contains(roomid)) { if (previewedRooms.contains(roomid)) {
std::vector<std::string> vias;
auto parents = cache::client()->getParentRoomIds(roomid.toStdString());
for (const auto &p : parents) {
auto child = cache::client()->getStateEvent<mtx::events::state::space::Child>( auto child = cache::client()->getStateEvent<mtx::events::state::space::Child>(
parentSpace.toStdString(), roomid.toStdString()); p, roomid.toStdString());
ChatPage::instance()->joinRoomVia( if (child && child->content.via)
roomid.toStdString(), vias.insert(vias.end(), child->content.via->begin(), child->content.via->end());
(child && child->content.via) ? child->content.via.value() : std::vector<std::string>{}, }
false); ChatPage::instance()->joinRoomVia(roomid.toStdString(), vias, false);
} }
} }
void void

View file

@ -105,7 +105,7 @@ public slots:
return -1; return -1;
} }
void joinPreview(QString roomid, QString parentSpace); void joinPreview(QString roomid);
void acceptInvite(QString roomid); void acceptInvite(QString roomid);
void declineInvite(QString roomid); void declineInvite(QString roomid);
void leave(QString roomid, QString reason = ""); void leave(QString roomid, QString reason = "");
@ -169,11 +169,7 @@ public slots:
{ {
return mapFromSource(roomlistmodel->index(roomlistmodel->roomidToIndex(roomid))).row(); return mapFromSource(roomlistmodel->index(roomlistmodel->roomidToIndex(roomid))).row();
} }
void joinPreview(QString roomid) void joinPreview(QString roomid) { roomlistmodel->joinPreview(roomid); }
{
roomlistmodel->joinPreview(roomid,
filterType == FilterBy::Space ? filterStr : QLatin1String(""));
}
void acceptInvite(QString roomid) { roomlistmodel->acceptInvite(roomid); } void acceptInvite(QString roomid) { roomlistmodel->acceptInvite(roomid); }
void declineInvite(QString roomid) { roomlistmodel->declineInvite(roomid); } void declineInvite(QString roomid) { roomlistmodel->declineInvite(roomid); }
void leave(QString roomid, QString reason = "") { roomlistmodel->leave(roomid, reason); } void leave(QString roomid, QString reason = "") { roomlistmodel->leave(roomid, reason); }