Move remaining code to app module

There's still some stuff that could potentially go elsewhere but I think it's enough for now.
This commit is contained in:
James Graham
2025-04-18 09:26:17 +00:00
parent 0708f022bc
commit b6791485c4
124 changed files with 406 additions and 403 deletions

View File

@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "commonroomsmodel.h"
#include "jobs/neochatgetcommonroomsjob.h"
#include <QGuiApplication>
using namespace Quotient;
CommonRoomsModel::CommonRoomsModel(QObject *parent)
: QAbstractListModel(parent)
{
}
NeoChatConnection *CommonRoomsModel::connection() const
{
return m_connection;
}
void CommonRoomsModel::setConnection(NeoChatConnection *connection)
{
m_connection = connection;
Q_EMIT connectionChanged();
reload();
}
QString CommonRoomsModel::userId() const
{
return m_userId;
}
void CommonRoomsModel::setUserId(const QString &userId)
{
m_userId = userId;
Q_EMIT userIdChanged();
reload();
}
QVariant CommonRoomsModel::data(const QModelIndex &index, int roleName) const
{
Q_UNUSED(index)
Q_UNUSED(roleName)
return {};
}
int CommonRoomsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_commonRooms.size();
}
void CommonRoomsModel::reload()
{
if (!m_connection || m_userId.isEmpty()) {
return;
}
if (!m_connection->canCheckMutualRooms()) {
return;
}
// Checking if you have mutual rooms with yourself doesn't make sense and servers reject it too
if (m_connection->userId() == m_userId) {
return;
}
m_connection->callApi<NeochatGetCommonRoomsJob>(m_userId).then([this](const auto job) {
const auto &replyData = job->jsonData();
beginResetModel();
for (const auto &roomId : replyData[u"joined"_s].toArray()) {
m_commonRooms.push_back(roomId.toString());
}
endResetModel();
Q_EMIT countChanged();
});
}
#include "moc_commonroomsmodel.cpp"

View File

@@ -0,0 +1,58 @@
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QAbstractListModel>
#include <QQmlEngine>
#include "neochatconnection.h"
#include "neochatroom.h"
#include <Quotient/events/roommessageevent.h>
#include <Quotient/roommember.h>
/**
* @brief Model to show the common or mutual rooms between you and another user.
*/
class CommonRoomsModel : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(NeoChatConnection *connection WRITE setConnection READ connection NOTIFY connectionChanged REQUIRED)
Q_PROPERTY(QString userId WRITE setUserId READ userId NOTIFY userIdChanged REQUIRED)
Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
public:
enum Roles {
TextRole = Qt::DisplayRole,
LongitudeRole,
LatitudeRole,
AssetRole,
AuthorRole,
};
Q_ENUM(Roles)
explicit CommonRoomsModel(QObject *parent = nullptr);
[[nodiscard]] NeoChatConnection *connection() const;
void setConnection(NeoChatConnection *connection);
[[nodiscard]] QString userId() const;
void setUserId(const QString &userId);
[[nodiscard]] QVariant data(const QModelIndex &index, int roleName) const override;
[[nodiscard]] Q_INVOKABLE int rowCount(const QModelIndex &parent = {}) const override;
Q_SIGNALS:
void connectionChanged();
void userIdChanged();
void countChanged();
private:
void reload();
QPointer<NeoChatConnection> m_connection;
QString m_userId;
QList<QString> m_commonRooms;
};

View File

