diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml
index bb8deda6..f2a957c9 100644
--- a/resources/qml/RoomList.qml
+++ b/resources/qml/RoomList.qml
@@ -131,7 +131,7 @@ Page {
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
font.pixelSize: fontMetrics.font.pixelSize * 0.9
color: roomItem.unimportantText
- text: model.timestamp
+ text: model.time
}
}
diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp
index 5fc4dc65..afe9679a 100644
--- a/src/timeline/RoomlistModel.cpp
+++ b/src/timeline/RoomlistModel.cpp
@@ -42,10 +42,13 @@ RoomlistModel::roleNames() const
{RoomName, "roomName"},
{RoomId, "roomId"},
{LastMessage, "lastMessage"},
+ {Time, "time"},
{Timestamp, "timestamp"},
{HasUnreadMessages, "hasUnreadMessages"},
{HasLoudNotification, "hasLoudNotification"},
{NotificationCount, "notificationCount"},
+ {IsInvite, "isInvite"},
+ {IsSpace, "isSpace"},
};
}
@@ -64,8 +67,10 @@ RoomlistModel::data(const QModelIndex &index, int role) const
return room->roomId();
case Roles::LastMessage:
return room->lastMessage().body;
- case Roles::Timestamp:
+ case Roles::Time:
return room->lastMessage().descriptiveTime;
+ case Roles::Timestamp:
+ return QVariant(static_cast<quint64>(room->lastMessage().timestamp));
case Roles::HasUnreadMessages:
return this->roomReadStatus.count(roomid) &&
this->roomReadStatus.at(roomid);
@@ -73,6 +78,9 @@ RoomlistModel::data(const QModelIndex &index, int role) const
return room->hasMentions();
case Roles::NotificationCount:
return room->notificationCount();
+ case Roles::IsInvite:
+ case Roles::IsSpace:
+ return false;
default:
return {};
}
@@ -90,9 +98,9 @@ RoomlistModel::updateReadStatus(const std::map<QString, bool> roomReadStatus_)
if (roomUnread != roomReadStatus[roomid]) {
roomsToUpdate.push_back(this->roomidToIndex(roomid));
}
- }
- this->roomReadStatus = roomReadStatus_;
+ this->roomReadStatus[roomid] = roomUnread;
+ }
for (auto idx : roomsToUpdate) {
emit dataChanged(index(idx),
@@ -135,6 +143,7 @@ RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification)
Roles::LastMessage,
Roles::Timestamp,
Roles::NotificationCount,
+ Qt::DisplayRole,
});
});
connect(
@@ -162,6 +171,7 @@ RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification)
{
Roles::HasLoudNotification,
Roles::NotificationCount,
+ Qt::DisplayRole,
});
int total_unread_msgs = 0;
@@ -225,7 +235,6 @@ RoomlistModel::initializeRooms(const std::vector<QString> &roomIds_)
beginResetModel();
models.clear();
roomids.clear();
- roomids = roomIds_;
for (const auto &id : roomIds_)
addRoom(id, true);
endResetModel();
@@ -239,3 +248,79 @@ RoomlistModel::clear()
roomids.clear();
endResetModel();
}
+
+namespace {
+enum NotificationImportance : short
+{
+ ImportanceDisabled = -1,
+ AllEventsRead = 0,
+ NewMessage = 1,
+ NewMentions = 2,
+ Invite = 3
+};
+}
+
+short int
+FilteredRoomlistModel::calculateImportance(const QModelIndex &idx) const
+{
+ // Returns the degree of importance of the unread messages in the room.
+ // If sorting by importance is disabled in settings, this only ever
+ // returns ImportanceDisabled or Invite
+ if (sourceModel()->data(idx, RoomlistModel::IsInvite).toBool()) {
+ return Invite;
+ } else if (!this->sortByImportance) {
+ return ImportanceDisabled;
+ } else if (sourceModel()->data(idx, RoomlistModel::HasLoudNotification).toBool()) {
+ return NewMentions;
+ } else if (sourceModel()->data(idx, RoomlistModel::NotificationCount).toInt() > 0) {
+ return NewMessage;
+ } else {
+ return AllEventsRead;
+ }
+}
+bool
+FilteredRoomlistModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
+{
+ QModelIndex const left_idx = sourceModel()->index(left.row(), 0, QModelIndex());
+ QModelIndex const right_idx = sourceModel()->index(right.row(), 0, QModelIndex());
+
+ // Sort by "importance" (i.e. invites before mentions before
+ // notifs before new events before old events), then secondly
+ // by recency.
+
+ // Checking importance first
+ const auto a_importance = calculateImportance(left_idx);
+ const auto b_importance = calculateImportance(right_idx);
+ if (a_importance != b_importance) {
+ return a_importance > b_importance;
+ }
+
+ // Now sort by recency
+ // Zero if empty, otherwise the time that the event occured
+ uint64_t a_recency = sourceModel()->data(left_idx, RoomlistModel::Timestamp).toULongLong();
+ uint64_t b_recency = sourceModel()->data(right_idx, RoomlistModel::Timestamp).toULongLong();
+
+ if (a_recency != b_recency)
+ return a_recency > b_recency;
+ else
+ return left.row() < right.row();
+}
+
+FilteredRoomlistModel::FilteredRoomlistModel(RoomlistModel *model, QObject *parent)
+ : QSortFilterProxyModel(parent)
+ , roomlistmodel(model)
+{
+ this->sortByImportance = UserSettings::instance()->sortByImportance();
+ setSourceModel(model);
+ setDynamicSortFilter(true);
+
+ QObject::connect(UserSettings::instance().get(),
+ &UserSettings::roomSortingChanged,
+ this,
+ [this](bool sortByImportance_) {
+ this->sortByImportance = sortByImportance_;
+ invalidate();
+ });
+
+ sort(0);
+}
diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h
index c4c9d9ba..c3374bd2 100644
--- a/src/timeline/RoomlistModel.h
+++ b/src/timeline/RoomlistModel.h
@@ -7,6 +7,7 @@
#include <QAbstractListModel>
#include <QHash>
#include <QSharedPointer>
+#include <QSortFilterProxyModel>
#include <QString>
#include <mtx/responses/sync.hpp>
@@ -24,10 +25,13 @@ public:
RoomName,
RoomId,
LastMessage,
+ Time,
Timestamp,
HasUnreadMessages,
HasLoudNotification,
NotificationCount,
+ IsInvite,
+ IsSpace,
};
RoomlistModel(TimelineViewManager *parent = nullptr);
@@ -73,4 +77,26 @@ private:
std::vector<QString> roomids;
QHash<QString, QSharedPointer<TimelineModel>> models;
std::map<QString, bool> roomReadStatus;
+
+ friend class FilteredRoomlistModel;
+};
+
+class FilteredRoomlistModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+public:
+ FilteredRoomlistModel(RoomlistModel *model, QObject *parent = nullptr);
+ bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
+
+public slots:
+ int roomidToIndex(QString roomid)
+ {
+ return mapFromSource(roomlistmodel->index(roomlistmodel->roomidToIndex(roomid)))
+ .row();
+ }
+
+private:
+ short int calculateImportance(const QModelIndex &idx) const;
+ RoomlistModel *roomlistmodel;
+ bool sortByImportance = true;
};
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 19c3fb30..2625127c 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -318,6 +318,8 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj
, room_id_(room_id)
, manager_(manager)
{
+ lastMessage_.timestamp = 0;
+
connect(
this,
&TimelineModel::redactionFailed,
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 5c1065cb..b3d3b663 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -382,7 +382,7 @@ private:
QString eventIdToShow;
int showEventTimerCounter = 0;
- DescInfo lastMessage_;
+ DescInfo lastMessage_{};
friend struct SendMessageVisitor;
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index b0c13b03..c84e0df8 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -193,9 +193,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
});
qmlRegisterSingletonType<RoomlistModel>(
"im.nheko", 1, 0, "Rooms", [](QQmlEngine *, QJSEngine *) -> QObject * {
- auto ptr = self->rooms;
- QQmlEngine::setObjectOwnership(ptr, QQmlEngine::CppOwnership);
- return ptr;
+ return new FilteredRoomlistModel(self->rooms);
});
qmlRegisterSingletonType<UserSettings>(
"im.nheko", 1, 0, "Settings", [](QQmlEngine *, QJSEngine *) -> QObject * {
|