From b7ee83f6b60f273fb23ff43bc0ccf80ed0a94455 Mon Sep 17 00:00:00 2001 From: James Graham Date: Sun, 14 Apr 2024 16:37:34 +0000 Subject: [PATCH] Add visualisation of the account's third party IDs in the account editor. A category won't be shown if there are no relevant IDs (will add the ability to add new ones later). Part of network/neochat#565 ![image](/uploads/7da00b0b4acf90d145c09969ac2a91e1/image.png) --- src/CMakeLists.txt | 2 ++ src/models/threepidmodel.cpp | 57 ++++++++++++++++++++++++++++++ src/models/threepidmodel.h | 57 ++++++++++++++++++++++++++++++ src/neochatconnection.cpp | 8 +++++ src/neochatconnection.h | 11 ++++++ src/settings/AccountEditorPage.qml | 12 ++++++- src/settings/CMakeLists.txt | 1 + src/settings/ThreePIdCard.qml | 43 ++++++++++++++++++++++ 8 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 src/models/threepidmodel.cpp create mode 100644 src/models/threepidmodel.h create mode 100644 src/settings/ThreePIdCard.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8a5c41dd2..5f5c15927 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -175,6 +175,8 @@ add_library(neochat STATIC models/roomtreeitem.cpp models/roomtreeitem.h foreigntypes.h + models/threepidmodel.cpp + models/threepidmodel.h ) set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES diff --git a/src/models/threepidmodel.cpp b/src/models/threepidmodel.cpp new file mode 100644 index 000000000..df73ee599 --- /dev/null +++ b/src/models/threepidmodel.cpp @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2024 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +#include "threepidmodel.h" + +#include "neochatconnection.h" + +ThreePIdModel::ThreePIdModel(NeoChatConnection *connection) + : QAbstractListModel(connection) +{ + Q_ASSERT(connection); + connect(connection, &NeoChatConnection::stateChanged, this, [this]() { + const auto connection = dynamic_cast(this->parent()); + if (connection != nullptr && connection->isLoggedIn()) { + const auto threePIdJob = connection->callApi(); + connect(threePIdJob, &Quotient::BaseJob::success, this, [this, threePIdJob]() { + beginResetModel(); + m_threePIds = threePIdJob->threepids(); + endResetModel(); + }); + } + }); +} + +QVariant ThreePIdModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return {}; + } + if (index.row() >= rowCount()) { + qDebug() << "ThreePIdModel, something's wrong: index.row() >= m_threePIds.count()"; + return {}; + } + + if (role == AddressRole) { + return m_threePIds.at(index.row()).address; + } + if (role == MediumRole) { + return m_threePIds.at(index.row()).medium; + } + + return {}; +} + +int ThreePIdModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return m_threePIds.count(); +} + +QHash ThreePIdModel::roleNames() const +{ + return { + {AddressRole, QByteArrayLiteral("address")}, + {MediumRole, QByteArrayLiteral("medium")}, + }; +} diff --git a/src/models/threepidmodel.h b/src/models/threepidmodel.h new file mode 100644 index 000000000..68e857de4 --- /dev/null +++ b/src/models/threepidmodel.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: 2024 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +#pragma once + +#include +#include + +#include + +class NeoChatConnection; + +/** + * @class ThreePIdModel + * + * This class defines the model for visualising an account's 3PIDs. + */ +class ThreePIdModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + +public: + /** + * @brief Defines the model roles. + */ + enum EventRoles { + AddressRole = Qt::DisplayRole, /**< The third-party identifier address. */ + MediumRole, /**< The medium of the third-party identifier. One of: [email, msisdn]. */ + }; + + explicit ThreePIdModel(NeoChatConnection *parent); + + /** + * @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 roleNames() const override; + +private: + QVector m_threePIds; +}; diff --git a/src/neochatconnection.cpp b/src/neochatconnection.cpp index 9bf4fab53..b7c243c79 100644 --- a/src/neochatconnection.cpp +++ b/src/neochatconnection.cpp @@ -16,6 +16,7 @@ #include "spacehierarchycache.h" #include +#include #include #include @@ -41,12 +42,14 @@ using namespace Qt::StringLiterals; NeoChatConnection::NeoChatConnection(QObject *parent) : Connection(parent) + , m_threePIdModel(new ThreePIdModel(this)) { connectSignals(); } NeoChatConnection::NeoChatConnection(const QUrl &server, QObject *parent) : Connection(server, parent) + , m_threePIdModel(new ThreePIdModel(this)) { connectSignals(); } @@ -248,6 +251,11 @@ void NeoChatConnection::deactivateAccount(const QString &password) }); } +ThreePIdModel *NeoChatConnection::threePIdModel() const +{ + return m_threePIdModel; +} + void NeoChatConnection::createRoom(const QString &name, const QString &topic, const QString &parent, bool setChildParent) { QList initialStateEvents; diff --git a/src/neochatconnection.h b/src/neochatconnection.h index 056766de0..9eff1bdad 100644 --- a/src/neochatconnection.h +++ b/src/neochatconnection.h @@ -9,6 +9,8 @@ #include #include +#include "models/threepidmodel.h" + class LinkPreviewer; class NeoChatConnection : public Quotient::Connection @@ -29,6 +31,11 @@ class NeoChatConnection : public Quotient::Connection Q_PROPERTY(QString deviceKey READ deviceKey CONSTANT) Q_PROPERTY(QString encryptionKey READ encryptionKey CONSTANT) + /** + * @brief The model with the account's 3PIDs. + */ + Q_PROPERTY(ThreePIdModel *threePIdModel READ threePIdModel CONSTANT) + /** * @brief The total number of notifications for all direct chats. */ @@ -96,6 +103,8 @@ public: Q_INVOKABLE void deactivateAccount(const QString &password); + ThreePIdModel *threePIdModel() const; + /** * @brief Create new room for a group chat. */ @@ -167,6 +176,8 @@ private: bool m_isOnline = true; void setIsOnline(bool isOnline); + ThreePIdModel *m_threePIdModel; + void connectSignals(); int m_badgeNotificationCount = 0; diff --git a/src/settings/AccountEditorPage.qml b/src/settings/AccountEditorPage.qml index 591daa4d7..0bbd3cb7c 100644 --- a/src/settings/AccountEditorPage.qml +++ b/src/settings/AccountEditorPage.qml @@ -11,6 +11,7 @@ import QtQuick.Window import org.kde.kirigami as Kirigami import org.kde.kirigamiaddons.formcard as FormCard import org.kde.kirigamiaddons.components as KirigamiComponents + import org.kde.neochat FormCard.FormCardPage { @@ -195,7 +196,16 @@ FormCard.FormCardPage { } } } - + ThreePIdCard { + connection: root.connection + title: i18n("Email Addresses") + medium: "email" + } + ThreePIdCard { + connection: root.connection + title: i18n("Phone Numbers") + medium: "msisdn" + } FormCard.FormHeader { Layout.fillWidth: true title: i18n("Server Information") diff --git a/src/settings/CMakeLists.txt b/src/settings/CMakeLists.txt index bd4a0c97f..78e2a7de1 100644 --- a/src/settings/CMakeLists.txt +++ b/src/settings/CMakeLists.txt @@ -32,4 +32,5 @@ qt_add_qml_module(settings IgnoredUsersDialog.qml NotificationRuleItem.qml ThemeRadioButton.qml + ThreePIdCard.qml ) diff --git a/src/settings/ThreePIdCard.qml b/src/settings/ThreePIdCard.qml new file mode 100644 index 000000000..89e72aa09 --- /dev/null +++ b/src/settings/ThreePIdCard.qml @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2024 James Graham +// SPDX-License-Identifier: GPL-2.0-or-later + +import QtQuick +import QtQuick.Layouts + +import org.kde.kirigami as Kirigami +import org.kde.kirigamiaddons.formcard as FormCard +import org.kde.kitemmodels + +import org.kde.neochat + +ColumnLayout { + id: root + + required property NeoChatConnection connection + + required property string title + required property string medium + + visible: deviceRepeater.count > 0 + + FormCard.FormHeader { + title: root.title + } + FormCard.FormCard { + id: devicesCard + + Repeater { + id: deviceRepeater + model: KSortFilterProxyModel { + sourceModel: root.connection.threePIdModel + filterRoleName: "medium" + filterString: root.medium + } + + delegate: FormCard.FormTextDelegate { + required property string address + text: address + } + } + } +}