Improve DevicesPage and DevicesModel
- Split the list into sections for "this devices", "verified devices", "unverified devices", and "devices without encryption support" - Sort the lists by last activity
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <csapi/device_management.h>
|
||||
|
||||
#include "controller.h"
|
||||
#include <KLocalizedString>
|
||||
#include <connection.h>
|
||||
#include <user.h>
|
||||
|
||||
@@ -14,12 +15,6 @@ using namespace Quotient;
|
||||
DevicesModel::DevicesModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
connect(&Controller::instance(), &Controller::activeConnectionChanged, this, [this]() {
|
||||
DevicesModel::fetchDevices();
|
||||
Q_EMIT connectionChanged();
|
||||
});
|
||||
|
||||
fetchDevices();
|
||||
}
|
||||
|
||||
void DevicesModel::fetchDevices()
|
||||
@@ -30,6 +25,7 @@ void DevicesModel::fetchDevices()
|
||||
beginResetModel();
|
||||
m_devices = job->devices();
|
||||
endResetModel();
|
||||
Q_EMIT countChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -40,16 +36,33 @@ QVariant DevicesModel::data(const QModelIndex &index, int role) const
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto &device = m_devices[index.row()];
|
||||
|
||||
switch (role) {
|
||||
case Id:
|
||||
return m_devices[index.row()].deviceId;
|
||||
return device.deviceId;
|
||||
case DisplayName:
|
||||
return m_devices[index.row()].displayName;
|
||||
return device.displayName;
|
||||
case LastIp:
|
||||
return m_devices[index.row()].lastSeenIp;
|
||||
return device.lastSeenIp;
|
||||
case LastTimestamp:
|
||||
if (m_devices[index.row()].lastSeenTs)
|
||||
return *m_devices[index.row()].lastSeenTs;
|
||||
if (device.lastSeenTs) {
|
||||
return *device.lastSeenTs;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
case Type:
|
||||
if (device.deviceId == m_connection->deviceId()) {
|
||||
return This;
|
||||
}
|
||||
if (!m_connection->isKnownE2eeCapableDevice(m_connection->userId(), device.deviceId)) {
|
||||
return Unencrypted;
|
||||
}
|
||||
if (m_connection->isVerifiedDevice(m_connection->userId(), device.deviceId)) {
|
||||
return Verified;
|
||||
} else {
|
||||
return Unverified;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -62,11 +75,21 @@ int DevicesModel::rowCount(const QModelIndex &parent) const
|
||||
|
||||
QHash<int, QByteArray> DevicesModel::roleNames() const
|
||||
{
|
||||
return {{Id, "id"}, {DisplayName, "displayName"}, {LastIp, "lastIp"}, {LastTimestamp, "lastTimestamp"}};
|
||||
return {
|
||||
{Id, "id"},
|
||||
{DisplayName, "displayName"},
|
||||
{LastIp, "lastIp"},
|
||||
{LastTimestamp, "lastTimestamp"},
|
||||
{Type, "type"},
|
||||
};
|
||||
}
|
||||
|
||||
void DevicesModel::logout(int index, const QString &password)
|
||||
void DevicesModel::logout(const QString &deviceId, const QString &password)
|
||||
{
|
||||
int index;
|
||||
for (index = 0; m_devices[index].deviceId != deviceId; index++)
|
||||
;
|
||||
|
||||
auto job = Controller::instance().activeConnection()->callApi<NeochatDeleteDeviceJob>(m_devices[index].deviceId);
|
||||
|
||||
connect(job, &BaseJob::result, this, [this, job, password, index] {
|
||||
@@ -74,6 +97,7 @@ void DevicesModel::logout(int index, const QString &password)
|
||||
beginRemoveRows(QModelIndex(), index, index);
|
||||
m_devices.remove(index);
|
||||
endRemoveRows();
|
||||
Q_EMIT countChanged();
|
||||
};
|
||||
if (job->error() != BaseJob::Success) {
|
||||
QJsonObject replyData = job->jsonData();
|
||||
@@ -91,8 +115,11 @@ void DevicesModel::logout(int index, const QString &password)
|
||||
});
|
||||
}
|
||||
|
||||
void DevicesModel::setName(int index, const QString &name)
|
||||
void DevicesModel::setName(const QString &deviceId, const QString &name)
|
||||
{
|
||||
int index;
|
||||
for (index = 0; m_devices[index].deviceId != deviceId; index++);
|
||||
|
||||
auto job = Controller::instance().activeConnection()->callApi<UpdateDeviceJob>(m_devices[index].deviceId, name);
|
||||
QString oldName = m_devices[index].displayName;
|
||||
beginResetModel();
|
||||
@@ -107,7 +134,27 @@ void DevicesModel::setName(int index, const QString &name)
|
||||
|
||||
Connection *DevicesModel::connection() const
|
||||
{
|
||||
return Controller::instance().activeConnection();
|
||||
return m_connection;
|
||||
}
|
||||
|
||||
void DevicesModel::setConnection(Connection *connection)
|
||||
{
|
||||
if (m_connection) {
|
||||
disconnect(m_connection, nullptr, this, nullptr);
|
||||
}
|
||||
m_connection = connection;
|
||||
Q_EMIT connectionChanged();
|
||||
fetchDevices();
|
||||
|
||||
connect(m_connection, &Connection::sessionVerified, this, [this](const QString &userId, const QString &deviceId) {
|
||||
Q_UNUSED(deviceId);
|
||||
if (userId == Controller::instance().activeConnection()->userId()) {
|
||||
fetchDevices();
|
||||
}
|
||||
});
|
||||
connect(m_connection, &Connection::finishedQueryingKeys, this, [this]() {
|
||||
fetchDevices();
|
||||
});
|
||||
}
|
||||
|
||||
#include "moc_devicesmodel.cpp"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include <QPointer>
|
||||
#include <csapi/definitions/client_device.h>
|
||||
|
||||
namespace Quotient
|
||||
@@ -28,7 +29,7 @@ class DevicesModel : public QAbstractListModel
|
||||
/**
|
||||
* @brief The current connection that the model is getting its devices from.
|
||||
*/
|
||||
Q_PROPERTY(Quotient::Connection *connection READ connection NOTIFY connectionChanged)
|
||||
Q_PROPERTY(Quotient::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged REQUIRED)
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -39,10 +40,17 @@ public:
|
||||
DisplayName, /**< Display name set by the user for this device. */
|
||||
LastIp, /**< The IP address where this device was last seen. */
|
||||
LastTimestamp, /**< The timestamp when this devices was last seen. */
|
||||
Type, /**< The category to sort this device into. */
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
DevicesModel(QObject *parent = nullptr);
|
||||
enum DeviceType {
|
||||
This,
|
||||
Verified,
|
||||
Unverified,
|
||||
Unencrypted,
|
||||
};
|
||||
Q_ENUM(DeviceType);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
@@ -66,21 +74,27 @@ public:
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
/**
|
||||
* @brief Logout the device at the given index.
|
||||
* @brief Logout the device with the given id.
|
||||
*/
|
||||
Q_INVOKABLE void logout(int index, const QString &password);
|
||||
Q_INVOKABLE void logout(const QString &deviceId, const QString &password);
|
||||
|
||||
/**
|
||||
* @brief Set the display name of the device at the given index.
|
||||
* @brief Set the display name of the device with the given id.
|
||||
*/
|
||||
Q_INVOKABLE void setName(int index, const QString &name);
|
||||
Q_INVOKABLE void setName(const QString &deviceId, const QString &name);
|
||||
|
||||
Quotient::Connection *connection() const;
|
||||
explicit DevicesModel(QObject *parent = nullptr);
|
||||
|
||||
|
||||
[[nodiscard]] Quotient::Connection *connection() const;
|
||||
void setConnection(Quotient::Connection *connection);
|
||||
|
||||
Q_SIGNALS:
|
||||
void connectionChanged();
|
||||
void countChanged();
|
||||
|
||||
private:
|
||||
void fetchDevices();
|
||||
QVector<Quotient::Device> m_devices;
|
||||
QPointer<Quotient::Connection> m_connection;
|
||||
};
|
||||
|
||||
28
src/models/devicesproxymodel.cpp
Normal file
28
src/models/devicesproxymodel.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "devicesproxymodel.h"
|
||||
#include "devicesmodel.h"
|
||||
|
||||
int DevicesProxyModel::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
void DevicesProxyModel::setType(int type)
|
||||
{
|
||||
m_type = type;
|
||||
Q_EMIT typeChanged();
|
||||
}
|
||||
|
||||
bool DevicesProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
Q_UNUSED(source_parent)
|
||||
return sourceModel()->data(sourceModel()->index(source_row, 0), DevicesModel::Type).toInt() == m_type;
|
||||
}
|
||||
DevicesProxyModel::DevicesProxyModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent)
|
||||
, m_type(0)
|
||||
{
|
||||
setSortRole(DevicesModel::LastTimestamp);
|
||||
sort(0, Qt::DescendingOrder);
|
||||
}
|
||||
25
src/models/devicesproxymodel.h
Normal file
25
src/models/devicesproxymodel.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class DevicesProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int type READ type WRITE setType NOTIFY typeChanged);
|
||||
|
||||
public:
|
||||
DevicesProxyModel(QObject *parent = nullptr);
|
||||
[[nodiscard]] bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||
|
||||
void setType(int type);
|
||||
[[nodiscard]] int type() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void typeChanged();
|
||||
|
||||
private:
|
||||
int m_type;
|
||||
};
|
||||
Reference in New Issue
Block a user