diff --git a/src/dialogs/UserProfile.cpp b/src/dialogs/UserProfile.cpp
index 1da293a5..34f81fa3 100644
--- a/src/dialogs/UserProfile.cpp
+++ b/src/dialogs/UserProfile.cpp
@@ -8,6 +8,8 @@
#include "AvatarProvider.h"
#include "Cache.h"
+#include "ChatPage.h"
+#include "MatrixClient.h"
#include "Utils.h"
#include "dialogs/UserProfile.h"
#include "ui/Avatar.h"
@@ -17,10 +19,25 @@ using namespace dialogs;
constexpr int BUTTON_SIZE = 36;
-DeviceItem::DeviceItem(QWidget *parent, QString deviceName)
+DeviceItem::DeviceItem(DeviceInfo device, QWidget *parent)
: QWidget(parent)
- , name_(deviceName)
-{}
+ , info_(std::move(device))
+{
+ QFont font;
+ font.setBold(true);
+
+ auto deviceIdLabel = new QLabel(info_.device_id, this);
+ deviceIdLabel->setFont(font);
+
+ auto layout = new QVBoxLayout{this};
+ layout->addWidget(deviceIdLabel);
+
+ if (!info_.display_name.isEmpty())
+ layout->addWidget(new QLabel(info_.display_name, this));
+
+ layout->setMargin(0);
+ layout->setSpacing(4);
+}
UserProfile::UserProfile(QWidget *parent)
: QWidget(parent)
@@ -34,6 +51,7 @@ UserProfile::UserProfile(QWidget *parent)
banBtn_->setIcon(banIcon);
banBtn_->setIconSize(QSize(BUTTON_SIZE / 2, BUTTON_SIZE / 2));
banBtn_->setToolTip(tr("Ban the user from the room"));
+ banBtn_->setDisabled(true); // Not used yet.
ignoreIcon.addFile(":/icons/icons/ui/volume-off-indicator.png");
ignoreBtn_ = new FlatButton(this);
@@ -42,6 +60,7 @@ UserProfile::UserProfile(QWidget *parent)
ignoreBtn_->setIcon(ignoreIcon);
ignoreBtn_->setIconSize(QSize(BUTTON_SIZE / 2, BUTTON_SIZE / 2));
ignoreBtn_->setToolTip(tr("Ignore messages from this user"));
+ ignoreBtn_->setDisabled(true); // Not used yet.
kickIcon.addFile(":/icons/icons/ui/round-remove-button.png");
kickBtn_ = new FlatButton(this);
@@ -50,6 +69,7 @@ UserProfile::UserProfile(QWidget *parent)
kickBtn_->setIcon(kickIcon);
kickBtn_->setIconSize(QSize(BUTTON_SIZE / 2, BUTTON_SIZE / 2));
kickBtn_->setToolTip(tr("Kick the user from the room"));
+ kickBtn_->setDisabled(true); // Not used yet.
startChatIcon.addFile(":/icons/icons/ui/black-bubble-speech.png");
startChat_ = new FlatButton(this);
@@ -59,21 +79,34 @@ UserProfile::UserProfile(QWidget *parent)
startChat_->setIconSize(QSize(BUTTON_SIZE / 2, BUTTON_SIZE / 2));
startChat_->setToolTip(tr("Start a conversation"));
+ connect(startChat_, &QPushButton::clicked, this, [this]() {
+ auto user_id = userIdLabel_->text();
+
+ mtx::requests::CreateRoom req;
+ req.preset = mtx::requests::Preset::PrivateChat;
+ req.visibility = mtx::requests::Visibility::Private;
+
+ if (utils::localUser() != user_id)
+ req.invite = {user_id.toStdString()};
+
+ emit ChatPage::instance()->createRoom(req);
+ });
+
// Button line
auto btnLayout = new QHBoxLayout;
+ btnLayout->addStretch(1);
btnLayout->addWidget(startChat_);
btnLayout->addWidget(ignoreBtn_);
- // TODO: check if the user has enough power level given the room_id
- // in which the profile was opened.
btnLayout->addWidget(kickBtn_);
btnLayout->addWidget(banBtn_);
+ btnLayout->addStretch(1);
btnLayout->setSpacing(8);
btnLayout->setMargin(0);
avatar_ = new Avatar(this);
avatar_->setLetter("X");
- avatar_->setSize(148);
+ avatar_->setSize(128);
QFont font;
font.setPointSizeF(font.pointSizeF() * 2);
@@ -90,10 +123,26 @@ UserProfile::UserProfile(QWidget *parent)
textLayout->setSpacing(4);
textLayout->setMargin(0);
+ devices_ = new QListWidget{this};
+ devices_->setFrameStyle(QFrame::NoFrame);
+ devices_->setSelectionMode(QAbstractItemView::NoSelection);
+ devices_->setAttribute(Qt::WA_MacShowFocusRect, 0);
+ devices_->setSpacing(5);
+ devices_->hide();
+
+ QFont descriptionLabelFont;
+ descriptionLabelFont.setWeight(65);
+
+ devicesLabel_ = new QLabel(tr("Devices").toUpper(), this);
+ devicesLabel_->setFont(descriptionLabelFont);
+ devicesLabel_->hide();
+
auto vlayout = new QVBoxLayout{this};
vlayout->addWidget(avatar_);
vlayout->addLayout(textLayout);
vlayout->addLayout(btnLayout);
+ vlayout->addWidget(devicesLabel_, Qt::AlignLeft);
+ vlayout->addWidget(devices_);
vlayout->setAlignment(avatar_, Qt::AlignCenter | Qt::AlignTop);
vlayout->setAlignment(userIdLabel_, Qt::AlignCenter | Qt::AlignTop);
@@ -107,6 +156,10 @@ UserProfile::UserProfile(QWidget *parent)
vlayout->setSpacing(15);
vlayout->setContentsMargins(20, 40, 20, 20);
+
+ qRegisterMetaType<std::vector<DeviceInfo>>();
+
+ connect(this, &UserProfile::devicesRetrieved, this, &UserProfile::updateDeviceList);
}
void
@@ -121,13 +174,7 @@ UserProfile::init(const QString &userId, const QString &roomId)
AvatarProvider::resolve(
roomId, userId, this, [this](const QImage &img) { avatar_->setImage(img); });
- QSettings settings;
- auto localUser = settings.value("auth/user_id").toString();
-
- if (localUser == userId) {
- qDebug() << "the local user should have edit rights on avatar & display name";
- // TODO: click on display name & avatar to change.
- }
+ auto localUser = utils::localUser();
try {
bool hasMemberRights =
@@ -141,6 +188,75 @@ UserProfile::init(const QString &userId, const QString &roomId)
} catch (const lmdb::error &e) {
nhlog::db()->warn("lmdb error: {}", e.what());
}
+
+ if (localUser == userId) {
+ // TODO: click on display name & avatar to change.
+ kickBtn_->hide();
+ banBtn_->hide();
+ ignoreBtn_->hide();
+ }
+
+ mtx::requests::QueryKeys req;
+ req.device_keys[userId.toStdString()] = {};
+
+ http::client()->query_keys(
+ req,
+ [user_id = userId.toStdString(), this](const mtx::responses::QueryKeys &res,
+ mtx::http::RequestErr err) {
+ if (err) {
+ nhlog::net()->warn("failed to query device keys: {} {}",
+ err->matrix_error.error,
+ static_cast<int>(err->status_code));
+ // TODO: Notify the UI.
+ return;
+ }
+
+ if (res.device_keys.empty() ||
+ (res.device_keys.find(user_id) == res.device_keys.end())) {
+ nhlog::net()->warn("no devices retrieved {}", user_id);
+ return;
+ }
+
+ auto devices = res.device_keys.at(user_id);
+
+ std::vector<DeviceInfo> deviceInfo;
+ for (const auto &d : devices) {
+ auto device = d.second;
+
+ // TODO: Verify signatures and ignore those that don't pass.
+ deviceInfo.emplace_back(DeviceInfo{
+ QString::fromStdString(d.first),
+ QString::fromStdString(device.unsigned_info.device_display_name)});
+ }
+
+ std::sort(deviceInfo.begin(),
+ deviceInfo.end(),
+ [](const DeviceInfo &a, const DeviceInfo &b) {
+ return a.device_id > b.device_id;
+ });
+
+ if (!deviceInfo.empty())
+ emit devicesRetrieved(deviceInfo);
+ });
+}
+
+void
+UserProfile::updateDeviceList(const std::vector<DeviceInfo> &devices)
+{
+ for (const auto &dev : devices) {
+ auto deviceItem = new DeviceItem(dev, this);
+ auto item = new QListWidgetItem;
+
+ item->setSizeHint(deviceItem->minimumSizeHint());
+ item->setFlags(Qt::NoItemFlags);
+ item->setTextAlignment(Qt::AlignCenter);
+
+ devices_->insertItem(devices_->count() - 1, item);
+ devices_->setItemWidget(item, deviceItem);
+ }
+
+ devicesLabel_->show();
+ devices_->show();
}
void
diff --git a/src/dialogs/UserProfile.h b/src/dialogs/UserProfile.h
index 8d5b9c5f..ad01c650 100644
--- a/src/dialogs/UserProfile.h
+++ b/src/dialogs/UserProfile.h
@@ -7,6 +7,15 @@ class Avatar;
class FlatButton;
class QLabel;
class QListWidget;
+class Toggle;
+
+struct DeviceInfo
+{
+ QString device_id;
+ QString display_name;
+};
+
+Q_DECLARE_METATYPE(std::vector<DeviceInfo>)
namespace dialogs {
@@ -15,10 +24,10 @@ class DeviceItem : public QWidget
Q_OBJECT
public:
- explicit DeviceItem(QWidget *parent, QString deviceName);
+ explicit DeviceItem(DeviceInfo device, QWidget *parent);
private:
- QString name_;
+ DeviceInfo info_;
// Toggle *verifyToggle_;
};
@@ -34,12 +43,15 @@ public:
protected:
void paintEvent(QPaintEvent *) override;
+signals:
+ void devicesRetrieved(const std::vector<DeviceInfo> &devices);
+
+private slots:
+ void updateDeviceList(const std::vector<DeviceInfo> &devices);
+
private:
Avatar *avatar_;
- QString displayName_;
- QString userId_;
-
QLabel *userIdLabel_;
QLabel *displayNameLabel_;
@@ -48,6 +60,8 @@ private:
FlatButton *ignoreBtn_;
FlatButton *startChat_;
+ QLabel *devicesLabel_;
QListWidget *devices_;
};
+
} // dialogs
|