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
import "./components"
import "./dialogs"
import Qt.labs.platform 1.1 as Platform
import QtQml 2.12
@ -36,14 +37,27 @@ Page {
id: communityContextMenu
property string tagId
property bool hidden
property bool muted
function show(id_, tags_) {
function show(id_, hidden_, muted_) {
tagId = id_;
hidden = hidden_;
muted = muted_;
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 {
text: qsTr("Hide rooms with this tag or from this space by default.")
checkable: true
checked: communityContextMenu.hidden
onTriggered: Communities.toggleTagId(communityContextMenu.tagId)
}
@ -57,19 +71,30 @@ Page {
property color unimportantText: Nheko.colors.buttonText
property color bubbleBackground: Nheko.colors.highlight
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
width: ListView.view.width
state: "normal"
ToolTip.visible: hovered && collapsed
ToolTip.text: model.tooltip
ToolTip.text: communityItem.tooltip
ToolTip.delay: Nheko.tooltipDelay
onClicked: Communities.setCurrentTagId(model.id)
onPressAndHold: communityContextMenu.show(model.id)
onClicked: Communities.setCurrentTagId(communityItem.id)
onPressAndHold: communityContextMenu.show(communityItem.id, communityItem.hidden, communityItem.muted)
states: [
State {
name: "highlight"
when: (communityItem.hovered || model.hidden) && !(Communities.currentTagId == model.id)
when: (communityItem.hovered || communityItem.hidden) && !(Communities.currentTagId === communityItem.id)
PropertyChanges {
target: communityItem
@ -83,7 +108,7 @@ Page {
},
State {
name: "selected"
when: Communities.currentTagId == model.id
when: Communities.currentTagId == communityItem.id
PropertyChanges {
target: communityItem
@ -102,7 +127,7 @@ Page {
TapHandler {
acceptedButtons: Qt.RightButton
onSingleTapped: communityContextMenu.show(model.id)
onSingleTapped: communityContextMenu.show(communityItem.id, communityItem.hidden, communityItem.muted)
gesturePolicy: TapHandler.ReleaseWithinBounds
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
}
@ -114,27 +139,27 @@ Page {
spacing: Nheko.paddingMedium
anchors.fill: parent
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 {
visible: !communitySidebar.collapsed && model.collapsible
visible: !communitySidebar.collapsed && communityItem.collapsible
Layout.preferredHeight: fontMetrics.lineSpacing
Layout.preferredWidth: fontMetrics.lineSpacing
Layout.alignment: Qt.AlignVCenter
height: 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.delay: Nheko.tooltipDelay
ToolTip.text: model.collapsed ? qsTr("Expand") : qsTr("Collapse")
ToolTip.text: communityItem.collapsed ? qsTr("Expand") : qsTr("Collapse")
hoverEnabled: true
onClicked: model.collapsed = !model.collapsed
onClicked: communityItem.collapsed = !communityItem.collapsed
}
Item {
Layout.preferredWidth: fontMetrics.lineSpacing
visible: !communitySidebar.collapsed && !model.collapsible && Communities.containsSubspaces
visible: !communitySidebar.collapsed && !communityItem.collapsible && Communities.containsSubspaces
}
Avatar {
@ -145,14 +170,27 @@ Page {
height: avatarSize
width: avatarSize
url: {
if (model.avatarUrl.startsWith("mxc://"))
return model.avatarUrl.replace("mxc://", "image://MxcImage/");
if (communityItem.avatarUrl.startsWith("mxc://"))
return communityItem.avatarUrl.replace("mxc://", "image://MxcImage/");
else
return "image://colorimage/" + model.avatarUrl + "?" + communityItem.unimportantText;
return "image://colorimage/" + communityItem.avatarUrl + "?" + communityItem.unimportantText;
}
roomid: model.id
displayName: model.displayName
roomid: communityItem.id
displayName: communityItem.displayName
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 {
@ -161,7 +199,7 @@ Page {
color: communityItem.importantText
Layout.fillWidth: true
elideWidth: width
fullText: model.displayName
fullText: communityItem.displayName
textFormat: Text.PlainText
}
@ -169,10 +207,20 @@ Page {
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 {
color: backgroundColor
color: communityItem.backgroundColor
}
}

View File

@ -3,6 +3,7 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./components"
import "./dialogs"
import "./ui"
import Qt.labs.platform 1.1 as Platform
@ -294,9 +295,6 @@ Page {
anchors.margins: Nheko.paddingMedium
Avatar {
// In the future we could show an online indicator by setting the userid for the avatar
//userid: Nheko.currentUser.userid
id: avatar
enabled: false
@ -308,33 +306,17 @@ Page {
userid: isDirect ? directChatOtherUserId : ""
roomid: roomId
Rectangle {
NotificationBubble {
id: collapsedNotificationBubble
notificationCount: roomItem.notificationCount
hasLoudNotification: roomItem.hasLoudNotification
bubbleBackgroundColor: roomItem.bubbleBackground
bubbleTextColor: roomItem.bubbleText
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: -Nheko.paddingSmall
visible: collapsed && notificationCount > 0
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
}
mayBeVisible: collapsed && (isSpace ? Settings.spaceNotifications : true)
}
}
@ -351,7 +333,24 @@ Page {
height: avatar.height
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 {
id: titleRow
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
spacing: Nheko.paddingSmall
@ -380,6 +379,8 @@ Page {
}
RowLayout {
id: subtextRow
Layout.fillWidth: true
spacing: 0
visible: !isSpace
@ -395,40 +396,6 @@ Page {
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/ScreenShare.qml</file>
<file>qml/voip/VideoCall.qml</file>
<file>qml/components/NotificationBubble.qml</file>
</qresource>
<qresource prefix="/media">
<file>media/ring.ogg</file>

View File

@ -88,7 +88,8 @@ UserSettings::load(std::optional<QString> profile)
openImageExternal_ = settings.value(QStringLiteral("user/open_image_external"), false).toBool();
openVideoExternal_ = settings.value(QStringLiteral("user/open_video_external"), false).toBool();
decryptSidebar_ = settings.value(QStringLiteral("user/decrypt_sidebar"), true).toBool();
privacyScreen_ = settings.value(QStringLiteral("user/privacy_screen"), false).toBool();
spaceNotifications_ = settings.value(QStringLiteral("user/space_notifications"), true).toBool();
privacyScreen_ = settings.value(QStringLiteral("user/privacy_screen"), false).toBool();
privacyScreenTimeout_ =
settings.value(QStringLiteral("user/privacy_screen_timeout"), 0).toInt();
exposeDBusApi_ = settings.value(QStringLiteral("user/expose_dbus_api"), false).toBool();
@ -132,7 +133,8 @@ UserSettings::load(std::optional<QString> profile)
userId_ = settings.value(prefix + "auth/user_id", "").toString();
deviceId_ = settings.value(prefix + "auth/device_id", "").toString();
hiddenTags_ = settings.value(prefix + "user/hidden_tags", QStringList{}).toStringList();
hiddenPins_ = settings.value(prefix + "user/hidden_pins", QStringList{}).toStringList();
mutedTags_ = settings.value(prefix + "user/muted_tags", QStringList{"global"}).toStringList();
hiddenPins_ = settings.value(prefix + "user/hidden_pins", QStringList{}).toStringList();
hiddenWidgets_ = settings.value(prefix + "user/hidden_widgets", QStringList{}).toStringList();
recentReactions_ =
settings.value(prefix + "user/recent_reactions", QStringList{}).toStringList();
@ -220,14 +222,21 @@ UserSettings::setGroupView(bool state)
}
void
UserSettings::setHiddenTags(QStringList hiddenTags)
UserSettings::setHiddenTags(const QStringList &hiddenTags)
{
hiddenTags_ = hiddenTags;
save();
}
void
UserSettings::setHiddenPins(QStringList hiddenTags)
UserSettings::setMutedTags(const QStringList &mutedTags)
{
mutedTags_ = mutedTags;
save();
}
void
UserSettings::setHiddenPins(const QStringList &hiddenTags)
{
hiddenPins_ = hiddenTags;
save();
@ -235,7 +244,7 @@ UserSettings::setHiddenPins(QStringList hiddenTags)
}
void
UserSettings::setHiddenWidgets(QStringList hiddenTags)
UserSettings::setHiddenWidgets(const QStringList &hiddenTags)
{
hiddenWidgets_ = hiddenTags;
save();
@ -416,6 +425,16 @@ UserSettings::setDecryptSidebar(bool state)
save();
}
void
UserSettings::setSpaceNotifications(bool state)
{
if (state == spaceNotifications_)
return;
spaceNotifications_ = state;
emit spaceNotificationsChanged(state);
save();
}
void
UserSettings::setPrivacyScreen(bool state)
{
@ -777,6 +796,7 @@ UserSettings::save()
settings.setValue(QStringLiteral("avatar_circles"), avatarCircles_);
settings.setValue(QStringLiteral("decrypt_sidebar"), decryptSidebar_);
settings.setValue(QStringLiteral("space_notifications"), spaceNotifications_);
settings.setValue(QStringLiteral("privacy_screen"), privacyScreen_);
settings.setValue(QStringLiteral("privacy_screen_timeout"), privacyScreenTimeout_);
settings.setValue(QStringLiteral("mobile_mode"), mobileMode_);
@ -830,6 +850,7 @@ UserSettings::save()
onlyShareKeysWithVerifiedUsers_);
settings.setValue(prefix + "user/online_key_backup", useOnlineKeyBackup_);
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_widgets", hiddenWidgets_);
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");
case DecryptSidebar:
return tr("Decrypt messages in sidebar");
case SpaceNotifications:
return tr("Show message counts for spaces");
case PrivacyScreen:
return tr("Privacy Screen");
case PrivacyScreenTimeout:
@ -1053,6 +1076,8 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
return i->openVideoExternal();
case DecryptSidebar:
return i->decryptSidebar();
case SpaceNotifications:
return i->spaceNotifications();
case PrivacyScreen:
return i->privacyScreen();
case PrivacyScreenTimeout:
@ -1208,6 +1233,9 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
case DecryptSidebar:
return tr("Decrypt the messages shown in the sidebar.\nOnly affects messages in "
"encrypted chats.");
case SpaceNotifications:
return tr(
"Choose where to show the total number of notifications contained within a space.");
case PrivacyScreen:
return tr("When the window loses focus, the timeline will\nbe blurred.");
case MobileMode:
@ -1317,6 +1345,7 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
case ShareKeysWithTrustedUsers:
case UseOnlineKeyBackup:
case ExposeDBusApi:
case SpaceNotifications:
return Toggle;
case Profile:
case UserId:
@ -1409,7 +1438,7 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
return fontDb.families();
case EmojiFont:
return fontDb.families(QFontDatabase::WritingSystem::Symbol);
case Ringtone:
case Ringtone: {
QStringList l{
QStringLiteral("Mute"),
QStringLiteral("Default"),
@ -1419,6 +1448,7 @@ UserSettingsModel::data(const QModelIndex &index, int role) const
l.push_back(i->ringtone());
return l;
}
}
} else if (role == Good) {
switch (index.row()) {
case OnlineBackupKey:
@ -1624,6 +1654,13 @@ UserSettingsModel::setData(const QModelIndex &index, const QVariant &value, int
return false;
}
return i->decryptSidebar();
case SpaceNotifications: {
if (value.userType() == QMetaType::Bool) {
i->setSpaceNotifications(value.toBool());
return true;
} else
return false;
}
case PrivacyScreen: {
if (value.userType() == QMetaType::Bool) {
i->setPrivacyScreen(value.toBool());
@ -1936,7 +1973,9 @@ UserSettingsModel::UserSettingsModel(QObject *p)
connect(s.get(), &UserSettings::decryptSidebarChanged, this, [this]() {
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]() {
emit dataChanged(index(Tray), index(Tray), {Value});
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)
Q_PROPERTY(
bool decryptSidebar READ decryptSidebar WRITE setDecryptSidebar NOTIFY decryptSidebarChanged)
Q_PROPERTY(bool spaceNotifications READ spaceNotifications WRITE setSpaceNotifications NOTIFY
spaceNotificationsChanged)
Q_PROPERTY(
bool privacyScreen READ privacyScreen WRITE setPrivacyScreen NOTIFY privacyScreenChanged)
Q_PROPERTY(int privacyScreenTimeout READ privacyScreenTimeout WRITE setPrivacyScreenTimeout
@ -162,6 +164,7 @@ public:
void setAlertOnNotification(bool state);
void setAvatarCircles(bool state);
void setDecryptSidebar(bool state);
void setSpaceNotifications(bool state);
void setPrivacyScreen(bool state);
void setPrivacyScreenTimeout(int state);
void setPresence(Presence state);
@ -184,9 +187,10 @@ public:
void setDeviceId(QString deviceId);
void setHomeserver(QString homeserver);
void setDisableCertificateValidation(bool disabled);
void setHiddenTags(QStringList hiddenTags);
void setHiddenPins(QStringList hiddenTags);
void setHiddenWidgets(QStringList hiddenTags);
void setHiddenTags(const QStringList &hiddenTags);
void setMutedTags(const QStringList &mutedTags);
void setHiddenPins(const QStringList &hiddenTags);
void setHiddenWidgets(const QStringList &hiddenTags);
void setRecentReactions(QStringList recent);
void setUseIdenticon(bool state);
void setOpenImageExternal(bool state);
@ -202,6 +206,7 @@ public:
bool groupView() const { return groupView_; }
bool avatarCircles() const { return avatarCircles_; }
bool decryptSidebar() const { return decryptSidebar_; }
bool spaceNotifications() const { return spaceNotifications_; }
bool privacyScreen() const { return privacyScreen_; }
int privacyScreenTimeout() const { return privacyScreenTimeout_; }
bool markdown() const { return markdown_; }
@ -250,6 +255,7 @@ public:
QString homeserver() const { return homeserver_; }
bool disableCertificateValidation() const { return disableCertificateValidation_; }
QStringList hiddenTags() const { return hiddenTags_; }
QStringList mutedTags() const { return mutedTags_; }
QStringList hiddenPins() const { return hiddenPins_; }
QStringList hiddenWidgets() const { return hiddenWidgets_; }
QStringList recentReactions() const { return recentReactions_; }
@ -278,6 +284,7 @@ signals:
void alertOnNotificationChanged(bool state);
void avatarCirclesChanged(bool state);
void decryptSidebarChanged(bool state);
void spaceNotificationsChanged(bool state);
void privacyScreenChanged(bool state);
void privacyScreenTimeoutChanged(int state);
void timelineMaxWidthChanged(int state);
@ -340,6 +347,7 @@ private:
bool hasAlertOnNotification_;
bool avatarCircles_;
bool decryptSidebar_;
bool spaceNotifications_;
bool privacyScreen_;
int privacyScreenTimeout_;
bool shareKeysWithTrustedUsers_;
@ -370,6 +378,7 @@ private:
QString deviceId_;
QString homeserver_;
QStringList hiddenTags_;
QStringList mutedTags_;
QStringList hiddenPins_;
QStringList hiddenWidgets_;
QStringList recentReactions_;
@ -424,6 +433,7 @@ class UserSettingsModel : public QAbstractListModel
GroupView,
SortByImportance,
DecryptSidebar,
SpaceNotifications,
TraySection,
Tray,

View File

@ -27,6 +27,7 @@
#include <cmark.h>
#include "Cache.h"
#include "Cache_p.h"
#include "Config.h"
#include "EventAccessors.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
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_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()}
{
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>
CommunitiesModel::roleNames() const
@ -28,6 +40,9 @@ CommunitiesModel::roleNames() const
{Hidden, "hidden"},
{Depth, "depth"},
{Id, "id"},
{UnreadMessages, "unreadMessages"},
{HasLoudNotification, "hasLoudNotification"},
{Muted, "muted"},
};
}
@ -50,6 +65,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 +92,17 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
return 0;
case CommunitiesModel::Roles::Id:
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) {
switch (role) {
@ -84,16 +117,27 @@ 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: {
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()) {
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 +151,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 +162,10 @@ 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:
return utils::getChildNotificationsForSpace(id).first;
case CommunitiesModel::Roles::HasLoudNotification:
return utils::getChildNotificationsForSpace(id).second > 0;
}
} else if (index.row() - 2 < tags_.size() + 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) {
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 +219,24 @@ CommunitiesModel::data(const QModelIndex &index, int role) const
return 0;
case CommunitiesModel::Roles::Id:
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();
@ -277,8 +343,8 @@ CommunitiesModel::initializeSidebar()
for (const auto &t : ts)
tags_.push_back(QString::fromStdString(t));
hiddentTagIds_ = UserSettings::instance()->hiddenTags();
spaceOrder_.restoreCollapsed();
endResetModel();
emit tagsChanged();
@ -298,12 +364,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 +389,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 +419,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)) {
@ -380,8 +445,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 +461,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 +475,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 +494,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 +516,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)
{

View File

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

View File

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

View File

@ -364,16 +364,26 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
{
this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString());
auto roomInfo = cache::singleRoomInfo(room_id_.toStdString());
this->isSpace_ = roomInfo.is_space;
this->notification_count = roomInfo.notification_count;
this->highlight_count = roomInfo.highlight_count;
lastMessage_.timestamp = roomInfo.approximate_last_modification_ts;
auto roomInfo = cache::singleRoomInfo(room_id_.toStdString());
this->isSpace_ = roomInfo.is_space;
this->notification_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;
// this connection will simplify adding the plainRoomNameChanged() signal everywhere that it
// needs to be
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(
this,
&TimelineModel::redactionFailed,