From 649c5ff86d97ed7036822dc420405d84460d5e96 Mon Sep 17 00:00:00 2001 From: Thulinma Date: Sat, 16 Oct 2021 14:17:55 +0200 Subject: [PATCH] Add support for listing devices that do not support encryption, add support for logging out devices. Ticks off another box in #23! --- CMakeLists.txt | 2 +- io.github.NhekoReborn.Nheko.yaml | 2 +- resources/qml/dialogs/UserProfile.qml | 10 +++++ src/ui/UserProfile.cpp | 63 +++++++++++++++++++++++++++ src/ui/UserProfile.h | 18 +++++++- 5 files changed, 92 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ff3ca26..439a4971 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -385,7 +385,7 @@ if(USE_BUNDLED_MTXCLIENT) FetchContent_Declare( MatrixClient GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git - GIT_TAG 8b56b466dbacde501ed9087d53bb4f51b297eca8 + GIT_TAG e5284ccc9d902117bbe782b0be76fa272b7f0a90 ) set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "") set(BUILD_LIB_TESTS OFF CACHE INTERNAL "") diff --git a/io.github.NhekoReborn.Nheko.yaml b/io.github.NhekoReborn.Nheko.yaml index ce272ea7..1ad5a0db 100644 --- a/io.github.NhekoReborn.Nheko.yaml +++ b/io.github.NhekoReborn.Nheko.yaml @@ -163,7 +163,7 @@ modules: buildsystem: cmake-ninja name: mtxclient sources: - - commit: 8b56b466dbacde501ed9087d53bb4f51b297eca8 + - commit: e5284ccc9d902117bbe782b0be76fa272b7f0a90 type: git url: https://github.com/Nheko-Reborn/mtxclient.git - config-opts: diff --git a/resources/qml/dialogs/UserProfile.qml b/resources/qml/dialogs/UserProfile.qml index c921278e..30052168 100644 --- a/resources/qml/dialogs/UserProfile.qml +++ b/resources/qml/dialogs/UserProfile.qml @@ -310,6 +310,7 @@ ApplicationWindow { Image { Layout.preferredHeight: 16 Layout.preferredWidth: 16 + visible: verificationStatus != VerificationStatus.NOT_APPLICABLE source: { switch (verificationStatus) { case VerificationStatus.VERIFIED: @@ -337,6 +338,15 @@ ApplicationWindow { } } + ImageButton { + image: ":/icons/icons/ui/power-button-off.png" + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Sign out this device.") + onClicked: profile.signOutDevice(deviceId) + visible: profile.isSelf + } + } footer: DialogButtonBox { diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index 31ae9f8b..591110af 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -16,6 +16,7 @@ #include "mtx/responses/crypto.hpp" #include "timeline/TimelineModel.h" #include "timeline/TimelineViewManager.h" +#include "ui/UIA.h" UserProfile::UserProfile(QString roomid, QString userid, @@ -137,6 +138,27 @@ UserProfile::isSelf() const return this->userid_ == utils::localUser(); } +void +UserProfile::signOutDevice(const QString &deviceID) +{ + http::client()->delete_device( + deviceID.toStdString(), + UIA::instance()->genericHandler(tr("Sign out device %1").arg(deviceID)), + [this, deviceID](mtx::http::RequestErr e) { + if (e) { + nhlog::ui()->critical("Failure when attempting to sign out device {}", + deviceID.toStdString()); + return; + } + nhlog::ui()->info("Device {} successfully signed out!", deviceID.toStdString()); + // This is us. Let's update the interface accordingly + if (isSelf() && deviceID.toStdString() == ::http::client()->device_id()) { + ChatPage::instance()->dropToLoginPageCb(tr("You signed out this device.")); + } + refreshDevices(); + }); +} + void UserProfile::refreshDevices() { @@ -221,6 +243,47 @@ UserProfile::updateVerificationStatus() verified}); } + // For self, also query devices without keys + if (isSelf()) { + http::client()->query_devices( + [this, deviceInfo](const mtx::responses::QueryDevices &allDevs, + mtx::http::RequestErr err) mutable { + if (err) { + nhlog::net()->warn("failed to query devices: {} {}", + err->matrix_error.error, + static_cast(err->status_code)); + this->deviceList_.queueReset(std::move(deviceInfo)); + emit devicesChanged(); + return; + } + for (const auto &d : allDevs.devices) { + // First, check if we already have an entry for this device + bool found = false; + for (auto &e : deviceInfo) { + if (e.device_id.toStdString() == d.device_id) { + found = true; + // Gottem! Let's fill in the blanks + e.lastIp = QString::fromStdString(d.last_seen_ip); + e.lastTs = d.last_seen_ts; + break; + } + } + // No entry? Let's add one. + if (!found) { + deviceInfo.push_back({QString::fromStdString(d.device_id), + QString::fromStdString(d.display_name), + verification::NOT_APPLICABLE, + QString::fromStdString(d.last_seen_ip), + d.last_seen_ts}); + } + } + + this->deviceList_.queueReset(std::move(deviceInfo)); + emit devicesChanged(); + }); + return; + } + this->deviceList_.queueReset(std::move(deviceInfo)); emit devicesChanged(); } diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h index 68f9c21b..e8bff6ba 100644 --- a/src/ui/UserProfile.h +++ b/src/ui/UserProfile.h @@ -21,7 +21,8 @@ enum Status SELF, VERIFIED, UNVERIFIED, - BLOCKED + BLOCKED, + NOT_APPLICABLE }; Q_ENUM_NS(Status) } @@ -33,12 +34,24 @@ class TimelineViewManager; class DeviceInfo { public: + DeviceInfo(const QString deviceID, + const QString displayName, + verification::Status verification_status_, + const QString lastIp_, + const size_t lastTs_) + : device_id(deviceID) + , display_name(displayName) + , verification_status(verification_status_) + , lastIp(lastIp_) + , lastTs(lastTs_) + {} DeviceInfo(const QString deviceID, const QString displayName, verification::Status verification_status_) : device_id(deviceID) , display_name(displayName) , verification_status(verification_status_) + , lastTs(0) {} DeviceInfo() : verification_status(verification::UNVERIFIED) @@ -48,6 +61,8 @@ public: QString display_name; verification::Status verification_status; + QString lastIp; + size_t lastTs; }; class DeviceInfoModel : public QAbstractListModel @@ -121,6 +136,7 @@ public: Q_INVOKABLE void fetchDeviceList(const QString &userID); Q_INVOKABLE void refreshDevices(); Q_INVOKABLE void banUser(); + Q_INVOKABLE void signOutDevice(const QString &deviceID); // Q_INVOKABLE void ignoreUser(); Q_INVOKABLE void kickUser(); Q_INVOKABLE void startChat();