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,
@@ -138,6 +139,27 @@ UserProfile::isSelf() const
}
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()
{
cache::client()->markUserKeysOutOfDate({this->userid_.toStdString()});
@@ -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<int>(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)
}
@@ -35,10 +36,22 @@ 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();
|