summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--resources/icons/ui/add-square-button.pngbin965 -> 375 bytes
-rw-r--r--resources/icons/ui/add-square-button@2x.pngbin1386 -> 414 bytes
-rw-r--r--resources/icons/ui/paper-clip-outline.pngbin596 -> 627 bytes
-rw-r--r--resources/icons/ui/paper-clip-outline@2x.pngbin872 -> 1005 bytes
-rw-r--r--resources/icons/ui/settings.pngbin1363 -> 761 bytes
-rw-r--r--resources/icons/ui/settings@2x.pngbin2573 -> 1263 bytes
-rw-r--r--resources/icons/ui/vertical-ellipsis.pngbin830 -> 674 bytes
-rw-r--r--resources/icons/ui/vertical-ellipsis@2x.pngbin1041 -> 892 bytes
-rw-r--r--src/ChatPage.h2
-rw-r--r--src/MainWindow.cpp2
-rw-r--r--src/Utils.cpp8
-rw-r--r--src/Utils.h3
-rw-r--r--src/dialogs/UserProfile.cpp142
-rw-r--r--src/dialogs/UserProfile.h24
-rw-r--r--src/ui/OverlayModal.cpp2
15 files changed, 162 insertions, 21 deletions
diff --git a/resources/icons/ui/add-square-button.png b/resources/icons/ui/add-square-button.png
index a4933c16..7b6f1b19 100644
--- a/resources/icons/ui/add-square-button.png
+++ b/resources/icons/ui/add-square-button.png
Binary files differdiff --git a/resources/icons/ui/add-square-button@2x.png b/resources/icons/ui/add-square-button@2x.png
index b2c4a44b..1aeeb2d5 100644
--- a/resources/icons/ui/add-square-button@2x.png
+++ b/resources/icons/ui/add-square-button@2x.png
Binary files differdiff --git a/resources/icons/ui/paper-clip-outline.png b/resources/icons/ui/paper-clip-outline.png
index 579f564e..98890e4a 100644
--- a/resources/icons/ui/paper-clip-outline.png
+++ b/resources/icons/ui/paper-clip-outline.png
Binary files differdiff --git a/resources/icons/ui/paper-clip-outline@2x.png b/resources/icons/ui/paper-clip-outline@2x.png
index e9f12cd3..ec54386f 100644
--- a/resources/icons/ui/paper-clip-outline@2x.png
+++ b/resources/icons/ui/paper-clip-outline@2x.png
Binary files differdiff --git a/resources/icons/ui/settings.png b/resources/icons/ui/settings.png
index 6e013f75..ba521e27 100644
--- a/resources/icons/ui/settings.png
+++ b/resources/icons/ui/settings.png
Binary files differdiff --git a/resources/icons/ui/settings@2x.png b/resources/icons/ui/settings@2x.png
index dae05360..f77065ca 100644
--- a/resources/icons/ui/settings@2x.png
+++ b/resources/icons/ui/settings@2x.png
Binary files differdiff --git a/resources/icons/ui/vertical-ellipsis.png b/resources/icons/ui/vertical-ellipsis.png
index 5ce9d78f..6b3a36e3 100644
--- a/resources/icons/ui/vertical-ellipsis.png
+++ b/resources/icons/ui/vertical-ellipsis.png
Binary files differdiff --git a/resources/icons/ui/vertical-ellipsis@2x.png b/resources/icons/ui/vertical-ellipsis@2x.png
index 9af0c042..4f28066b 100644
--- a/resources/icons/ui/vertical-ellipsis@2x.png
+++ b/resources/icons/ui/vertical-ellipsis@2x.png
Binary files differdiff --git a/src/ChatPage.h b/src/ChatPage.h
index 6a70acf4..f032901a 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -80,6 +80,7 @@ public:
 
 public slots:
         void leaveRoom(const QString &room_id);
+        void createRoom(const mtx::requests::CreateRoom &req);
 
 signals:
         void connectionLost();
@@ -159,7 +160,6 @@ private slots:
         void dropToLoginPage(const QString &msg);
 
         void joinRoom(const QString &room);
-        void createRoom(const mtx::requests::CreateRoom &req);
         void sendTypingNotifications();
 
 private:
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 35bfba86..fe63456a 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -317,6 +317,7 @@ MainWindow::openUserProfile(const QString &user_id, const QString &room_id)
 
         userProfileModal_ =
           QSharedPointer<OverlayModal>(new OverlayModal(this, userProfileDialog_.data()));
+        userProfileModal_->setContentAlignment(Qt::AlignTop | Qt::AlignHCenter);
 
         userProfileModal_->show();
 }
@@ -394,7 +395,6 @@ MainWindow::showOverlayProgressBar()
                 progressModal_ =
                   QSharedPointer<OverlayModal>(new OverlayModal(this, spinner_.data()),
                                                [](OverlayModal *modal) { modal->deleteLater(); });
-                progressModal_->setContentAlignment(Qt::AlignCenter);
                 progressModal_->setColor(QColor(30, 30, 30));
                 progressModal_->setDismissible(false);
                 progressModal_->show();
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 2247c2b7..97e13c9b 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -2,12 +2,20 @@
 
 #include <QApplication>
 #include <QDesktopWidget>
+#include <QSettings>
 
 #include <variant.hpp>
 
 using TimelineEvent = mtx::events::collections::TimelineEvents;
 
 QString
+utils::localUser()
+{
+        QSettings settings;
+        return settings.value("auth/user_id").toString();
+}
+
+QString
 utils::descriptiveTime(const QDateTime &then)
 {
         const auto now  = QDateTime::currentDateTime();
diff --git a/src/Utils.h b/src/Utils.h
index 8f9b7cff..10b5ee2b 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -15,6 +15,9 @@ namespace utils {
 
 using TimelineEvent = mtx::events::collections::TimelineEvents;
 
+QString
+localUser();
+
 //! Human friendly timestamp representation.
 QString
 descriptiveTime(const QDateTime &then);
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
diff --git a/src/ui/OverlayModal.cpp b/src/ui/OverlayModal.cpp
index 41e07a91..30ebdf5c 100644
--- a/src/ui/OverlayModal.cpp
+++ b/src/ui/OverlayModal.cpp
@@ -29,7 +29,7 @@ OverlayModal::OverlayModal(QWidget *parent, QWidget *content)
         layout_->addWidget(content);
         layout_->setSpacing(0);
         layout_->setContentsMargins(10, 40, 10, 20);
-        setContentAlignment(Qt::AlignTop | Qt::AlignHCenter);
+        setContentAlignment(Qt::AlignCenter);
 
         content->setFocus();
 }