@@ -0,0 +1,164 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "notificationsmodel.h"
#include <Quotient/events/event.h>
#include <Quotient/uri.h>
#include "eventhandler.h"
#include "neochatroom.h"
using namespace Quotient;
NotificationsModel::NotificationsModel(QObject *parent)
: QAbstractListModel(parent)
{
}
int NotificationsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_notifications.count();
}
QVariant NotificationsModel::data(const QModelIndex &index, int role) const
{
auto row = index.row();
if (row < 0 || row >= m_notifications.count()) {
return {};
}
if (role == TextRole) {
return m_notifications[row].text;
}
if (role == RoomIdRole) {
return m_notifications[row].roomId;
}
if (role == AuthorName) {
return m_notifications[row].authorName;
}
if (role == AuthorAvatar) {
return m_notifications[row].authorAvatar;
}
if (role == RoomRole) {
return QVariant::fromValue(m_connection->room(m_notifications[row].roomId));
}
if (role == EventIdRole) {
return m_notifications[row].eventId;
}
if (role == RoomDisplayNameRole) {
return m_notifications[row].roomDisplayName;
}
if (role == UriRole) {
return Uri(m_notifications[row].roomId.toLatin1(), m_notifications[row].eventId.toLatin1()).toUrl();
}
return {};
}
QHash<int, QByteArray> NotificationsModel::roleNames() const
{
return {
{TextRole, "text"},
{RoomIdRole, "roomId"},
{AuthorName, "authorName"},
{AuthorAvatar, "authorAvatar"},
{RoomRole, "room"},
{EventIdRole, "eventId"},
{RoomDisplayNameRole, "roomDisplayName"},
{UriRole, "uri"},
};
}
NeoChatConnection *NotificationsModel::connection() const
{
return m_connection;
}
void NotificationsModel::setConnection(NeoChatConnection *connection)
{
if (m_connection) {
// disconnect things...
}
if (!connection) {
return;
}
m_connection = connection;
Q_EMIT connectionChanged();
connect(connection, &Connection::syncDone, this, [this]() {
loadData();
});
loadData();
}
void NotificationsModel::loadData()
{
Q_ASSERT(m_connection);
if (m_job || (m_notifications.size() && m_nextToken.isEmpty())) {
return;
}
m_job = m_connection->callApi<GetNotificationsJob>(m_nextToken);
Q_EMIT loadingChanged();
connect(m_job, &BaseJob::finished, this, [this]() {
m_nextToken = m_job->nextToken();
Q_EMIT nextTokenChanged();
for (const auto &notification : m_job->notifications()) {
if (std::any_of(notification.actions.constBegin(), notification.actions.constEnd(), [](const QVariant &it) {
if (it.canConvert<QVariantMap>()) {
auto map = it.toMap();
if (map["set_tweak"_L1] == "highlight"_L1) {
return true;
}
}
return false;
})) {
const auto &authorId = notification.event->fullJson()["sender"_L1].toString();
const auto &room = m_connection->room(notification.roomId);
if (!room) {
continue;
}
auto u = room->member(authorId).avatarUrl();
auto avatar = u.isEmpty() ? QUrl() : connection()->makeMediaUrl(u);
const auto &authorAvatar = avatar.isValid() && avatar.scheme() == u"mxc"_s ? avatar : QUrl();
const auto &roomEvent = eventCast<const RoomEvent>(notification.event.get());
beginInsertRows({}, m_notifications.length(), m_notifications.length());
m_notifications += Notification{
.roomId = notification.roomId,
.text = room->member(authorId).htmlSafeDisplayName() + (roomEvent->is<StateEvent>() ? u" "_s : u": "_s)
+ EventHandler::plainBody(dynamic_cast<NeoChatRoom *>(room), roomEvent, true),
.authorName = room->member(authorId).htmlSafeDisplayName(),
.authorAvatar = authorAvatar,
.eventId = roomEvent->id(),
.roomDisplayName = room->displayName(),
};
endInsertRows();
}
}
m_job = nullptr;
Q_EMIT loadingChanged();
});
}
bool NotificationsModel::canFetchMore(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return !m_nextToken.isEmpty();
}
void NotificationsModel::fetchMore(const QModelIndex &parent)
{
Q_UNUSED(parent);
loadData();
}
bool NotificationsModel::loading() const
{
return m_job;
}
QString NotificationsModel::nextToken() const
{
return m_nextToken;
}
#include "moc_notificationsmodel.cpp"

View File

