From 7e538851d6e3779434722e56a968e9f8b8a9da0d Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Wed, 28 Jul 2021 21:31:37 -0400 Subject: [PATCH] Use a QSortFilterProxyModel instead of resetting the model --- CMakeLists.txt | 4 +-- resources/qml/ReadReceipts.qml | 4 +-- src/ReadReceiptsModel.cpp | 53 +++++++++++++++++++--------- src/ReadReceiptsModel.h | 27 ++++++++++++-- src/timeline/TimelineModel.cpp | 2 +- src/timeline/TimelineModel.h | 2 +- src/timeline/TimelineViewManager.cpp | 6 ++-- 7 files changed, 70 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fc8e19d..80ea628f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -350,7 +350,7 @@ set(SRC_FILES src/MemberList.cpp src/MxcImageProvider.cpp src/Olm.cpp - src/ReadReceiptsModel.cpp + src/ReadReceiptsModel.cpp src/RegisterPage.cpp src/SSOHandler.cpp src/CombinedImagePackModel.cpp @@ -555,7 +555,7 @@ qt5_wrap_cpp(MOC_HEADERS src/MainWindow.h src/MemberList.h src/MxcImageProvider.h - src/ReadReceiptsModel.h + src/ReadReceiptsModel.h src/RegisterPage.h src/SSOHandler.h src/CombinedImagePackModel.h diff --git a/resources/qml/ReadReceipts.qml b/resources/qml/ReadReceipts.qml index 84dc5666..5f213328 100644 --- a/resources/qml/ReadReceipts.qml +++ b/resources/qml/ReadReceipts.qml @@ -10,7 +10,7 @@ import im.nheko 1.0 ApplicationWindow { id: readReceiptsRoot - property ReadReceiptsModel readReceipts + property ReadReceiptsProxy readReceipts x: MainWindow.x + (MainWindow.width / 2) - (width / 2) y: MainWindow.y + (MainWindow.height / 2) - (height / 2) @@ -86,7 +86,7 @@ ApplicationWindow { ToolTip.text: model.mxid TapHandler { - onSingleTapped: chat.model.openUserProfile(userId) + onSingleTapped: Rooms.currentRoom.openUserProfile(userId) } CursorShape { diff --git a/src/ReadReceiptsModel.cpp b/src/ReadReceiptsModel.cpp index 936c6d61..0be22be2 100644 --- a/src/ReadReceiptsModel.cpp +++ b/src/ReadReceiptsModel.cpp @@ -46,10 +46,13 @@ ReadReceiptsModel::update() QHash ReadReceiptsModel::roleNames() const { - return {{Mxid, "mxid"}, - {DisplayName, "displayName"}, - {AvatarUrl, "avatarUrl"}, - {Timestamp, "timestamp"}}; + // Note: RawTimestamp is purposely not included here + return { + {Mxid, "mxid"}, + {DisplayName, "displayName"}, + {AvatarUrl, "avatarUrl"}, + {Timestamp, "timestamp"}, + }; } QVariant @@ -67,6 +70,8 @@ ReadReceiptsModel::data(const QModelIndex &index, int role) const return cache::avatarUrl(room_id_, readReceipts_[index.row()].first); case Timestamp: return dateFormat(readReceipts_[index.row()].second); + case RawTimestamp: + return readReceipts_[index.row()].second; default: return {}; } @@ -76,21 +81,22 @@ void ReadReceiptsModel::addUsers( const std::multimap> &users) { - beginResetModel(); + auto newReceipts = users.size() - readReceipts_.size(); - readReceipts_.clear(); - for (const auto &user : users) { - readReceipts_.push_back({QString::fromStdString(user.second), - QDateTime::fromMSecsSinceEpoch(user.first)}); - } + if (newReceipts > 0) { + beginInsertRows( + QModelIndex{}, readReceipts_.size(), readReceipts_.size() + newReceipts - 1); - std::sort(readReceipts_.begin(), - readReceipts_.end(), - [](const QPair &a, const QPair &b) { - return a.second > b.second; - }); + for (const auto &user : users) { + QPair item = { + QString::fromStdString(user.second), + QDateTime::fromMSecsSinceEpoch(user.first)}; + if (!readReceipts_.contains(item)) + readReceipts_.push_back(item); + } - endResetModel(); + endInsertRows(); + } } QString @@ -112,3 +118,18 @@ ReadReceiptsModel::dateFormat(const QDateTime &then) const return QLocale::system().toString(then.time(), QLocale::ShortFormat); } + +ReadReceiptsProxy::ReadReceiptsProxy(QString event_id, QString room_id, QObject *parent) + : QSortFilterProxyModel{parent} + , model_{event_id, room_id, this} +{ + setSourceModel(&model_); + setSortRole(ReadReceiptsModel::RawTimestamp); +} + +bool +ReadReceiptsProxy::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const +{ + // since we are sorting from greatest to least timestamp, return something that looks totally backwards! + return source_left.data().toULongLong() > source_right.data().toULongLong(); +} diff --git a/src/ReadReceiptsModel.h b/src/ReadReceiptsModel.h index f2e39f88..9e26bcd5 100644 --- a/src/ReadReceiptsModel.h +++ b/src/ReadReceiptsModel.h @@ -8,15 +8,13 @@ #include #include #include +#include #include class ReadReceiptsModel : public QAbstractListModel { Q_OBJECT - Q_PROPERTY(QString eventId READ eventId CONSTANT) - Q_PROPERTY(QString roomId READ roomId CONSTANT) - public: enum Roles { @@ -24,6 +22,7 @@ public: DisplayName, AvatarUrl, Timestamp, + RawTimestamp, }; explicit ReadReceiptsModel(QString event_id, QString room_id, QObject *parent = nullptr); @@ -51,4 +50,26 @@ private: QVector> readReceipts_; }; +class ReadReceiptsProxy : public QSortFilterProxyModel +{ + Q_OBJECT + + Q_PROPERTY(QString eventId READ eventId CONSTANT) + Q_PROPERTY(QString roomId READ roomId CONSTANT) + +public: + explicit ReadReceiptsProxy(QString event_id, QString room_id, QObject *parent = nullptr); + + QString eventId() const { return event_id_; } + QString roomId() const { return room_id_; } + + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const; + +private: + QString event_id_; + QString room_id_; + + ReadReceiptsModel model_; +}; + #endif // READRECEIPTSMODEL_H diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index f5737063..6ae0c4d1 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -1092,7 +1092,7 @@ TimelineModel::relatedInfo(QString id) void TimelineModel::showReadReceipts(QString id) { - emit openReadReceiptsDialog(new ReadReceiptsModel{id, roomId(), this}); + emit openReadReceiptsDialog(new ReadReceiptsProxy{id, roomId(), this}); } void diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 82fce257..0d5f7109 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -349,7 +349,7 @@ signals: void typingUsersChanged(std::vector users); void replyChanged(QString reply); void editChanged(QString reply); - void openReadReceiptsDialog(ReadReceiptsModel *rr); + void openReadReceiptsDialog(ReadReceiptsProxy *rr); void paginationInProgressChanged(const bool); void newCallEvent(const mtx::events::collections::TimelineEvents &event); void scrollToIndex(int index); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 58b0d5a8..76bc127e 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -206,12 +206,12 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par 0, "InviteesModel", "InviteesModel needs to be instantiated on the C++ side"); - qmlRegisterUncreatableType( + qmlRegisterUncreatableType( "im.nheko", 1, 0, - "ReadReceiptsModel", - "ReadReceiptsModel needs to be instantiated on the C++ side"); + "ReadReceiptsProxy", + "ReadReceiptsProxy needs to be instantiated on the C++ side"); static auto self = this; qmlRegisterSingletonType(