Show previews for space rooms

This commit is contained in:
Nicolas Werner 2021-07-04 23:06:50 +02:00
parent ecd57df6e2
commit 9540d704e0
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
2 changed files with 213 additions and 25 deletions

View file

@ -33,6 +33,27 @@ RoomlistModel::RoomlistModel(TimelineViewManager *parent)
&RoomlistModel::totalUnreadMessageCountUpdated, &RoomlistModel::totalUnreadMessageCountUpdated,
ChatPage::instance(), ChatPage::instance(),
&ChatPage::unreadMessages); &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<int, QByteArray> QHash<int, QByteArray>
@ -61,6 +82,16 @@ RoomlistModel::data(const QModelIndex &index, int role) const
if (index.row() >= 0 && static_cast<size_t>(index.row()) < roomids.size()) { if (index.row() >= 0 && static_cast<size_t>(index.row()) < roomids.size()) {
auto roomid = roomids.at(index.row()); 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)) { if (models.contains(roomid)) {
auto room = models.value(roomid); auto room = models.value(roomid);
switch (role) { switch (role) {
@ -68,8 +99,6 @@ RoomlistModel::data(const QModelIndex &index, int role) const
return room->roomAvatarUrl(); return room->roomAvatarUrl();
case Roles::RoomName: case Roles::RoomName:
return room->plainRoomName(); return room->plainRoomName();
case Roles::RoomId:
return room->roomId();
case Roles::LastMessage: case Roles::LastMessage:
return room->lastMessage().body; return room->lastMessage().body;
case Roles::Time: case Roles::Time:
@ -88,6 +117,8 @@ RoomlistModel::data(const QModelIndex &index, int role) const
return false; return false;
case Roles::IsSpace: case Roles::IsSpace:
return room->isSpace(); return room->isSpace();
case Roles::IsPreview:
return false;
case Roles::Tags: { case Roles::Tags: {
auto info = cache::singleRoomInfo(roomid.toStdString()); auto info = cache::singleRoomInfo(roomid.toStdString());
QStringList list; QStringList list;
@ -95,14 +126,6 @@ RoomlistModel::data(const QModelIndex &index, int role) const
list.push_back(QString::fromStdString(t)); list.push_back(QString::fromStdString(t));
return list; 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: default:
return {}; return {};
} }
@ -113,8 +136,6 @@ RoomlistModel::data(const QModelIndex &index, int role) const
return QString::fromStdString(room.avatar_url); return QString::fromStdString(room.avatar_url);
case Roles::RoomName: case Roles::RoomName:
return QString::fromStdString(room.name); return QString::fromStdString(room.name);
case Roles::RoomId:
return roomid;
case Roles::LastMessage: case Roles::LastMessage:
return QString(); return QString();
case Roles::Time: case Roles::Time:
@ -130,21 +151,77 @@ RoomlistModel::data(const QModelIndex &index, int role) const
return true; return true;
case Roles::IsSpace: case Roles::IsSpace:
return false; return false;
case Roles::IsPreview:
return false;
case Roles::Tags: case Roles::Tags:
return QStringList(); return QStringList();
case Roles::ParentSpaces: { default:
auto parents = return {};
cache::client()->getParentRoomIds(roomid.toStdString());
QStringList list;
for (const auto &t : parents)
list.push_back(QString::fromStdString(t));
return list;
} }
} 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<quint64>(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: default:
return {}; return {};
} }
} else { } 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<quint64>(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 { } else {
return {}; return {};
@ -248,25 +325,111 @@ RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification)
newRoom->updateLastMessage(); newRoom->updateLastMessage();
bool wasInvite = invites.contains(room_id); std::vector<QString> previewsToAdd;
if (!suppressInsertNotification && !wasInvite) if (newRoom->isSpace()) {
beginInsertRows(QModelIndex(), (int)roomids.size(), (int)roomids.size()); 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)); models.insert(room_id, std::move(newRoom));
if (wasInvite) { if (wasInvite) {
auto idx = roomidToIndex(room_id); auto idx = roomidToIndex(room_id);
invites.remove(room_id); invites.remove(room_id);
emit dataChanged(index(idx), index(idx)); 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 { } else {
roomids.push_back(room_id); roomids.push_back(room_id);
} }
for (auto p : previewsToAdd) {
previewedRooms.insert(p, std::nullopt);
roomids.push_back(std::move(p));
}
if (!suppressInsertNotification && !wasInvite) if (!suppressInsertNotification && !wasInvite)
endInsertRows(); endInsertRows();
} }
} }
void
RoomlistModel::fetchPreview(QString roomid_) const
{
std::string roomid = roomid_.toStdString();
http::client()->get_state_event<mtx::events::state::Create>(
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<mtx::events::state::Avatar>(
roomid,
"",
[this, roomid, is_space](const mtx::events::state::Avatar &a,
mtx::http::RequestErr) {
auto avatar_url = a.url;
http::client()->get_state_event<mtx::events::state::Topic>(
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<mtx::events::state::Name>(
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<RoomlistModel *>(this)->fetchedPreview(
QString::fromStdString(roomid), info);
});
});
});
});
}
void void
RoomlistModel::sync(const mtx::responses::Rooms &rooms) RoomlistModel::sync(const mtx::responses::Rooms &rooms)
{ {
@ -426,7 +589,9 @@ RoomlistModel::setCurrentRoom(QString roomid)
namespace { namespace {
enum NotificationImportance : short enum NotificationImportance : short
{ {
ImportanceDisabled = -1, ImportanceDisabled = -3,
NoPreview = -2,
Preview = -1,
AllEventsRead = 0, AllEventsRead = 0,
NewMessage = 1, NewMessage = 1,
NewMentions = 2, NewMentions = 2,
@ -448,6 +613,11 @@ FilteredRoomlistModel::calculateImportance(const QModelIndex &idx) const
return CurrentSpace; return CurrentSpace;
else else
return SubSpace; 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()) { } else if (sourceModel()->data(idx, RoomlistModel::IsInvite).toBool()) {
return Invite; return Invite;
} else if (!this->sortByImportance) { } else if (!this->sortByImportance) {
@ -460,6 +630,7 @@ FilteredRoomlistModel::calculateImportance(const QModelIndex &idx) const
return AllEventsRead; return AllEventsRead;
} }
} }
bool bool
FilteredRoomlistModel::lessThan(const QModelIndex &left, const QModelIndex &right) const FilteredRoomlistModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{ {
@ -531,6 +702,12 @@ bool
FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) const FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) const
{ {
if (filterType == FilterBy::Nothing) { if (filterType == FilterBy::Nothing) {
if (sourceModel()
->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsPreview)
.toBool()) {
return false;
}
if (sourceModel() if (sourceModel()
->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace)
.toBool()) { .toBool()) {
@ -560,6 +737,12 @@ FilteredRoomlistModel::filterAcceptsRow(int sourceRow, const QModelIndex &) cons
return true; return true;
} else if (filterType == FilterBy::Tag) { } else if (filterType == FilterBy::Tag) {
if (sourceModel()
->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsPreview)
.toBool()) {
return false;
}
if (sourceModel() if (sourceModel()
->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace) ->data(sourceModel()->index(sourceRow, 0), RoomlistModel::IsSpace)
.toBool()) { .toBool()) {

View file

@ -37,6 +37,8 @@ public:
NotificationCount, NotificationCount,
IsInvite, IsInvite,
IsSpace, IsSpace,
IsPreview,
IsPreviewFetched,
Tags, Tags,
ParentSpaces, ParentSpaces,
}; };
@ -87,15 +89,18 @@ private slots:
signals: signals:
void totalUnreadMessageCountUpdated(int unreadMessages); void totalUnreadMessageCountUpdated(int unreadMessages);
void currentRoomChanged(); void currentRoomChanged();
void fetchedPreview(QString roomid, RoomInfo info);
private: private:
void addRoom(const QString &room_id, bool suppressInsertNotification = false); void addRoom(const QString &room_id, bool suppressInsertNotification = false);
void fetchPreview(QString roomid) const;
TimelineViewManager *manager = nullptr; TimelineViewManager *manager = nullptr;
std::vector<QString> roomids; std::vector<QString> roomids;
QHash<QString, RoomInfo> invites; QHash<QString, RoomInfo> invites;
QHash<QString, QSharedPointer<TimelineModel>> models; QHash<QString, QSharedPointer<TimelineModel>> models;
std::map<QString, bool> roomReadStatus; std::map<QString, bool> roomReadStatus;
QHash<QString, std::optional<RoomInfo>> previewedRooms;
QSharedPointer<TimelineModel> currentRoom_; QSharedPointer<TimelineModel> currentRoom_;