@@ -0,0 +1,68 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#pragma once
#include "neochatconnection.h"
#include <QAbstractListModel>
#include <QPointer>
#include <QQmlEngine>
#include <QVariant>
#include <Quotient/csapi/notifications.h>
class NotificationsModel : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
Q_PROPERTY(QString nextToken READ nextToken NOTIFY nextTokenChanged)
public:
enum Roles {
TextRole = Qt::DisplayRole,
RoomIdRole,
AuthorName,
AuthorAvatar,
RoomRole,
EventIdRole,
RoomDisplayNameRole,
UriRole,
};
Q_ENUM(Roles);
struct Notification {
QString roomId;
QString text;
QString authorName;
QUrl authorAvatar;
QString eventId;
QString roomDisplayName;
};
NotificationsModel(QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
bool canFetchMore(const QModelIndex &parent) const override;
void fetchMore(const QModelIndex &parent) override;
NeoChatConnection *connection() const;
void setConnection(NeoChatConnection *connection);
bool loading() const;
QString nextToken() const;
Q_SIGNALS:
void connectionChanged();
void loadingChanged();
void nextTokenChanged();
private:
QPointer<NeoChatConnection> m_connection;
void loadData();
QList<Notification> m_notifications;
QString m_nextToken;
QPointer<Quotient::GetNotificationsJob> m_job;
};

View File

@@ -0,0 +1,182 @@
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "serverlistmodel.h"
#include <QDebug>
#include <KConfig>
#include <KConfigGroup>
#include <KSharedConfig>
#include "neochatconnection.h"
using namespace Qt::StringLiterals;
ServerListModel::ServerListModel(QObject *parent)
: QAbstractListModel(parent)
{
}
QVariant ServerListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return {};
}
if (index.row() >= m_servers.count()) {
qDebug() << "ServerListModel, something's wrong: index.row() >= m_notificationRules.count()";
return {};
}
if (role == UrlRole) {
return m_servers.at(index.row()).url;
}
if (role == IsHomeServerRole) {
return m_servers.at(index.row()).isHomeServer;
}
if (role == IsAddServerDelegateRole) {
return m_servers.at(index.row()).isAddServerDelegate;
}
if (role == IsDeletableRole) {
return m_servers.at(index.row()).isDeletable;
}
return {};
}
int ServerListModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_servers.count();
}
void ServerListModel::checkServer(const QString &url)
{
const auto stateConfig = KSharedConfig::openStateConfig();
const KConfigGroup serverGroup = stateConfig->group(u"Servers"_s);
if (!serverGroup.hasKey(url)) {
if (Quotient::isJobPending(m_checkServerJob)) {
m_checkServerJob->abandon();
}
m_checkServerJob = m_connection->callApi<Quotient::QueryPublicRoomsJob>(url, 1);
connect(m_checkServerJob, &Quotient::BaseJob::success, this, [this, url] {
Q_EMIT serverCheckComplete(url, true);
});
}
}
void ServerListModel::addServer(const QString &url)
{
const auto stateConfig = KSharedConfig::openStateConfig();
KConfigGroup serverGroup = stateConfig->group(u"Servers"_s);
if (!serverGroup.hasKey(url)) {
Server newServer = Server{
url,
false,
false,
true,
};
beginInsertRows(QModelIndex(), m_servers.count() - 1, m_servers.count() - 1);
m_servers.insert(rowCount() - 1, newServer);
endInsertRows();
}
serverGroup.writeEntry(url, url);
stateConfig->sync();
}
void ServerListModel::removeServerAtIndex(int row)
{
const auto stateConfig = KSharedConfig::openStateConfig();
KConfigGroup serverGroup = stateConfig->group(u"Servers"_s);
serverGroup.deleteEntry(data(index(row), UrlRole).toString());
beginRemoveRows(QModelIndex(), row, row);
m_servers.removeAt(row);
endRemoveRows();
stateConfig->sync();
}
QHash<int, QByteArray> ServerListModel::roleNames() const
{
return {
{UrlRole, QByteArrayLiteral("url")},
{IsHomeServerRole, QByteArrayLiteral("isHomeServer")},
{IsAddServerDelegateRole, QByteArrayLiteral("isAddServerDelegate")},
{IsDeletableRole, QByteArrayLiteral("isDeletable")},
};
}
NeoChatConnection *ServerListModel::connection() const
{
return m_connection;
}
void ServerListModel::setConnection(NeoChatConnection *connection)
{
if (m_connection == connection) {
return;
}
m_connection = connection;
Q_EMIT connectionChanged();
initialize();
}
void ServerListModel::initialize()
{
if (m_connection == nullptr) {
return;
}
beginResetModel();
const auto stateConfig = KSharedConfig::openStateConfig();
const KConfigGroup serverGroup = stateConfig->group(u"Servers"_s);
QString domain = m_connection->domain();
// Add the user's homeserver
m_servers.append(Server{
domain,
true,
false,
false,
});
// Add matrix.org
m_servers.append(Server{
u"matrix.org"_s,
false,
false,
false,
});
// Add each of the saved custom servers
for (const auto &i : serverGroup.keyList()) {
m_servers.append(Server{
serverGroup.readEntry(i, QString()),
false,
false,
true,
});
}
// Add add server delegate entry
m_servers.append(Server{
QString(),
false,
true,
false,
});
beginResetModel();
}
#include "moc_serverlistmodel.cpp"

