Someone hit a nasty bug while attempting to find a KDE room on the kde.org server, the error wouldn't come up normally and the dialog would be blank. That's because that specific placeholder message doesn't appear until you type something into the search field, which is weird. There is a few other oddities with SearchPage that I squashed, including showing the loading placeholder in more appropiate situations.
343 lines
7.7 KiB
C++
343 lines
7.7 KiB
C++
// SPDX-FileCopyrightText: 2019-2020 Black Hat <bhat@encom.eu.org>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "publicroomlistmodel.h"
|
|
|
|
#include "neochatconnection.h"
|
|
#include "publicroomlist_logging.h"
|
|
|
|
using namespace Quotient;
|
|
|
|
class NeoChatQueryPublicRoomsJob : public QueryPublicRoomsJob
|
|
{
|
|
public:
|
|
explicit NeoChatQueryPublicRoomsJob(const QString &server = {},
|
|
std::optional<int> limit = std::nullopt,
|
|
const QString &since = {},
|
|
const std::optional<Filter> &filter = std::nullopt,
|
|
std::optional<bool> includeAllNetworks = std::nullopt,
|
|
const QString &thirdPartyInstanceId = {})
|
|
: QueryPublicRoomsJob(server, limit, since, filter, includeAllNetworks, thirdPartyInstanceId)
|
|
{
|
|
// TODO Remove once we can use libQuotient's job directly
|
|
// This is to make libQuotient happy about results not having the "chunk" field
|
|
setExpectedKeys({});
|
|
}
|
|
};
|
|
|
|
PublicRoomListModel::PublicRoomListModel(QObject *parent)
|
|
: QAbstractListModel(parent)
|
|
{
|
|
}
|
|
|
|
NeoChatConnection *PublicRoomListModel::connection() const
|
|
{
|
|
return m_connection;
|
|
}
|
|
|
|
void PublicRoomListModel::setConnection(NeoChatConnection *connection)
|
|
{
|
|
if (m_connection == connection) {
|
|
return;
|
|
}
|
|
|
|
beginResetModel();
|
|
|
|
nextBatch = QString();
|
|
attempted = false;
|
|
rooms.clear();
|
|
m_server.clear();
|
|
|
|
if (m_connection) {
|
|
m_connection->disconnect(this);
|
|
}
|
|
|
|
endResetModel();
|
|
|
|
m_connection = connection;
|
|
|
|
if (job) {
|
|
job->abandon();
|
|
job = nullptr;
|
|
Q_EMIT searchingChanged();
|
|
}
|
|
|
|
if (m_connection) {
|
|
next();
|
|
}
|
|
|
|
Q_EMIT connectionChanged();
|
|
Q_EMIT serverChanged();
|
|
}
|
|
|
|
QString PublicRoomListModel::server() const
|
|
{
|
|
return m_server;
|
|
}
|
|
|
|
void PublicRoomListModel::setServer(const QString &value)
|
|
{
|
|
if (m_server == value) {
|
|
return;
|
|
}
|
|
|
|
m_server = value;
|
|
|
|
beginResetModel();
|
|
|
|
nextBatch = QString();
|
|
attempted = false;
|
|
rooms.clear();
|
|
|
|
endResetModel();
|
|
|
|
if (job) {
|
|
job->abandon();
|
|
job = nullptr;
|
|
Q_EMIT searchingChanged();
|
|
}
|
|
|
|
if (m_connection) {
|
|
next();
|
|
}
|
|
|
|
Q_EMIT serverChanged();
|
|
}
|
|
|
|
QString PublicRoomListModel::searchText() const
|
|
{
|
|
return m_searchText;
|
|
}
|
|
|
|
void PublicRoomListModel::setSearchText(const QString &value)
|
|
{
|
|
if (m_searchText == value) {
|
|
return;
|
|
}
|
|
|
|
m_searchText = value;
|
|
Q_EMIT searchTextChanged();
|
|
|
|
nextBatch = QString();
|
|
attempted = false;
|
|
|
|
if (job) {
|
|
job->abandon();
|
|
job = nullptr;
|
|
Q_EMIT searchingChanged();
|
|
}
|
|
}
|
|
|
|
bool PublicRoomListModel::showOnlySpaces() const
|
|
{
|
|
return m_showOnlySpaces;
|
|
}
|
|
|
|
void PublicRoomListModel::setShowOnlySpaces(bool showOnlySpaces)
|
|
{
|
|
if (showOnlySpaces == m_showOnlySpaces) {
|
|
return;
|
|
}
|
|
m_showOnlySpaces = showOnlySpaces;
|
|
Q_EMIT showOnlySpacesChanged();
|
|
|
|
nextBatch = QString();
|
|
attempted = false;
|
|
|
|
if (job) {
|
|
job->abandon();
|
|
job = nullptr;
|
|
Q_EMIT searchingChanged();
|
|
}
|
|
}
|
|
|
|
void PublicRoomListModel::search(int limit)
|
|
{
|
|
if (limit < 1 || attempted) {
|
|
return;
|
|
}
|
|
|
|
if (job) {
|
|
qCDebug(PublicRoomList) << "Other job running, ignore";
|
|
return;
|
|
}
|
|
|
|
next(limit);
|
|
}
|
|
|
|
void PublicRoomListModel::next(int limit)
|
|
{
|
|
if (m_connection == nullptr || limit < 1) {
|
|
return;
|
|
}
|
|
m_redirectedText.clear();
|
|
Q_EMIT redirectedChanged();
|
|
m_errorText.clear();
|
|
Q_EMIT errorTextChanged();
|
|
|
|
if (job) {
|
|
qCDebug(PublicRoomList) << "Other job running, ignore";
|
|
return;
|
|
}
|
|
|
|
QStringList roomTypes;
|
|
if (m_showOnlySpaces) {
|
|
roomTypes += u"m.space"_s;
|
|
}
|
|
job = m_connection->callApi<NeoChatQueryPublicRoomsJob>(m_server, limit, nextBatch, QueryPublicRoomsJob::Filter{m_searchText, roomTypes});
|
|
Q_EMIT searchingChanged();
|
|
|
|
connect(job, &BaseJob::finished, this, [this] {
|
|
if (!attempted) {
|
|
beginResetModel();
|
|
rooms.clear();
|
|
endResetModel();
|
|
|
|
attempted = true;
|
|
}
|
|
|
|
if (job->status() == BaseJob::Success) {
|
|
nextBatch = job->nextBatch();
|
|
|
|
this->beginInsertRows({}, rooms.count(), rooms.count() + job->chunk().count() - 1);
|
|
rooms.append(job->chunk());
|
|
this->endInsertRows();
|
|
} else if (job->error() == BaseJob::ContentAccessError && !m_searchText.isEmpty()) {
|
|
m_redirectedText = job->jsonData()[u"error"_s].toString();
|
|
Q_EMIT redirectedChanged();
|
|
} else {
|
|
m_errorText = job->jsonData()[u"error"_s].toString();
|
|
Q_EMIT errorTextChanged();
|
|
}
|
|
|
|
this->job = nullptr;
|
|
Q_EMIT searchingChanged();
|
|
});
|
|
}
|
|
|
|
QVariant PublicRoomListModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (!index.isValid()) {
|
|
return QVariant();
|
|
}
|
|
|
|
if (index.row() >= rooms.count()) {
|
|
qCDebug(PublicRoomList) << "something's wrong: index.row() >= rooms.count()";
|
|
return {};
|
|
}
|
|
auto room = rooms.at(index.row());
|
|
if (role == DisplayNameRole) {
|
|
auto displayName = room.name;
|
|
if (!displayName.isEmpty()) {
|
|
return displayName;
|
|
}
|
|
|
|
displayName = room.canonicalAlias;
|
|
if (!displayName.isEmpty()) {
|
|
return displayName;
|
|
}
|
|
|
|
if (!displayName.isEmpty()) {
|
|
return displayName;
|
|
}
|
|
|
|
return room.roomId;
|
|
}
|
|
if (role == AvatarUrlRole) {
|
|
auto avatarUrl = room.avatarUrl;
|
|
if (avatarUrl.isEmpty() || !m_connection) {
|
|
return QUrl();
|
|
}
|
|
return m_connection->makeMediaUrl(avatarUrl);
|
|
}
|
|
if (role == TopicRole) {
|
|
return room.topic;
|
|
}
|
|
if (role == RoomIdRole) {
|
|
return room.roomId;
|
|
}
|
|
if (role == AliasRole) {
|
|
if (!room.canonicalAlias.isEmpty()) {
|
|
return room.canonicalAlias;
|
|
}
|
|
return {};
|
|
}
|
|
if (role == MemberCountRole) {
|
|
return room.numJoinedMembers;
|
|
}
|
|
if (role == AllowGuestsRole) {
|
|
return room.guestCanJoin;
|
|
}
|
|
if (role == WorldReadableRole) {
|
|
return room.worldReadable;
|
|
}
|
|
if (role == IsJoinedRole) {
|
|
if (!m_connection) {
|
|
return {};
|
|
}
|
|
|
|
return m_connection->room(room.roomId, JoinState::Join) != nullptr;
|
|
}
|
|
if (role == IsSpaceRole) {
|
|
return room.roomType == u"m.space"_s;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
QHash<int, QByteArray> PublicRoomListModel::roleNames() const
|
|
{
|
|
QHash<int, QByteArray> roles;
|
|
|
|
roles[DisplayNameRole] = "displayName";
|
|
roles[AvatarUrlRole] = "avatarUrl";
|
|
roles[TopicRole] = "topic";
|
|
roles[RoomIdRole] = "roomId";
|
|
roles[MemberCountRole] = "memberCount";
|
|
roles[AllowGuestsRole] = "allowGuests";
|
|
roles[WorldReadableRole] = "worldReadable";
|
|
roles[IsJoinedRole] = "isJoined";
|
|
roles[IsSpaceRole] = "isSpace";
|
|
roles[AliasRole] = "alias";
|
|
|
|
return roles;
|
|
}
|
|
|
|
int PublicRoomListModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
if (parent.isValid()) {
|
|
return 0;
|
|
}
|
|
|
|
return rooms.count();
|
|
}
|
|
|
|
bool PublicRoomListModel::canFetchMore(const QModelIndex &parent) const
|
|
{
|
|
Q_UNUSED(parent)
|
|
return !nextBatch.isEmpty();
|
|
}
|
|
|
|
void PublicRoomListModel::fetchMore(const QModelIndex &parent)
|
|
{
|
|
Q_UNUSED(parent)
|
|
next();
|
|
}
|
|
|
|
bool PublicRoomListModel::searching() const
|
|
{
|
|
return job != nullptr;
|
|
}
|
|
|
|
QString PublicRoomListModel::redirectedText() const
|
|
{
|
|
return m_redirectedText;
|
|
}
|
|
|
|
QString PublicRoomListModel::errorText() const
|
|
{
|
|
return m_errorText;
|
|
}
|
|
|
|
#include "moc_publicroomlistmodel.cpp"
|