Merge remote-tracking branch 'origin/perSpaceNotifs' into perSpaceNotifs

This commit is contained in:
Nicolas Werner 2022-07-14 01:26:48 +02:00
commit aa63e50cea
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
12 changed files with 374 additions and 124 deletions

View file

@ -3,6 +3,7 @@
// //
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
import "./components"
import "./dialogs" import "./dialogs"
import Qt.labs.platform 1.1 as Platform import Qt.labs.platform 1.1 as Platform
import QtQml 2.12 import QtQml 2.12
@ -36,14 +37,27 @@ Page {
id: communityContextMenu id: communityContextMenu
property string tagId property string tagId
property bool hidden
property bool muted
function show(id_, tags_) { function show(id_, hidden_, muted_) {
tagId = id_; tagId = id_;
hidden = hidden_;
muted = muted_;
open(); open();
} }
Platform.MenuItem {
text: qsTr("Do not show notification counts for this space or tag.")
checkable: true
checked: communityContextMenu.muted
onTriggered: Communities.toggleTagMute(communityContextMenu.tagId)
}
Platform.MenuItem { Platform.MenuItem {
text: qsTr("Hide rooms with this tag or from this space by default.") text: qsTr("Hide rooms with this tag or from this space by default.")
checkable: true
checked: communityContextMenu.hidden
onTriggered: Communities.toggleTagId(communityContextMenu.tagId) onTriggered: Communities.toggleTagId(communityContextMenu.tagId)
} }
@ -57,19 +71,30 @@ 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 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: model.tooltip ToolTip.text: communityItem.tooltip
ToolTip.delay: Nheko.tooltipDelay ToolTip.delay: Nheko.tooltipDelay
onClicked: Communities.setCurrentTagId(model.id) onClicked: Communities.setCurrentTagId(communityItem.id)
onPressAndHold: communityContextMenu.show(model.id) onPressAndHold: communityContextMenu.show(communityItem.id, communityItem.hidden, communityItem.muted)
states: [ states: [
State { State {
name: "highlight" name: "highlight"
when: (communityItem.hovered || model.hidden) && !(Communities.currentTagId == model.id) when: (communityItem.hovered || communityItem.hidden) && !(Communities.currentTagId === communityItem.id)
PropertyChanges { PropertyChanges {
target: communityItem target: communityItem
@ -83,7 +108,7 @@ Page {
}, },
State { State {
name: "selected" name: "selected"
when: Communities.currentTagId == model.id when: Communities.currentTagId == communityItem.id
PropertyChanges { PropertyChanges {
target: communityItem target: communityItem
@ -102,7 +127,7 @@ Page {
TapHandler { TapHandler {
acceptedButtons: Qt.RightButton acceptedButtons: Qt.RightButton
onSingleTapped: communityContextMenu.show(model.id) onSingleTapped: communityContextMenu.show(communityItem.id, communityItem.hidden, communityItem.muted)
gesturePolicy: TapHandler.ReleaseWithinBounds gesturePolicy: TapHandler.ReleaseWithinBounds
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
} }
@ -114,27 +139,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 * model.depth)) anchors.leftMargin: Nheko.paddingMedium + (communitySidebar.collapsed ? 0 : (fontMetrics.lineSpacing * communityItem.depth))
ImageButton { ImageButton {
visible: !communitySidebar.collapsed && model.collapsible visible: !communitySidebar.collapsed && communityItem.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: model.collapsed ? ":/icons/icons/ui/collapsed.svg" : ":/icons/icons/ui/expanded.svg" image: communityItem.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: model.collapsed ? qsTr("Expand") : qsTr("Collapse") ToolTip.text: communityItem.collapsed ? qsTr("Expand") : qsTr("Collapse")
hoverEnabled: true hoverEnabled: true
onClicked: model.collapsed = !model.collapsed onClicked: communityItem.collapsed = !communityItem.collapsed
} }
Item { Item {
Layout.preferredWidth: fontMetrics.lineSpacing Layout.preferredWidth: fontMetrics.lineSpacing
visible: !communitySidebar.collapsed && !model.collapsible && Communities.containsSubspaces visible: !communitySidebar.collapsed && !communityItem.collapsible && Communities.containsSubspaces
} }
Avatar { Avatar {
@ -145,14 +170,27 @@ Page {
height: avatarSize height: avatarSize
width: avatarSize width: avatarSize
url: { url: {
if (model.avatarUrl.startsWith("mxc://")) if (communityItem.avatarUrl.startsWith("mxc://"))
return model.avatarUrl.replace("mxc://", "image://MxcImage/"); return communityItem.avatarUrl.replace("mxc://", "image://MxcImage/");
else else
return "image://colorimage/" + model.avatarUrl + "?" + communityItem.unimportantText; return "image://colorimage/" + communityItem.avatarUrl + "?" + communityItem.unimportantText;
} }
roomid: model.id roomid: communityItem.id
displayName: model.displayName displayName: communityItem.displayName
color: communityItem.backgroundColor color: communityItem.backgroundColor
NotificationBubble {
notificationCount: communityItem.unreadMessages
hasLoudNotification: communityItem.hasLoudNotification
bubbleBackgroundColor: communityItem.bubbleBackground
bubbleTextColor: communityItem.bubbleText
font.pixelSize: fontMetrics.font.pixelSize * 0.6
mayBeVisible: communitySidebar.collapsed && !communityItem.muted && Settings.spaceNotifications
anchors.right: avatar.right
anchors.bottom: avatar.bottom
anchors.margins: -Nheko.paddingSmall
}
} }
ElidedLabel { ElidedLabel {
@ -161,7 +199,7 @@ Page {
color: communityItem.importantText color: communityItem.importantText
Layout.fillWidth: true Layout.fillWidth: true
elideWidth: width elideWidth: width
fullText: model.displayName fullText: communityItem.displayName
textFormat: Text.PlainText textFormat: Text.PlainText
} }
@ -169,10 +207,20 @@ Page {
Layout.fillWidth: true Layout.fillWidth: true
} }
NotificationBubble {
notificationCount: communityItem.unreadMessages
hasLoudNotification: communityItem.hasLoudNotification
bubbleBackgroundColor: communityItem.bubbleBackground
bubbleTextColor: communityItem.bubbleText
mayBeVisible: !communitySidebar.collapsed && !communityItem.muted && Settings.spaceNotifications
Layout.alignment: Qt.AlignRight
Layout.leftMargin: Nheko.paddingSmall
}
} }
background: Rectangle { background: Rectangle {
color: backgroundColor color: communityItem.backgroundColor
} }
} }

View file

@ -3,6 +3,7 @@
// //
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
import "./components"
import "./dialogs" import "./dialogs"
import "./ui" import "./ui"
import Qt.labs.platform 1.1 as Platform import Qt.labs.platform 1.1 as Platform
@ -294,9 +295,6 @@ Page {
anchors.margins: Nheko.paddingMedium anchors.margins: Nheko.paddingMedium
Avatar { Avatar {
// In the future we could show an online indicator by setting the userid for the avatar
//userid: Nheko.currentUser.userid
id: avatar id: avatar
enabled: false enabled: false
@ -308,33 +306,17 @@ Page {
userid: isDirect ? directChatOtherUserId : "" userid: isDirect ? directChatOtherUserId : ""
roomid: roomId roomid: roomId
Rectangle { NotificationBubble {
id: collapsedNotificationBubble id: collapsedNotificationBubble
notificationCount: roomItem.notificationCount
hasLoudNotification: roomItem.hasLoudNotification
bubbleBackgroundColor: roomItem.bubbleBackground
bubbleTextColor: roomItem.bubbleText
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.margins: -Nheko.paddingSmall anchors.margins: -Nheko.paddingSmall
visible: collapsed && notificationCount > 0 mayBeVisible: collapsed && (isSpace ? Settings.spaceNotifications : true)
enabled: false
Layout.alignment: Qt.AlignRight
height: fontMetrics.averageCharacterWidth * 3
width: Math.max(collapsedBubbleText.width, height)
radius: height / 2
color: hasLoudNotification ? Nheko.theme.red : roomItem.bubbleBackground
Label {
id: collapsedBubbleText
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
width: Math.max(implicitWidth + Nheko.paddingMedium, parent.height)
font.bold: true
font.pixelSize: fontMetrics.font.pixelSize * 0.8
color: hasLoudNotification ? "white" : roomItem.bubbleText
text: notificationCount > 9999 ? "9999+" : notificationCount
}
} }
} }
@ -351,7 +333,24 @@ Page {
height: avatar.height height: avatar.height
spacing: Nheko.paddingSmall spacing: Nheko.paddingSmall
NotificationBubble {
id: notificationBubble
parent: isSpace ? titleRow : subtextRow
notificationCount: roomItem.notificationCount
hasLoudNotification: roomItem.hasLoudNotification
bubbleBackgroundColor: roomItem.bubbleBackground
bubbleTextColor: roomItem.bubbleText
Layout.alignment: Qt.AlignRight
Layout.leftMargin: Nheko.paddingSmall
Layout.preferredWidth: implicitWidth
Layout.preferredHeight: implicitHeight
mayBeVisible: !collapsed && (isSpace ? Settings.spaceNotifications : true)
}
RowLayout { RowLayout {
id: titleRow
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignTop
Layout.fillWidth: true Layout.fillWidth: true
spacing: Nheko.paddingSmall spacing: Nheko.paddingSmall
@ -380,6 +379,8 @@ Page {
} }
RowLayout { RowLayout {
id: subtextRow
Layout.fillWidth: true Layout.fillWidth: true
spacing: 0 spacing: 0
visible: !isSpace visible: !isSpace
@ -395,40 +396,6 @@ Page {
Layout.fillWidth: true Layout.fillWidth: true
} }
Rectangle {
id: notificationBubble
visible: notificationCount > 0
Layout.alignment: Qt.AlignRight
Layout.leftMargin: Nheko.paddingSmall
height: notificationBubbleText.height + Nheko.paddingMedium
Layout.preferredWidth: Math.max(notificationBubbleText.width, height)
radius: height / 2
color: hasLoudNotification ? Nheko.theme.red : roomItem.bubbleBackground
ToolTip.text: notificationCount
ToolTip.delay: Nheko.tooltipDelay
ToolTip.visible: notificationBubbleHover.hovered && (notificationCount > 9999)
Label {
id: notificationBubbleText
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
width: Math.max(implicitWidth + Nheko.paddingMedium, parent.height)
font.bold: true
font.pixelSize: fontMetrics.font.pixelSize * 0.8
color: hasLoudNotification ? "white" : roomItem.bubbleText
text: notificationCount > 9999 ? "9999+" : notificationCount
HoverHandler {
id: notificationBubbleHover
}
}
}
} }
} }

View file

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2022 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.15
import QtQuick.Controls 2.15
import im.nheko 1.0
Rectangle {
id: bubbleRoot
required property int notificationCount
required property bool hasLoudNotification
required property color bubbleBackgroundColor
required property color bubbleTextColor
property bool mayBeVisible: true
property alias font: notificationBubbleText.font
visible: mayBeVisible && notificationCount > 0
implicitHeight: notificationBubbleText.height + Nheko.paddingMedium
implicitWidth: Math.max(notificationBubbleText.width, height)
radius: height / 2
color: hasLoudNotification ? Nheko.theme.red : bubbleBackgroundColor
ToolTip.text: notificationCount
ToolTip.delay: Nheko.tooltipDelay
ToolTip.visible: notificationBubbleHover.hovered && (notificationCount > 9999)
Label {
id: notificationBubbleText
anchors.centerIn: bubbleRoot
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
width: Math.max(implicitWidth + Nheko.paddingMedium, bubbleRoot.height)
font.bold: true
font.pixelSize: fontMetrics.font.pixelSize * 0.8
color: bubbleRoot.hasLoudNotification ? "white" : bubbleRoot.bubbleTextColor
text: bubbleRoot.notificationCount > 9999 ? "9999+" : bubbleRoot.notificationCount
HoverHandler {
id: notificationBubbleHover
}
}
}

View file

@ -185,6 +185,7 @@
<file>qml/voip/PlaceCall.qml</file> <file>qml/voip/PlaceCall.qml</file>
<file>qml/voip/ScreenShare.qml</file> <file>qml/voip/ScreenShare.qml</file>
<file>qml/voip/VideoCall.qml</file> <file>qml/voip/VideoCall.qml</file>
<file>qml/components/NotificationBubble.qml</file>
</qresource> </qresource>
<qresource prefix="/media"> <qresource prefix="/media">
<file>media/ring.ogg</file> <file>media/ring.ogg</file>

View file

@ -88,6 +88,7 @@ UserSettings::load(std::optional<QString> profile)
openImageExternal_ = settings.value(QStringLiteral("user/open_image_external"), false).toBool(); openImageExternal_ = settings.value(QStringLiteral("user/open_image_external"), false).toBool();
openVideoExternal_ = settings.value(QStringLiteral("user/open_video_external"), false).toBool(); openVideoExternal_ = settings.value(QStringLiteral("user/open_video_external"), false).toBool();
decryptSidebar_ = settings.value(QStringLiteral("user/decrypt_sidebar"), true).toBool(); decryptSidebar_ = settings.value(QStringLiteral("user/decrypt_sidebar"), true).toBool();
spaceNotifications_ = settings.value(QStringLiteral("user/space_notifications"), true).toBool();
privacyScreen_ = settings.value(QStringLiteral("user/privacy_screen"), false).toBool(); privacyScreen_ = settings.value(QStringLiteral("user/privacy_screen"), false).toBool();
privacyScreenTimeout_ = privacyScreenTimeout_ =
settings.value(QStringLiteral("user/privacy_screen_timeout"), 0).toInt(); settings.value(QStringLiteral("user/privacy_screen_timeout"), 0).toInt();
@ -132,6 +133,7 @@ UserSettings::load(std::optional<QString> profile)
userId_ = settings.value(prefix + "auth/user_id", "").toString(); userId_ = settings.value(prefix + "auth/user_id", "").toString();
deviceId_ = settings.value(prefix + "auth/device_id", "").toString(); deviceId_ = settings.value(prefix + "auth/device_id", "").toString();
hiddenTags_ = settings.value(prefix + "user/hidden_tags", QStringList{}).toStringList(); hiddenTags_ = settings.value(prefix + "user/hidden_tags", QStringList{}).toStringList();
mutedTags_ = settings.value(prefix + "user/muted_tags", QStringList{"global"}).toStringList();
hiddenPins_ = settings.value(prefix + "user/hidden_pins", QStringList{}).toStringList(); hiddenPins_ = settings.value(prefix + "user/hidden_pins", QStringList{}).toStringList();
hiddenWidgets_ = settings.value(prefix + "user/hidden_widgets", QStringList{}).toStringList(); hiddenWidgets_ = settings.value(prefix + "user/hidden_widgets", QStringList{}).toStringList();
recentReactions_ = recentReactions_ =
@ -220,14 +222,21 @@ UserSettings::setGroupView(bool state)
} }
void void
UserSettings::setHiddenTags(QStringList hiddenTags) UserSettings::setHiddenTags(const QStringList &hiddenTags)
{ {
hiddenTags_ = hiddenTags; hiddenTags_ = hiddenTags;
save(); save();
} }
void void
UserSettings::setHiddenPins(QStringList hiddenTags) UserSettings::setMutedTags(const QStringList &mutedTags)
{
mutedTags_ = mutedTags;
save();
}
void
UserSettings::setHiddenPins(const QStringList &hiddenTags)
{ {
hiddenPins_ = hiddenTags; hiddenPins_ = hiddenTags;
save(); save();
@ -235,7 +244,7 @@ UserSettings::setHiddenPins(QStringList hiddenTags)
} }
void void
UserSettings::setHiddenWidgets(QStringList hiddenTags) UserSettings::setHiddenWidgets(const QStringList &hiddenTags)
{ {
hiddenWidgets_ = hiddenTags; hiddenWidgets_ = hiddenTags;
save(); save();
@ -416,6 +425,16 @@ UserSettings::setDecryptSidebar(bool state)
save(); save();
} }
void
UserSettings::setSpaceNotifications(bool state)
{
if (state == spaceNotifications_)
return;
spaceNotifications_ = state;
emit spaceNotificationsChanged(state);
save();
}
void void
UserSettings::setPrivacyScreen(bool state) UserSettings::setPrivacyScreen(bool state)
{ {
@ -777,6 +796,7 @@ UserSettings::save()
settings.setValue(QStringLiteral("avatar_circles"), avatarCircles_); settings.setValue(QStringLiteral("avatar_circles"), avatarCircles_);
settings.setValue(QStringLiteral("decrypt_sidebar"), decryptSidebar_); settings.setValue(QStringLiteral("decrypt_sidebar"), decryptSidebar_);
settings.setValue(QStringLiteral("space_notifications"), spaceNotifications_);
settings.setValue(QStringLiteral("privacy_screen"), privacyScreen_); settings.setValue(QStringLiteral("privacy_screen"), privacyScreen_);
settings.setValue(QStringLiteral("privacy_screen_timeout"), privacyScreenTimeout_); settings.setValue(QStringLiteral("privacy_screen_timeout"), privacyScreenTimeout_);
settings.setValue(QStringLiteral("mobile_mode"), mobileMode_); settings.setValue(QStringLiteral("mobile_mode"), mobileMode_);
@ -830,6 +850,7 @@ UserSettings::save()
onlyShareKeysWithVerifiedUsers_); onlyShareKeysWithVerifiedUsers_);
settings.setValue(prefix + "user/online_key_backup", useOnlineKeyBackup_); settings.setValue(prefix + "user/online_key_backup", useOnlineKeyBackup_);
settings.setValue(prefix + "user/hidden_tags", hiddenTags_); settings.setValue(prefix + "user/hidden_tags", hiddenTags_);
settings.setValue(prefix + "user/muted_tags", mutedTags_);
settings.setValue(prefix + "user/hidden_pins", hiddenPins_); settings.setValue(prefix + "user/hidden_pins", hiddenPins_);
settings.setValue(prefix + "user/hidden_widgets", hiddenWidgets_); settings.setValue(prefix + "user/hidden_widgets", hiddenWidgets_);
settings.setValue(prefix + "user/recent_reactions", recentReactions_); settings.setValue(prefix + "user/recent_reactions", recentReactions_);
@ -923,6 +944,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
return tr("Open videos with external program"); return tr("Open videos with external program");
case DecryptSidebar: case DecryptSidebar:
return tr("Decrypt messages in sidebar"); return tr("Decrypt messages in sidebar");
case SpaceNotifications:
return tr("Show message counts for spaces");
case PrivacyScreen: case PrivacyScreen:
return tr("Privacy Screen"); return tr("Privacy Screen");
case PrivacyScreenTimeout: case PrivacyScreenTimeout:
@ -1053,6 +1076,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
return i->openVideoExternal(); return i->openVideoExternal();
case DecryptSidebar: case DecryptSidebar:
return i->decryptSidebar(); return i->decryptSidebar();
case SpaceNotifications:
return i->spaceNotifications();
case PrivacyScreen: case PrivacyScreen:
return i->privacyScreen(); return i->privacyScreen();
case PrivacyScreenTimeout: case PrivacyScreenTimeout:
@ -1208,6 +1233,9 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
case DecryptSidebar: case DecryptSidebar:
return tr("Decrypt the messages shown in the sidebar.\nOnly affects messages in " return tr("Decrypt the messages shown in the sidebar.\nOnly affects messages in "
"encrypted chats."); "encrypted chats.");
case SpaceNotifications:
return tr(
"Choose where to show the total number of notifications contained within a space.");
case PrivacyScreen: case PrivacyScreen:
return tr("When the window loses focus, the timeline will\nbe blurred."); return tr("When the window loses focus, the timeline will\nbe blurred.");
case MobileMode: case MobileMode:
@ -1317,6 +1345,7 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
case ShareKeysWithTrustedUsers: case ShareKeysWithTrustedUsers:
case UseOnlineKeyBackup: case UseOnlineKeyBackup:
case ExposeDBusApi: case ExposeDBusApi:
case SpaceNotifications:
return Toggle; return Toggle;
case Profile: case Profile:
case UserId: case UserId:
@ -1409,7 +1438,7 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
return fontDb.families(); return fontDb.families();
case EmojiFont: case EmojiFont:
return fontDb.families(QFontDatabase::WritingSystem::Symbol); return fontDb.families(QFontDatabase::WritingSystem::Symbol);
case Ringtone: case Ringtone: {
QStringList l{ QStringList l{
QStringLiteral("Mute"), QStringLiteral("Mute"),
QStringLiteral("Default"), QStringLiteral("Default"),
@ -1419,6 +1448,7 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
l.push_back(i->ringtone()); l.push_back(i->ringtone());
return l; return l;
} }
}
} else if (role == Good) { } else if (role == Good) {
switch (index.row()) { switch (index.row()) {
case OnlineBackupKey: case OnlineBackupKey:
@ -1624,6 +1654,13 @@ UserSettingsModel::setData(const QModelIndex &index, const QVariant &value, int
return false; return false;
} }
return i->decryptSidebar(); return i->decryptSidebar();
case SpaceNotifications: {
if (value.userType() == QMetaType::Bool) {
i->setSpaceNotifications(value.toBool());
return true;
} else
return false;
}
case PrivacyScreen: { case PrivacyScreen: {
if (value.userType() == QMetaType::Bool) { if (value.userType() == QMetaType::Bool) {
i->setPrivacyScreen(value.toBool()); i->setPrivacyScreen(value.toBool());
@ -1936,7 +1973,9 @@ UserSettingsModel::UserSettingsModel(QObject *p)
connect(s.get(), &UserSettings::decryptSidebarChanged, this, [this]() { connect(s.get(), &UserSettings::decryptSidebarChanged, this, [this]() {
emit dataChanged(index(DecryptSidebar), index(DecryptSidebar), {Value}); emit dataChanged(index(DecryptSidebar), index(DecryptSidebar), {Value});
}); });
connect(s.get(), &UserSettings::spaceNotificationsChanged, this, [this] {
emit dataChanged(index(SpaceNotifications), index(SpaceNotifications), {Value});
});
connect(s.get(), &UserSettings::trayChanged, this, [this]() { connect(s.get(), &UserSettings::trayChanged, this, [this]() {
emit dataChanged(index(Tray), index(Tray), {Value}); emit dataChanged(index(Tray), index(Tray), {Value});
emit dataChanged(index(StartInTray), index(StartInTray), {Enabled}); emit dataChanged(index(StartInTray), index(StartInTray), {Enabled});

View file

@ -58,6 +58,8 @@ class UserSettings : public QObject
bool avatarCircles READ avatarCircles WRITE setAvatarCircles NOTIFY avatarCirclesChanged) bool avatarCircles READ avatarCircles WRITE setAvatarCircles NOTIFY avatarCirclesChanged)
Q_PROPERTY( Q_PROPERTY(
bool decryptSidebar READ decryptSidebar WRITE setDecryptSidebar NOTIFY decryptSidebarChanged) bool decryptSidebar READ decryptSidebar WRITE setDecryptSidebar NOTIFY decryptSidebarChanged)
Q_PROPERTY(bool spaceNotifications READ spaceNotifications WRITE setSpaceNotifications NOTIFY
spaceNotificationsChanged)
Q_PROPERTY( Q_PROPERTY(
bool privacyScreen READ privacyScreen WRITE setPrivacyScreen NOTIFY privacyScreenChanged) bool privacyScreen READ privacyScreen WRITE setPrivacyScreen NOTIFY privacyScreenChanged)
Q_PROPERTY(int privacyScreenTimeout READ privacyScreenTimeout WRITE setPrivacyScreenTimeout Q_PROPERTY(int privacyScreenTimeout READ privacyScreenTimeout WRITE setPrivacyScreenTimeout
@ -162,6 +164,7 @@ public:
void setAlertOnNotification(bool state); void setAlertOnNotification(bool state);
void setAvatarCircles(bool state); void setAvatarCircles(bool state);
void setDecryptSidebar(bool state); void setDecryptSidebar(bool state);
void setSpaceNotifications(bool state);
void setPrivacyScreen(bool state); void setPrivacyScreen(bool state);
void setPrivacyScreenTimeout(int state); void setPrivacyScreenTimeout(int state);
void setPresence(Presence state); void setPresence(Presence state);
@ -184,9 +187,10 @@ public:
void setDeviceId(QString deviceId); void setDeviceId(QString deviceId);
void setHomeserver(QString homeserver); void setHomeserver(QString homeserver);
void setDisableCertificateValidation(bool disabled); void setDisableCertificateValidation(bool disabled);
void setHiddenTags(QStringList hiddenTags); void setHiddenTags(const QStringList &hiddenTags);
void setHiddenPins(QStringList hiddenTags); void setMutedTags(const QStringList &mutedTags);
void setHiddenWidgets(QStringList hiddenTags); void setHiddenPins(const QStringList &hiddenTags);
void setHiddenWidgets(const QStringList &hiddenTags);
void setRecentReactions(QStringList recent); void setRecentReactions(QStringList recent);
void setUseIdenticon(bool state); void setUseIdenticon(bool state);
void setOpenImageExternal(bool state); void setOpenImageExternal(bool state);
@ -202,6 +206,7 @@ public:
bool groupView() const { return groupView_; } bool groupView() const { return groupView_; }
bool avatarCircles() const { return avatarCircles_; } bool avatarCircles() const { return avatarCircles_; }
bool decryptSidebar() const { return decryptSidebar_; } bool decryptSidebar() const { return decryptSidebar_; }
bool spaceNotifications() const { return spaceNotifications_; }
bool privacyScreen() const { return privacyScreen_; } bool privacyScreen() const { return privacyScreen_; }
int privacyScreenTimeout() const { return privacyScreenTimeout_; } int privacyScreenTimeout() const { return privacyScreenTimeout_; }
bool markdown() const { return markdown_; } bool markdown() const { return markdown_; }
@ -250,6 +255,7 @@ public:
QString homeserver() const { return homeserver_; } QString homeserver() const { return homeserver_; }
bool disableCertificateValidation() const { return disableCertificateValidation_; } bool disableCertificateValidation() const { return disableCertificateValidation_; }
QStringList hiddenTags() const { return hiddenTags_; } QStringList hiddenTags() const { return hiddenTags_; }
QStringList mutedTags() const { return mutedTags_; }
QStringList hiddenPins() const { return hiddenPins_; } QStringList hiddenPins() const { return hiddenPins_; }
QStringList hiddenWidgets() const { return hiddenWidgets_; } QStringList hiddenWidgets() const { return hiddenWidgets_; }
QStringList recentReactions() const { return recentReactions_; } QStringList recentReactions() const { return recentReactions_; }
@ -278,6 +284,7 @@ signals:
void alertOnNotificationChanged(bool state); void alertOnNotificationChanged(bool state);
void avatarCirclesChanged(bool state); void avatarCirclesChanged(bool state);
void decryptSidebarChanged(bool state); void decryptSidebarChanged(bool state);
void spaceNotificationsChanged(bool state);
void privacyScreenChanged(bool state); void privacyScreenChanged(bool state);
void privacyScreenTimeoutChanged(int state); void privacyScreenTimeoutChanged(int state);
void timelineMaxWidthChanged(int state); void timelineMaxWidthChanged(int state);
@ -340,6 +347,7 @@ private:
bool hasAlertOnNotification_; bool hasAlertOnNotification_;
bool avatarCircles_; bool avatarCircles_;
bool decryptSidebar_; bool decryptSidebar_;
bool spaceNotifications_;
bool privacyScreen_; bool privacyScreen_;
int privacyScreenTimeout_; int privacyScreenTimeout_;
bool shareKeysWithTrustedUsers_; bool shareKeysWithTrustedUsers_;
@ -370,6 +378,7 @@ private:
QString deviceId_; QString deviceId_;
QString homeserver_; QString homeserver_;
QStringList hiddenTags_; QStringList hiddenTags_;
QStringList mutedTags_;
QStringList hiddenPins_; QStringList hiddenPins_;
QStringList hiddenWidgets_; QStringList hiddenWidgets_;
QStringList recentReactions_; QStringList recentReactions_;
@ -424,6 +433,7 @@ class UserSettingsModel : public QAbstractListModel
GroupView, GroupView,
SortByImportance, SortByImportance,
DecryptSidebar, DecryptSidebar,
SpaceNotifications,
TraySection, TraySection,
Tray, Tray,

View file

@ -27,6 +27,7 @@
#include <cmark.h> #include <cmark.h>
#include "Cache.h" #include "Cache.h"
#include "Cache_p.h"
#include "Config.h" #include "Config.h"
#include "EventAccessors.h" #include "EventAccessors.h"
#include "Logging.h" #include "Logging.h"
@ -880,3 +881,21 @@ 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,4 +311,9 @@ 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

@ -9,12 +9,24 @@
#include "Cache.h" #include "Cache.h"
#include "Cache_p.h" #include "Cache_p.h"
#include "ChatPage.h"
#include "Logging.h" #include "Logging.h"
#include "UserSettingsPage.h" #include "UserSettingsPage.h"
#include "Utils.h"
CommunitiesModel::CommunitiesModel(QObject *parent) CommunitiesModel::CommunitiesModel(QObject *parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
{} , hiddenTagIds_{UserSettings::instance()->hiddenTags()}
, mutedTagIds_{UserSettings::instance()->mutedTags()}
{
connect(ChatPage::instance(), &ChatPage::unreadMessages, this, [this](int) {
// Simply updating every space is easier than tracking which ones need updated.
if (!spaces_.empty())
emit dataChanged(index(0, 0),
index(spaces_.size() + tags_.size() + 1, 0),
{Roles::UnreadMessages, Roles::HasLoudNotification});
});
}
QHash<int, QByteArray> QHash<int, QByteArray>
CommunitiesModel::roleNames() const CommunitiesModel::roleNames() const
@ -28,6 +40,9 @@ CommunitiesModel::roleNames() const
{Hidden, "hidden"}, {Hidden, "hidden"},
{Depth, "depth"}, {Depth, "depth"},
{Id, "id"}, {Id, "id"},
{UnreadMessages, "unreadMessages"},
{HasLoudNotification, "hasLoudNotification"},
{Muted, "muted"},
}; };
} }
@ -50,6 +65,13 @@ CommunitiesModel::setData(const QModelIndex &index, const QVariant &value, int r
QVariant QVariant
CommunitiesModel::data(const QModelIndex &index, int role) const 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) { if (index.row() == 0) {
switch (role) { switch (role) {
case CommunitiesModel::Roles::AvatarUrl: case CommunitiesModel::Roles::AvatarUrl:
@ -70,6 +92,17 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
return 0; return 0;
case CommunitiesModel::Roles::Id: case CommunitiesModel::Roles::Id:
return ""; return "";
case CommunitiesModel::Roles::UnreadMessages: {
int total{0};
for (const auto &[id, info] : cache::getRoomInfo(cache::joinedRooms()))
total += info.notification_count;
return total;
}
case CommunitiesModel::Roles::HasLoudNotification:
for (const auto &[id, info] : cache::getRoomInfo(cache::joinedRooms()))
if (info.highlight_count > 0)
return true;
return false;
} }
} else if (index.row() == 1) { } else if (index.row() == 1) {
switch (role) { switch (role) {
@ -84,16 +117,27 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
case CommunitiesModel::Roles::Collapsible: case CommunitiesModel::Roles::Collapsible:
return false; return false;
case CommunitiesModel::Roles::Hidden: case CommunitiesModel::Roles::Hidden:
return hiddentTagIds_.contains(QStringLiteral("dm")); return hiddenTagIds_.contains(QStringLiteral("dm"));
case CommunitiesModel::Roles::Parent: case CommunitiesModel::Roles::Parent:
return ""; return "";
case CommunitiesModel::Roles::Depth: case CommunitiesModel::Roles::Depth:
return 0; return 0;
case CommunitiesModel::Roles::Id: case CommunitiesModel::Roles::Id:
return "dm"; return "dm";
case CommunitiesModel::Roles::UnreadMessages: {
int total{0};
for (const auto &[id, info] : cache::getRoomInfo(directMessages_))
total += info.notification_count;
return total;
}
case CommunitiesModel::Roles::HasLoudNotification:
for (const auto &[id, info] : cache::getRoomInfo(directMessages_))
if (info.highlight_count > 0)
return true;
return false;
} }
} else if (index.row() - 2 < spaceOrder_.size()) { } 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) { switch (role) {
case CommunitiesModel::Roles::AvatarUrl: case CommunitiesModel::Roles::AvatarUrl:
return QString::fromStdString(spaces_.at(id).avatar_url); return QString::fromStdString(spaces_.at(id).avatar_url);
@ -107,10 +151,10 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
return idx != spaceOrder_.lastChild(idx); return idx != spaceOrder_.lastChild(idx);
} }
case CommunitiesModel::Roles::Hidden: case CommunitiesModel::Roles::Hidden:
return hiddentTagIds_.contains("space:" + id); return hiddenTagIds_.contains("space:" + id);
case CommunitiesModel::Roles::Parent: { case CommunitiesModel::Roles::Parent: {
if (auto p = spaceOrder_.parent(index.row() - 2); p >= 0) if (auto p = spaceOrder_.parent(index.row() - 2); p >= 0)
return spaceOrder_.tree[p].name; return spaceOrder_.tree[p].id;
return ""; return "";
} }
@ -118,6 +162,10 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
return spaceOrder_.tree.at(index.row() - 2).depth; return spaceOrder_.tree.at(index.row() - 2).depth;
case CommunitiesModel::Roles::Id: case CommunitiesModel::Roles::Id:
return "space:" + id; return "space:" + id;
case CommunitiesModel::Roles::UnreadMessages:
return utils::getChildNotificationsForSpace(id).first;
case CommunitiesModel::Roles::HasLoudNotification:
return utils::getChildNotificationsForSpace(id).second > 0;
} }
} else if (index.row() - 2 < tags_.size() + spaceOrder_.size()) { } else if (index.row() - 2 < tags_.size() + spaceOrder_.size()) {
auto tag = tags_.at(index.row() - 2 - spaceOrder_.size()); auto tag = tags_.at(index.row() - 2 - spaceOrder_.size());
@ -160,7 +208,7 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
switch (role) { switch (role) {
case CommunitiesModel::Roles::Hidden: case CommunitiesModel::Roles::Hidden:
return hiddentTagIds_.contains("tag:" + tag); return hiddenTagIds_.contains("tag:" + tag);
case CommunitiesModel::Roles::Collapsed: case CommunitiesModel::Roles::Collapsed:
return true; return true;
case CommunitiesModel::Roles::Collapsible: case CommunitiesModel::Roles::Collapsible:
@ -171,6 +219,24 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
return 0; return 0;
case CommunitiesModel::Roles::Id: case CommunitiesModel::Roles::Id:
return "tag:" + tag; return "tag:" + tag;
case CommunitiesModel::Roles::UnreadMessages: {
int total{0};
auto rooms{cache::joinedRooms()};
for (const auto &[roomid, info] : cache::getRoomInfo(rooms))
if (std::find(std::begin(info.tags), std::end(info.tags), tag.toStdString()) !=
std::end(info.tags))
total += info.notification_count;
return total;
}
case CommunitiesModel::Roles::HasLoudNotification: {
auto rooms{cache::joinedRooms()};
for (const auto &[roomid, info] : cache::getRoomInfo(rooms))
if (std::find(std::begin(info.tags), std::end(info.tags), tag.toStdString()) !=
std::end(info.tags))
if (info.highlight_count > 0)
return true;
return false;
}
} }
} }
return QVariant(); return QVariant();
@ -277,8 +343,8 @@ CommunitiesModel::initializeSidebar()
for (const auto &t : ts) for (const auto &t : ts)
tags_.push_back(QString::fromStdString(t)); tags_.push_back(QString::fromStdString(t));
hiddentTagIds_ = UserSettings::instance()->hiddenTags();
spaceOrder_.restoreCollapsed(); spaceOrder_.restoreCollapsed();
endResetModel(); endResetModel();
emit tagsChanged(); emit tagsChanged();
@ -298,12 +364,12 @@ CommunitiesModel::FlatTree::storeCollapsed()
for (const auto &e : tree) { for (const auto &e : tree) {
if (e.depth > depth) { if (e.depth > depth) {
current.push_back(e.name); current.push_back(e.id);
} else if (e.depth == depth) { } else if (e.depth == depth) {
current.back() = e.name; current.back() = e.id;
} else { } else {
current.pop_back(); current.pop_back();
current.back() = e.name; current.back() = e.id;
} }
if (e.collapsed) if (e.collapsed)
@ -323,12 +389,12 @@ CommunitiesModel::FlatTree::restoreCollapsed()
for (auto &e : tree) { for (auto &e : tree) {
if (e.depth > depth) { if (e.depth > depth) {
current.push_back(e.name); current.push_back(e.id);
} else if (e.depth == depth) { } else if (e.depth == depth) {
current.back() = e.name; current.back() = e.id;
} else { } else {
current.pop_back(); current.pop_back();
current.back() = e.name; current.back() = e.id;
} }
if (elements.contains(current)) if (elements.contains(current))
@ -353,7 +419,6 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_)
bool tagsUpdated = false; bool tagsUpdated = false;
for (const auto &[roomid, room] : sync_.rooms.join) { for (const auto &[roomid, room] : sync_.rooms.join) {
(void)roomid;
for (const auto &e : room.account_data.events) for (const auto &e : room.account_data.events)
if (std::holds_alternative< if (std::holds_alternative<
mtx::events::AccountDataEvent<mtx::events::account_data::Tags>>(e)) { mtx::events::AccountDataEvent<mtx::events::account_data::Tags>>(e)) {
@ -380,8 +445,12 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_)
tagsUpdated = true; tagsUpdated = true;
} }
for (const auto &e : sync_.account_data.events) { for (const auto &e : sync_.account_data.events) {
if (std::holds_alternative< if (auto event =
mtx::events::AccountDataEvent<mtx::events::account_data::Direct>>(e)) { 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; tagsUpdated = true;
break; break;
} }
@ -392,7 +461,7 @@ CommunitiesModel::sync(const mtx::responses::Sync &sync_)
} }
void void
CommunitiesModel::setCurrentTagId(QString tagId) CommunitiesModel::setCurrentTagId(const QString &tagId)
{ {
if (tagId.startsWith(QLatin1String("tag:"))) { if (tagId.startsWith(QLatin1String("tag:"))) {
auto tag = tagId.mid(4); auto tag = tagId.mid(4);
@ -406,7 +475,7 @@ CommunitiesModel::setCurrentTagId(QString tagId)
} else if (tagId.startsWith(QLatin1String("space:"))) { } else if (tagId.startsWith(QLatin1String("space:"))) {
auto tag = tagId.mid(6); auto tag = tagId.mid(6);
for (const auto &t : spaceOrder_.tree) { for (const auto &t : spaceOrder_.tree) {
if (t.name == tag) { if (t.id == tag) {
this->currentTagId_ = tagId; this->currentTagId_ = tagId;
emit currentTagIdChanged(currentTagId_); emit currentTagIdChanged(currentTagId_);
return; return;
@ -425,13 +494,11 @@ CommunitiesModel::setCurrentTagId(QString tagId)
void void
CommunitiesModel::toggleTagId(QString tagId) CommunitiesModel::toggleTagId(QString tagId)
{ {
if (hiddentTagIds_.contains(tagId)) { if (hiddenTagIds_.contains(tagId))
hiddentTagIds_.removeOne(tagId); hiddenTagIds_.removeOne(tagId);
UserSettings::instance()->setHiddenTags(hiddentTagIds_); else
} else { hiddenTagIds_.push_back(tagId);
hiddentTagIds_.push_back(tagId); UserSettings::instance()->setHiddenTags(hiddenTagIds_);
UserSettings::instance()->setHiddenTags(hiddentTagIds_);
}
if (tagId.startsWith(QLatin1String("tag:"))) { if (tagId.startsWith(QLatin1String("tag:"))) {
auto idx = tags_.indexOf(tagId.mid(4)); auto idx = tags_.indexOf(tagId.mid(4));
@ -449,6 +516,34 @@ CommunitiesModel::toggleTagId(QString tagId)
emit hiddenTagsChanged(); 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) FilteredCommunitiesModel::FilteredCommunitiesModel(CommunitiesModel *model, QObject *parent)
: QSortFilterProxyModel(parent) : QSortFilterProxyModel(parent)
{ {

View file

@ -48,13 +48,17 @@ public:
Parent, Parent,
Depth, Depth,
Id, Id,
UnreadMessages,
HasLoudNotification,
Muted,
IsDirect,
}; };
struct FlatTree struct FlatTree
{ {
struct Elem struct Elem
{ {
QString name; QString id;
int depth = 0; int depth = 0;
bool collapsed = false; bool collapsed = false;
}; };
@ -65,7 +69,7 @@ public:
int indexOf(const QString &s) const int indexOf(const QString &s) const
{ {
for (int i = 0; i < size(); i++) for (int i = 0; i < size(); i++)
if (tree[i].name == s) if (tree[i].id == s)
return i; return i;
return -1; return -1;
} }
@ -121,7 +125,7 @@ public slots:
void sync(const mtx::responses::Sync &sync_); void sync(const mtx::responses::Sync &sync_);
void clear(); void clear();
QString currentTagId() const { return currentTagId_; } QString currentTagId() const { return currentTagId_; }
void setCurrentTagId(QString tagId); void setCurrentTagId(const QString &tagId);
void resetCurrentTagId() void resetCurrentTagId()
{ {
currentTagId_.clear(); currentTagId_.clear();
@ -138,6 +142,7 @@ public slots:
return tagsWD; return tagsWD;
} }
void toggleTagId(QString tagId); void toggleTagId(QString tagId);
void toggleTagMute(QString tagId);
FilteredCommunitiesModel *filtered() { return new FilteredCommunitiesModel(this, this); } FilteredCommunitiesModel *filtered() { return new FilteredCommunitiesModel(this, this); }
signals: signals:
@ -149,9 +154,11 @@ signals:
private: private:
QStringList tags_; QStringList tags_;
QString currentTagId_; QString currentTagId_;
QStringList hiddentTagIds_; QStringList hiddenTagIds_;
QStringList mutedTagIds_;
FlatTree spaceOrder_; FlatTree spaceOrder_;
std::map<QString, RoomInfo> spaces_; std::map<QString, RoomInfo> spaces_;
std::vector<std::string> directMessages_;
friend class FilteredCommunitiesModel; friend class FilteredCommunitiesModel;
}; };

View file

@ -330,10 +330,13 @@ RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification)
Qt::DisplayRole, Qt::DisplayRole,
}); });
if (getRoomById(room_id)->isSpace())
return; // no need to update space notifications
int total_unread_msgs = 0; int total_unread_msgs = 0;
for (const auto &room : qAsConst(models)) { for (const auto &room : qAsConst(models)) {
if (!room.isNull()) if (!room.isNull() && !room->isSpace())
total_unread_msgs += room->notificationCount(); total_unread_msgs += room->notificationCount();
} }

View file

@ -366,14 +366,24 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
auto roomInfo = cache::singleRoomInfo(room_id_.toStdString()); auto roomInfo = cache::singleRoomInfo(room_id_.toStdString());
this->isSpace_ = roomInfo.is_space; this->isSpace_ = roomInfo.is_space;
this->notification_count = roomInfo.notification_count; this->notification_count =
this->highlight_count = roomInfo.highlight_count; isSpace_ ? utils::getChildNotificationsForSpace(room_id_).first : roomInfo.notification_count;
this->highlight_count =
isSpace_ ? utils::getChildNotificationsForSpace(room_id_).second : roomInfo.highlight_count;
lastMessage_.timestamp = roomInfo.approximate_last_modification_ts; lastMessage_.timestamp = roomInfo.approximate_last_modification_ts;
// this connection will simplify adding the plainRoomNameChanged() signal everywhere that it // this connection will simplify adding the plainRoomNameChanged() signal everywhere that it
// needs to be // needs to be
connect(this, &TimelineModel::roomNameChanged, this, &TimelineModel::plainRoomNameChanged); connect(this, &TimelineModel::roomNameChanged, this, &TimelineModel::plainRoomNameChanged);
if (isSpace_)
connect(ChatPage::instance(), &ChatPage::unreadMessages, this, [this](int) {
auto temp{utils::getChildNotificationsForSpace(room_id_)};
notification_count = temp.first;
highlight_count = temp.second;
emit notificationsChanged();
});
connect( connect(
this, this,
&TimelineModel::redactionFailed, &TimelineModel::redactionFailed,