View File

@@ -0,0 +1,116 @@
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <Quotient/csapi/list_public_rooms.h>
#include <QAbstractListModel>
#include <QPointer>
#include <QQmlEngine>
#include <QUrl>
class NeoChatConnection;
/**
* @class ServerListModel
*
* This class defines the model for visualising a list of matrix servers.
*
* The list of servers is retrieved from the local cache. Any additions are also
* stored locally so that they are retrieved on subsequent instantiations.
*
* The model also automatically adds the local user's home server and matrix.org to
* the model. Finally the model also adds an entry to create a space in the model
* for an "add new server" delegate.
*/
class ServerListModel : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
public:
/**
* @brief Define the data required to represent a server.
*/
struct Server {
QString url; /**< Server URL. */
bool isHomeServer; /**< Whether the server is the local user's home server. */
bool isAddServerDelegate; /**< Wether the item is the "add new server" delegate. */
bool isDeletable; /**< Whether the item can be deleted from the model. */
};
/**
* @brief Defines the model roles.
*/
enum EventRoles {
UrlRole = Qt::UserRole + 1, /**< Server URL. */
IsHomeServerRole, /**< Whether the server is the local user's home server. */
IsAddServerDelegateRole, /**< Whether the item is the add new server delegate. */
IsDeletableRole, /**< Whether the item can be deleted from the model. */
};
explicit ServerListModel(QObject *parent = nullptr);
/**
* @brief Get the given role value at the given index.
*
* @sa QAbstractItemModel::data
*/
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
/**
* @brief Number of rows in the model.
*
* @sa QAbstractItemModel::rowCount
*/
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
/**
* @brief Returns a mapping from Role enum values to role names.
*
* @sa EventRoles, QAbstractItemModel::roleNames()
*/
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
/**
* @brief Start a check to see if the given URL is a valid matrix server.
*
* This function starts the check but due to the requests being asynchronous
* the caller will need to watch the serverCheckComplete signal for confirmation.
* The server URL should be treated as invalid until the signal is emitted true.
*
* @sa serverCheckComplete()
*/
Q_INVOKABLE void checkServer(const QString &url);
/**
* @brief Add a new server to the model.
*
* The server will also be stored in local cache.
*/
Q_INVOKABLE void addServer(const QString &url);
/**
* @brief Remove the server at the given index.
*
* The server will also be removed from local cache.
*/
Q_INVOKABLE void removeServerAtIndex(int index);
NeoChatConnection *connection() const;
void setConnection(NeoChatConnection *connection);
Q_SIGNALS:
void serverCheckComplete(QString url, bool valid);
void connectionChanged();
private:
QList<Server> m_servers;
QPointer<Quotient::QueryPublicRoomsJob> m_checkServerJob = nullptr;
QPointer<NeoChatConnection> m_connection;
void initialize();
};

View File

