From 042032ec46f526c875f41331be69c0cc6f20a0ac Mon Sep 17 00:00:00 2001 From: James Graham Date: Sat, 20 Jul 2024 18:12:30 +0000 Subject: [PATCH] Update user sort Update the user model so it also sorts by power level and update how we initialize the model to improve performance. The following is also changed: - Store a single `UserListModel` in `RoomManager` and use it for everything, this means we don't create extra models (incluiding the long initialisation for each in big rooms) - By using the single model once it has loaded the users of the new room opening and closing the draw now happens instantly (previously the model would have to be loaded every time the drawer was opened). - To stop the initial loading and room change of Neochat slowing down (as the `UserListModel` would be loaded before the `TimelineView` is shown) the initialisation of the model is delayed until the `TimelineView` has loaded. This prioritises showing some messages in the timeline over populating the model so in large rooms the user list will initially be blank, but this keeps the initial load snappier. --- src/models/completionmodel.cpp | 6 ++---- src/models/userlistmodel.cpp | 23 +++++++++++++++++++++-- src/models/userlistmodel.h | 4 ++++ src/neochatroom.cpp | 15 +++++++++++---- src/qml/RoomInformation.qml | 15 +-------------- src/qml/TimelineView.qml | 1 + src/roommanager.cpp | 12 ++++++++++++ src/roommanager.h | 15 +++++++++++++++ src/settings/Permissions.qml | 9 ++------- 9 files changed, 69 insertions(+), 31 deletions(-) diff --git a/src/models/completionmodel.cpp b/src/models/completionmodel.cpp index 789545ef6..fe03263e3 100644 --- a/src/models/completionmodel.cpp +++ b/src/models/completionmodel.cpp @@ -9,18 +9,16 @@ #include "customemojimodel.h" #include "emojimodel.h" #include "neochatroom.h" +#include "roommanager.h" #include "userlistmodel.h" CompletionModel::CompletionModel(QObject *parent) : QAbstractListModel(parent) , m_filterModel(new CompletionProxyModel()) - , m_userListModel(new UserListModel(this)) + , m_userListModel(RoomManager::instance().userListModel()) , m_emojiModel(new QConcatenateTablesProxyModel(this)) { connect(this, &CompletionModel::textChanged, this, &CompletionModel::updateCompletion); - connect(this, &CompletionModel::roomChanged, this, [this]() { - m_userListModel->setRoom(m_room); - }); m_emojiModel->addSourceModel(&CustomEmojiModel::instance()); m_emojiModel->addSourceModel(&EmojiModel::instance()); } diff --git a/src/models/userlistmodel.cpp b/src/models/userlistmodel.cpp index fe3f8f839..ba3a4cbf5 100644 --- a/src/models/userlistmodel.cpp +++ b/src/models/userlistmodel.cpp @@ -33,6 +33,7 @@ void UserListModel::setRoom(NeoChatRoom *room) m_currentRoom->disconnect(this); m_currentRoom->connection()->disconnect(this); m_currentRoom = nullptr; + m_members.clear(); endResetModel(); } @@ -56,7 +57,7 @@ void UserListModel::setRoom(NeoChatRoom *room) }); } - refreshAllMembers(); + m_active = false; Q_EMIT roomChanged(); } @@ -169,7 +170,6 @@ void UserListModel::refreshMember(const Quotient::RoomMember &member, const QLis void UserListModel::refreshAllMembers() { beginResetModel(); - m_members.clear(); if (m_currentRoom != nullptr) { m_members = m_currentRoom->joinedMemberIds(); @@ -179,8 +179,17 @@ void UserListModel::refreshAllMembers() MemberSorter sorter(m_currentRoom); #endif std::sort(m_members.begin(), m_members.end(), [&sorter, this](const auto &left, const auto &right) { + const auto leftPl = m_currentRoom->getUserPowerLevel(left); + const auto rightPl = m_currentRoom->getUserPowerLevel(right); + if (leftPl > rightPl) { + return true; + } else if (rightPl > leftPl) { + return false; + } + return sorter(m_currentRoom->member(left), m_currentRoom->member(right)); }); + } endResetModel(); Q_EMIT usersRefreshed(); @@ -216,4 +225,14 @@ QHash UserListModel::roleNames() const return roles; } +void UserListModel::activate() +{ + if (m_active) { + return; + } + + m_active = true; + refreshAllMembers(); +} + #include "moc_userlistmodel.cpp" diff --git a/src/models/userlistmodel.h b/src/models/userlistmodel.h index e579da0a9..39acd22e3 100644 --- a/src/models/userlistmodel.h +++ b/src/models/userlistmodel.h @@ -77,6 +77,8 @@ public: */ [[nodiscard]] QHash roleNames() const override; + void activate(); + Q_SIGNALS: void roomChanged(); void usersRefreshed(); @@ -94,6 +96,8 @@ private: QPointer m_currentRoom; QList m_members; + bool m_active = false; + int findUserPos(const Quotient::RoomMember &member) const; [[nodiscard]] int findUserPos(const QString &username) const; }; diff --git a/src/neochatroom.cpp b/src/neochatroom.cpp index 62e2fda6c..0d54e0dc4 100644 --- a/src/neochatroom.cpp +++ b/src/neochatroom.cpp @@ -866,11 +866,18 @@ void NeoChatRoom::setUserPowerLevel(const QString &userID, const int &powerLevel int NeoChatRoom::getUserPowerLevel(const QString &userId) const { - auto powerLevelEvent = currentState().get(); - if (!powerLevelEvent) { - return 0; + if (!successorId().isEmpty()) { + return 0; // No one can upgrade a room that's already upgraded } - return powerLevelEvent->powerLevelForUser(userId); + + const auto &mId = userId.isEmpty() ? connection()->userId() : userId; + if (const auto *plEvent = currentState().get()) { + return plEvent->powerLevelForUser(mId); + } + if (const auto *createEvent = creation()) { + return createEvent->senderId() == mId ? 100 : 0; + } + return 0; // That's rather weird but may happen, according to rvdh } QCoro::Task NeoChatRoom::doDeleteMessagesByUser(const QString &user, QString reason) diff --git a/src/qml/RoomInformation.qml b/src/qml/RoomInformation.qml index 23449bad9..653dc5557 100644 --- a/src/qml/RoomInformation.qml +++ b/src/qml/RoomInformation.qml @@ -192,20 +192,7 @@ QQC2.ScrollView { } } - KSortFilterProxyModel { - id: sortedMessageEventModel - - sourceModel: UserListModel { - room: root.room - } - - sortRoleName: "powerLevel" - sortOrder: Qt.DescendingOrder - filterRoleName: "name" - filterCaseSensitivity: Qt.CaseInsensitive - } - - model: root.room.isDirectChat() ? 0 : sortedMessageEventModel + model: root.room.isDirectChat() ? 0 : RoomManager.userListModel clip: true focus: true diff --git a/src/qml/TimelineView.qml b/src/qml/TimelineView.qml index f7689ba89..a3179b4d2 100644 --- a/src/qml/TimelineView.qml +++ b/src/qml/TimelineView.qml @@ -108,6 +108,7 @@ QQC2.ScrollView { onTriggered: { root.roomChanging = false; markReadIfVisibleTimer.reset(); + RoomManager.activateUserModel(); } } onAtYEndChanged: if (!root.roomChanging) { diff --git a/src/roommanager.cpp b/src/roommanager.cpp index 3afd35d85..063799f49 100644 --- a/src/roommanager.cpp +++ b/src/roommanager.cpp @@ -39,6 +39,7 @@ RoomManager::RoomManager(QObject *parent) , m_timelineModel(new TimelineModel(this)) , m_messageFilterModel(new MessageFilterModel(this, m_timelineModel)) , m_mediaMessageFilterModel(new MediaMessageFilterModel(this, m_messageFilterModel)) + , m_userListModel(new UserListModel(this)) { m_lastRoomConfig = m_config->group(QStringLiteral("LastOpenRoom")); m_lastSpaceConfig = m_config->group(QStringLiteral("LastOpenSpace")); @@ -46,6 +47,7 @@ RoomManager::RoomManager(QObject *parent) connect(this, &RoomManager::currentRoomChanged, this, [this]() { m_timelineModel->setRoom(m_currentRoom); + m_userListModel->setRoom(m_currentRoom); }); connect(&Controller::instance(), &Controller::activeConnectionChanged, this, [this](NeoChatConnection *connection) { @@ -113,6 +115,16 @@ MediaMessageFilterModel *RoomManager::mediaMessageFilterModel() const return m_mediaMessageFilterModel; } +UserListModel *RoomManager::userListModel() const +{ + return m_userListModel; +} + +void RoomManager::activateUserModel() +{ + m_userListModel->activate(); +} + UriResolveResult RoomManager::resolveResource(const Uri &uri) { return UriResolverBase::visitResource(m_connection, uri); diff --git a/src/roommanager.h b/src/roommanager.h index 410f42aa1..07de885df 100644 --- a/src/roommanager.h +++ b/src/roommanager.h @@ -22,6 +22,7 @@ #include "models/sortfilterroomtreemodel.h" #include "models/sortfilterspacelistmodel.h" #include "models/timelinemodel.h" +#include "models/userlistmodel.h" class NeoChatRoom; class NeoChatConnection; @@ -120,6 +121,14 @@ class RoomManager : public QObject, public UriResolverBase */ Q_PROPERTY(MediaMessageFilterModel *mediaMessageFilterModel READ mediaMessageFilterModel CONSTANT) + /** + * @brief The UserListModel that should be used for room member visualisation. + * + * @note Available here so that the room page and drawer both have access to the + * same model. + */ + Q_PROPERTY(UserListModel *userListModel READ userListModel CONSTANT) + /** * @brief Whether a room is currently open in NeoChat. * @@ -155,6 +164,9 @@ public: MessageFilterModel *messageFilterModel() const; MediaMessageFilterModel *mediaMessageFilterModel() const; + UserListModel *userListModel() const; + Q_INVOKABLE void activateUserModel(); + /** * @brief Resolve the given URI resource. * @@ -359,6 +371,9 @@ private: TimelineModel *m_timelineModel; MessageFilterModel *m_messageFilterModel; MediaMessageFilterModel *m_mediaMessageFilterModel; + + UserListModel *m_userListModel; + QPointer m_connection; void setCurrentRoom(const QString &roomId); diff --git a/src/settings/Permissions.qml b/src/settings/Permissions.qml index 130f9b45a..2dbcff655 100644 --- a/src/settings/Permissions.qml +++ b/src/settings/Permissions.qml @@ -20,11 +20,6 @@ FormCard.FormCardPage { title: i18nc('@title:window', 'Permissions') - property UserListModel userListModel: UserListModel { - id: userListModel - room: root.room - } - readonly property PowerLevelModel powerLevelModel: PowerLevelModel { showMute: false } @@ -39,7 +34,7 @@ FormCard.FormCardPage { FormCard.FormCard { Repeater { model: KSortFilterProxyModel { - sourceModel: userListModel + sourceModel: RoomManager.userListModel sortRoleName: "powerLevel" sortOrder: Qt.DescendingOrder filterRowCallback: function (source_row, source_parent) { @@ -158,7 +153,7 @@ FormCard.FormCardPage { model: UserFilterModel { id: userListFilterModel - sourceModel: userListModel + sourceModel: RoomManager.userListModel filterText: userListSearchField.text onFilterTextChanged: {