From 9540d704e090d129835ae4f96e0145cb6de07484 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sun, 4 Jul 2021 23:06:50 +0200 Subject: [PATCH] Show previews for space rooms --- src/timeline/RoomlistModel.cpp | 233 +++++++++++++++++++++++++++++---- src/timeline/RoomlistModel.h | 5 + 2 files changed, 213 insertions(+), 25 deletions(-) diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 6a90ab6e..87940948 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -33,6 +33,27 @@ RoomlistModel::RoomlistModel(TimelineViewManager *parent) &RoomlistModel::totalUnreadMessageCountUpdated, ChatPage::instance(), &ChatPage::unreadMessages); + + connect( + this, + &RoomlistModel::fetchedPreview, + this, + [this](QString roomid, RoomInfo info) { + if (this->previewedRooms.contains(roomid)) { + this->previewedRooms.insert(roomid, std::move(info)); + auto idx = this->roomidToIndex(roomid); + emit dataChanged(index(idx), + index(idx), + { + Roles::RoomName, + Roles::AvatarUrl, + Roles::IsSpace, + Roles::IsPreviewFetched, + Qt::DisplayRole, + }); + } + }, + Qt::QueuedConnection); } QHash @@ -61,6 +82,16 @@ RoomlistModel::data(const QModelIndex &index, int role) const if (index.row() >= 0 && static_cast(index.row()) < roomids.size()) { auto roomid = roomids.at(index.row()); + if (role == Roles::ParentSpaces) { + auto parents = cache::client()->getParentRoomIds(roomid.toStdString()); + QStringList list; + for (const auto &t : parents) + list.push_back(QString::fromStdString(t)); + return list; + } else if (role == Roles::RoomId) { + return roomid; + } + if (models.contains(roomid)) { auto room = models.value(roomid); switch (role) { @@ -68,8 +99,6 @@ RoomlistModel::data(const QModelIndex &index, int role) const return room->roomAvatarUrl(); case Roles::RoomName: return room->plainRoomName(); - case Roles::RoomId: - return room->roomId(); case Roles::LastMessage: return room->lastMessage().body; case Roles::Time: @@ -88,6 +117,8 @@ RoomlistModel::data(const QModelIndex &index, int role) const return false; case Roles::IsSpace: return room->isSpace(); + case Roles::IsPreview: + return false; case Roles::Tags: { auto info = cache::singleRoomInfo(roomid.toStdString()); QStringList list; @@ -95,14 +126,6 @@ RoomlistModel::data(const QModelIndex &index, int role) const list.push_back(QString::fromStdString(t)); return list; } - case Roles::ParentSpaces: { - auto parents = - cache::client()->getParentRoomIds(roomid.toStdString()); - QStringList list; - for (const auto &t : parents) - list.push_back(QString::fromStdString(t)); - return list; - } default: return {}; } @@ -113,8 +136,6 @@ RoomlistModel::data(const QModelIndex &index, int role) const return QString::fromStdString(room.avatar_url); case Roles::RoomName: return QString::fromStdString(room.name); - case Roles::RoomId: - return roomid; case Roles::LastMessage: return QString(); case Roles::Time: @@ -130,21 +151,77 @@ RoomlistModel::data(const QModelIndex &index, int role) const return true; case Roles::IsSpace: return false; + case Roles::IsPreview: + return false; case Roles::Tags: return QStringList(); - case Roles::ParentSpaces: { - auto parents = - cache::client()->getParentRoomIds(roomid.toStdString()); - QStringList list; - for (const auto &t : parents) - list.push_back(QString::fromStdString(t)); - return list; + default: + return {}; } + } else if (previewedRooms.contains(roomid) && + previewedRooms.value(roomid).has_value()) { + auto room = previewedRooms.value(roomid).value(); + switch (role) { + case Roles::AvatarUrl: + return QString::fromStdString(room.avatar_url); + case Roles::RoomName: + return QString::fromStdString(room.name); + case Roles::LastMessage: + return tr("Previewing this room"); + case Roles::Time: + return QString(); + case Roles::Timestamp: + return QVariant(static_cast(0)); + case Roles::HasUnreadMessages: + case Roles::HasLoudNotification: + return false; + case Roles::NotificationCount: + return 0; + case Roles::IsInvite: + return false; + case Roles::IsSpace: + return room.is_space; + case Roles::IsPreview: + return true; + case Roles::IsPreviewFetched: + return true; + case Roles::Tags: + return QStringList(); default: return {}; } } else { - return {}; + if (role == Roles::IsPreview) + return true; + else if (role == Roles::IsPreviewFetched) + return false; + + fetchPreview(roomid); + switch (role) { + case Roles::AvatarUrl: + return QString(); + case Roles::RoomName: + return tr("No preview available"); + case Roles::LastMessage: + return QString(); + case Roles::Time: + return QString(); + case Roles::Timestamp: + return QVariant(static_cast(0)); + case Roles::HasUnreadMessages: + case Roles::HasLoudNotification: + return false; + case Roles::NotificationCount: + return 0; + case Roles::IsInvite: + return false; + case Roles::IsSpace: + return false; + case Roles::Tags: + return QStringList(); + default: + return {}; + } } } else { return {}; @@ -248,25 +325,111 @@ RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification) newRoom->updateLastMessage(); - bool wasInvite = invites.contains(room_id); - if (!suppressInsertNotification && !wasInvite) - beginInsertRows(QModelIndex(), (int)roomids.size(), (int)roomids.size()); + std::vector previewsToAdd; + if (newRoom->isSpace()) { + auto childs = cache::client()->getChildRoomIds(room_id.toStdString()); + for (const auto &c : childs) { + auto id = QString::fromStdString(c); + if (!(models.contains(id) || invites.contains(id) || + previewedRooms.contains(id))) { + previewsToAdd.push_back(std::move(id)); + } + } + } + + bool wasInvite = invites.contains(room_id); + bool wasPreview = previewedRooms.contains(room_id); + if (!suppressInsertNotification && + ((!wasInvite && !wasPreview) || !previewedRooms.empty())) + // if the old room was already in the list, don't add it. Also add all + // previews at the same time. + beginInsertRows(QModelIndex(), + (int)roomids.size(), + (int)(roomids.size() + previewsToAdd.size() - + ((wasInvite || wasPreview) ? 0 : 1))); models.insert(room_id, std::move(newRoom)); - if (wasInvite) { auto idx = roomidToIndex(room_id); invites.remove(room_id); emit dataChanged(index(idx), index(idx)); + } else if (wasPreview) { + auto idx = roomidToIndex(room_id); + previewedRooms.remove(room_id); + emit dataChanged(index(idx), index(idx)); } else { roomids.push_back(room_id); } + for (auto p : previewsToAdd) { + previewedRooms.insert(p, std::nullopt); + roomids.push_back(std::move(p)); + } + if (!suppressInsertNotification && !wasInvite) endInsertRows(); } } +void +RoomlistModel::fetchPreview(QString roomid_) const +{ + std::string roomid = roomid_.toStdString(); + http::client()->get_state_event( + roomid, + "", + [this, roomid](const mtx::events::state::Create &c, mtx::http::RequestErr err) { + bool is_space = false; + if (!err) { + is_space = c.type == mtx::events::state::room_type::space; + } + + http::client()->get_state_event( + roomid, + "", + [this, roomid, is_space](const mtx::events::state::Avatar &a, + mtx::http::RequestErr) { + auto avatar_url = a.url; + + http::client()->get_state_event( + roomid, + "", + [this, roomid, avatar_url, is_space]( + const mtx::events::state::Topic &t, mtx::http::RequestErr) { + auto topic = t.topic; + http::client()->get_state_event( + roomid, + "", + [this, roomid, topic, avatar_url, is_space]( + const mtx::events::state::Name &n, + mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn( + "Failed to fetch name event to " + "create preview for {}", + roomid); + } + + // don't even add a preview, if we got not a single + // response + if (n.name.empty() && avatar_url.empty() && + topic.empty()) + return; + + RoomInfo info{}; + info.name = n.name; + info.is_space = is_space; + info.avatar_url = avatar_url; + info.topic = topic; + + const_cast(this)->fetchedPreview( + QString::fromStdString(roomid), info); + }); + }); + }); + }); +} + void RoomlistModel::sync(const mtx::responses::Rooms &rooms) { @@ -426,7 +589,9 @@ RoomlistModel::setCurrentRoom(QString roomid) namespace { enum NotificationImportance : short { - ImportanceDisabled = -1, + ImportanceDisabled = -3, + NoPreview = -2, + Preview = -1, AllEventsRead = 0, NewMessage = 1, NewMentions = 2, @@ -448,6 +613,11 @@ FilteredRoomlistModel::calculateImportance(const QModelIndex &idx) const return CurrentSpace; else return SubSpace; + } else if (sourceModel()->data(idx, RoomlistModel::IsPreview).toBool()) { + if (sourceModel()->data(idx, RoomlistModel::IsPreviewFetched).toBool()) + return Preview; + else + return NoPreview; } else if (sourceModel()->data(idx, RoomlistModel::IsInvite).toBool()) { return Invite; } else if (!this->sortByImportance) { @@ -460,6 +630,7 @@ FilteredRoomlistModel::calculateImportance(const QModelIndex &idx) const return AllEventsRead; } } + bool FilteredRoomlistModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { @@ -531,6 +702,12 @@ bool FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) const { if (filterType == FilterBy::Nothing) { + if (sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsPreview) + .toBool()) { + return false; + } + if (sourceModel() ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) .toBool()) { @@ -560,6 +737,12 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons return true; } else if (filterType == FilterBy::Tag) { + if (sourceModel() + ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsPreview) + .toBool()) { + return false; + } + if (sourceModel() ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) .toBool()) { diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h index d6cbb462..2005c35e 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h @@ -37,6 +37,8 @@ public: NotificationCount, IsInvite, IsSpace, + IsPreview, + IsPreviewFetched, Tags, ParentSpaces, }; @@ -87,15 +89,18 @@ private slots: signals: void totalUnreadMessageCountUpdated(int unreadMessages); void currentRoomChanged(); + void fetchedPreview(QString roomid, RoomInfo info); private: void addRoom(const QString &room_id, bool suppressInsertNotification = false); + void fetchPreview(QString roomid) const; TimelineViewManager *manager = nullptr; std::vector roomids; QHash invites; QHash> models; std::map roomReadStatus; + QHash> previewedRooms; QSharedPointer currentRoom_;