@@ -0,0 +1,189 @@
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
// SPDX-License-Identifier: GPL-3.0-only
#include "userdirectorylistmodel.h"
#include <Quotient/room.h>
#include "neochatconnection.h"
using namespace Quotient;
UserDirectoryListModel::UserDirectoryListModel(QObject *parent)
: QAbstractListModel(parent)
{
}
NeoChatConnection *UserDirectoryListModel::connection() const
{
return m_connection;
}
void UserDirectoryListModel::setConnection(NeoChatConnection *connection)
{
if (m_connection == connection) {
return;
}
beginResetModel();
attempted = false;
users.clear();
if (m_connection) {
m_connection->disconnect(this);
}
endResetModel();
m_connection = connection;
Q_EMIT connectionChanged();
if (m_job) {
m_job->abandon();
m_job = nullptr;
Q_EMIT searchingChanged();
}
}
QString UserDirectoryListModel::searchText() const
{
return m_searchText;
}
void UserDirectoryListModel::setSearchText(const QString &value)
{
if (m_searchText == value) {
return;
}
m_searchText = value;
Q_EMIT searchTextChanged();
if (m_job) {
m_job->abandon();
m_job = nullptr;
Q_EMIT searchingChanged();
}
attempted = false;
}
bool UserDirectoryListModel::searching() const
{
return m_job != nullptr;
}
void UserDirectoryListModel::search(int limit)
{
if (limit < 1) {
return;
}
if (m_job) {
qDebug() << "UserDirectoryListModel: Other jobs running, ignore";
return;
}
if (attempted) {
return;
}
m_job = m_connection->callApi<SearchUserDirectoryJob>(m_searchText, limit);
Q_EMIT searchingChanged();
connect(m_job, &BaseJob::finished, this, [this] {
attempted = true;
if (m_job->status() == BaseJob::Success) {
auto users = m_job->results();
this->beginResetModel();
this->users = users;
this->endResetModel();
}
this->m_job = nullptr;
Q_EMIT searchingChanged();
});
}
QVariant UserDirectoryListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (index.row() >= users.count()) {
qDebug() << "UserDirectoryListModel, something's wrong: index.row() >= "
"users.count()";
return {};
}
auto user = users.at(index.row());
if (role == DisplayNameRole) {
auto displayName = user.displayName;
if (!displayName.isEmpty()) {
return displayName;
}
displayName = user.userId;
if (!displayName.isEmpty()) {
return displayName;
}
return u"Unknown User"_s;
}
if (role == AvatarRole) {
auto avatarUrl = user.avatarUrl;
if (avatarUrl.isEmpty() || !m_connection) {
return QUrl();
}
return m_connection->makeMediaUrl(avatarUrl);
}
if (role == UserIDRole) {
return user.userId;
}
if (role == DirectChatExistsRole) {
if (!m_connection) {
return false;
};
auto userObj = m_connection->user(user.userId);
auto directChats = m_connection->directChats();
if (userObj && directChats.contains(userObj)) {
auto directChatsForUser = directChats.values(userObj);
if (!directChatsForUser.isEmpty()) {
return true;
}
}
return false;
}
return {};
}
QHash<int, QByteArray> UserDirectoryListModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[DisplayNameRole] = "displayName";
roles[AvatarRole] = "avatarUrl";
roles[UserIDRole] = "userId";
roles[DirectChatExistsRole] = "directChatExists";
return roles;
}
int UserDirectoryListModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
return users.count();
}
#include "moc_userdirectorylistmodel.cpp"

View File

