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();
}
|