@@ -0,0 +1,106 @@
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include <QAbstractListModel>
#include <QObject>
#include <QQmlEngine>
#include <Quotient/csapi/users.h>
class NeoChatConnection;
/**
* @class UserDirectoryListModel
*
* This class defines the model for visualising the results of a user search.
*
* The model searches for users that have the given keyword in the matrix
* ID or the display name. See
* https://spec.matrix.org/v1.6/client-server-api/#post_matrixclientv3user_directorysearch
* for more info on matrix user searches.
*/
class UserDirectoryListModel : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
/**
* @brief The current connection that the model is getting users from.
*/
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
/**
* @brief The text to search the public room list for.
*/
Q_PROPERTY(QString searchText READ searchText WRITE setSearchText NOTIFY searchTextChanged)
/**
* @brief Whether the model is searching.
*/
Q_PROPERTY(bool searching READ searching NOTIFY searchingChanged)
public:
/**
* @brief Defines the model roles.
*/
enum EventRoles {
DisplayNameRole = Qt::DisplayRole, /**< The user's display name. */
AvatarRole, /**< The source URL for the user's avatar. */
UserIDRole, /**< Matrix ID of the user. */
DirectChatExistsRole, /**< Whether there is already a direct chat with the user. */
};
explicit UserDirectoryListModel(QObject *parent = nullptr);
[[nodiscard]] NeoChatConnection *connection() const;
void setConnection(NeoChatConnection *connection);
[[nodiscard]] QString searchText() const;
void setSearchText(const QString &searchText);
[[nodiscard]] bool searching() const;
/**
* @brief Get the given role value at the given index.
*
* @sa QAbstractItemModel::data
*/
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
/**
* @brief Number of rows in the model.
*
* @sa QAbstractItemModel::rowCount
*/
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
/**
* @brief Returns a mapping from Role enum values to role names.
*
* @sa EventRoles, QAbstractItemModel::roleNames()
*/
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
/**
* @brief Search the user directory.
*
* @param limit the maximum number of rooms to load.
*/
Q_INVOKABLE void search(int limit = 50);
Q_SIGNALS:
void connectionChanged();
void searchTextChanged();
void searchingChanged();
private:
QPointer<NeoChatConnection> m_connection;
QString m_searchText;
bool attempted = false;
QList<Quotient::SearchUserDirectoryJob::User> users;
Quotient::SearchUserDirectoryJob *m_job = nullptr;
};

View File

@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "userfiltermodel.h"
#include "models/userlistmodel.h"
bool UserFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
Q_UNUSED(sourceParent);
if (!m_allowEmpty && m_filterText.length() < 1) {
return false;
}
return sourceModel()->data(sourceModel()->index(sourceRow, 0), UserListModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|| sourceModel()->data(sourceModel()->index(sourceRow, 0), UserListModel::UserIdRole).toString().contains(m_filterText, Qt::CaseInsensitive);
}
QString UserFilterModel::filterText() const
{
return m_filterText;
}
void UserFilterModel::setFilterText(const QString &filterText)
{
m_filterText = filterText;
Q_EMIT filterTextChanged();
invalidateFilter();
}
bool UserFilterModel::allowEmpty() const
{
return m_allowEmpty;
}
void UserFilterModel::setAllowEmpty(bool allowEmpty)
{
m_allowEmpty = allowEmpty;
Q_EMIT allowEmptyChanged();
}
#include "moc_userfiltermodel.cpp"

View File

@@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QQmlEngine>
#include <QSortFilterProxyModel>
/**
* @class UserFilterModel
*
* This class creates a custom QSortFilterProxyModel for filtering a users by either
* display name or matrix ID. The filter can accept a full matrix id i.e. example:kde.org
* to separate between accounts on different servers with similar names.
*/
class UserFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
QML_ELEMENT
/**
* @brief This property hold the text of the filter.
*
* The text is either a desired display name or matrix id.
*/
Q_PROPERTY(QString filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged)
Q_PROPERTY(bool allowEmpty READ allowEmpty WRITE setAllowEmpty NOTIFY allowEmptyChanged)
public:
/**
* @brief Custom filter function checking boith the display name and matrix ID.
*
* @note The filter cannot be modified and will always use the same filter properties.
*/
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
QString filterText() const;
void setFilterText(const QString &filterText);
bool allowEmpty() const;
void setAllowEmpty(bool allowEmpty);
Q_SIGNALS:
void filterTextChanged();
void allowEmptyChanged();
private:
QString m_filterText;
bool m_allowEmpty = false;
};