diff --git a/src/AvatarProvider.cpp b/src/AvatarProvider.cpp
index dbfc1945..57b61c75 100644
--- a/src/AvatarProvider.cpp
+++ b/src/AvatarProvider.cpp
@@ -50,6 +50,8 @@ resolve(const QString &room_id, const QString &user_id, QObject *receiver, Avata
[callback](const QByteArray &data) { callback(QImage::fromData(data)); });
mtx::http::ThumbOpts opts;
+ opts.width = 256;
+ opts.height = 256;
opts.mxc_url = avatarUrl.toStdString();
http::client()->get_thumbnail(
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index fdca98c3..35bfba86 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -310,6 +310,18 @@ MainWindow::hasActiveUser()
}
void
+MainWindow::openUserProfile(const QString &user_id, const QString &room_id)
+{
+ userProfileDialog_ = QSharedPointer<dialogs::UserProfile>(new dialogs::UserProfile(this));
+ userProfileDialog_->init(user_id, room_id);
+
+ userProfileModal_ =
+ QSharedPointer<OverlayModal>(new OverlayModal(this, userProfileDialog_.data()));
+
+ userProfileModal_->show();
+}
+
+void
MainWindow::openRoomSettings(const QString &room_id)
{
const auto roomToSearch = room_id.isEmpty() ? chat_page_->currentRoom() : "";
@@ -382,6 +394,7 @@ 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/MainWindow.h b/src/MainWindow.h
index 92040191..3d9a94d3 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -28,6 +28,7 @@
#include "RegisterPage.h"
#include "UserSettingsPage.h"
#include "WelcomePage.h"
+#include "dialogs/UserProfile.h"
class ChatPage;
class LoadingIndicator;
@@ -71,6 +72,7 @@ public:
void openLogoutDialog(std::function<void()> callback);
void openRoomSettings(const QString &room_id = "");
void openMemberListDialog(const QString &room_id = "");
+ void openUserProfile(const QString &user_id, const QString &room_id);
protected:
void closeEvent(QCloseEvent *event) override;
@@ -171,4 +173,7 @@ private:
QSharedPointer<OverlayModal> memberListModal_;
//! Member list dialog.
QSharedPointer<dialogs::MemberList> memberListDialog_;
+
+ QSharedPointer<OverlayModal> userProfileModal_;
+ QSharedPointer<dialogs::UserProfile> userProfileDialog_;
};
diff --git a/src/dialogs/UserProfile.cpp b/src/dialogs/UserProfile.cpp
new file mode 100644
index 00000000..1da293a5
--- /dev/null
+++ b/src/dialogs/UserProfile.cpp
@@ -0,0 +1,153 @@
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QListWidget>
+#include <QPaintEvent>
+#include <QSettings>
+#include <QStyleOption>
+#include <QVBoxLayout>
+
+#include "AvatarProvider.h"
+#include "Cache.h"
+#include "Utils.h"
+#include "dialogs/UserProfile.h"
+#include "ui/Avatar.h"
+#include "ui/FlatButton.h"
+
+using namespace dialogs;
+
+constexpr int BUTTON_SIZE = 36;
+
+DeviceItem::DeviceItem(QWidget *parent, QString deviceName)
+ : QWidget(parent)
+ , name_(deviceName)
+{}
+
+UserProfile::UserProfile(QWidget *parent)
+ : QWidget(parent)
+{
+ QIcon banIcon, kickIcon, ignoreIcon, startChatIcon;
+
+ banIcon.addFile(":/icons/icons/ui/do-not-disturb-rounded-sign.png");
+ banBtn_ = new FlatButton(this);
+ banBtn_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE);
+ banBtn_->setCornerRadius(BUTTON_SIZE / 2);
+ banBtn_->setIcon(banIcon);
+ banBtn_->setIconSize(QSize(BUTTON_SIZE / 2, BUTTON_SIZE / 2));
+ banBtn_->setToolTip(tr("Ban the user from the room"));
+
+ ignoreIcon.addFile(":/icons/icons/ui/volume-off-indicator.png");
+ ignoreBtn_ = new FlatButton(this);
+ ignoreBtn_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE);
+ ignoreBtn_->setCornerRadius(BUTTON_SIZE / 2);
+ ignoreBtn_->setIcon(ignoreIcon);
+ ignoreBtn_->setIconSize(QSize(BUTTON_SIZE / 2, BUTTON_SIZE / 2));
+ ignoreBtn_->setToolTip(tr("Ignore messages from this user"));
+
+ kickIcon.addFile(":/icons/icons/ui/round-remove-button.png");
+ kickBtn_ = new FlatButton(this);
+ kickBtn_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE);
+ kickBtn_->setCornerRadius(BUTTON_SIZE / 2);
+ kickBtn_->setIcon(kickIcon);
+ kickBtn_->setIconSize(QSize(BUTTON_SIZE / 2, BUTTON_SIZE / 2));
+ kickBtn_->setToolTip(tr("Kick the user from the room"));
+
+ startChatIcon.addFile(":/icons/icons/ui/black-bubble-speech.png");
+ startChat_ = new FlatButton(this);
+ startChat_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE);
+ startChat_->setCornerRadius(BUTTON_SIZE / 2);
+ startChat_->setIcon(startChatIcon);
+ startChat_->setIconSize(QSize(BUTTON_SIZE / 2, BUTTON_SIZE / 2));
+ startChat_->setToolTip(tr("Start a conversation"));
+
+ // Button line
+ auto btnLayout = new QHBoxLayout;
+ 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->setSpacing(8);
+ btnLayout->setMargin(0);
+
+ avatar_ = new Avatar(this);
+ avatar_->setLetter("X");
+ avatar_->setSize(148);
+
+ QFont font;
+ font.setPointSizeF(font.pointSizeF() * 2);
+
+ userIdLabel_ = new QLabel(this);
+ displayNameLabel_ = new QLabel(this);
+ displayNameLabel_->setFont(font);
+
+ auto textLayout = new QVBoxLayout;
+ textLayout->addWidget(displayNameLabel_);
+ textLayout->addWidget(userIdLabel_);
+ textLayout->setAlignment(displayNameLabel_, Qt::AlignCenter | Qt::AlignTop);
+ textLayout->setAlignment(userIdLabel_, Qt::AlignCenter | Qt::AlignTop);
+ textLayout->setSpacing(4);
+ textLayout->setMargin(0);
+
+ auto vlayout = new QVBoxLayout{this};
+ vlayout->addWidget(avatar_);
+ vlayout->addLayout(textLayout);
+ vlayout->addLayout(btnLayout);
+
+ vlayout->setAlignment(avatar_, Qt::AlignCenter | Qt::AlignTop);
+ vlayout->setAlignment(userIdLabel_, Qt::AlignCenter | Qt::AlignTop);
+
+ setAutoFillBackground(true);
+ setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
+ setWindowModality(Qt::WindowModal);
+
+ setMinimumWidth(340);
+ setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
+
+ vlayout->setSpacing(15);
+ vlayout->setContentsMargins(20, 40, 20, 20);
+}
+
+void
+UserProfile::init(const QString &userId, const QString &roomId)
+{
+ auto displayName = Cache::displayName(roomId, userId);
+
+ userIdLabel_->setText(userId);
+ displayNameLabel_->setText(displayName);
+ avatar_->setLetter(utils::firstChar(displayName));
+
+ 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.
+ }
+
+ try {
+ bool hasMemberRights =
+ cache::client()->hasEnoughPowerLevel({mtx::events::EventType::RoomMember},
+ roomId.toStdString(),
+ localUser.toStdString());
+ if (!hasMemberRights) {
+ kickBtn_->hide();
+ banBtn_->hide();
+ }
+ } catch (const lmdb::error &e) {
+ nhlog::db()->warn("lmdb error: {}", e.what());
+ }
+}
+
+void
+UserProfile::paintEvent(QPaintEvent *)
+{
+ QStyleOption opt;
+ opt.init(this);
+ QPainter p(this);
+ style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
+}
diff --git a/src/dialogs/UserProfile.h b/src/dialogs/UserProfile.h
new file mode 100644
index 00000000..8d5b9c5f
--- /dev/null
+++ b/src/dialogs/UserProfile.h
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <QString>
+#include <QWidget>
+
+class Avatar;
+class FlatButton;
+class QLabel;
+class QListWidget;
+
+namespace dialogs {
+
+class DeviceItem : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit DeviceItem(QWidget *parent, QString deviceName);
+
+private:
+ QString name_;
+
+ // Toggle *verifyToggle_;
+};
+
+class UserProfile : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit UserProfile(QWidget *parent = nullptr);
+
+ void init(const QString &userId, const QString &roomId);
+
+protected:
+ void paintEvent(QPaintEvent *) override;
+
+private:
+ Avatar *avatar_;
+
+ QString displayName_;
+ QString userId_;
+
+ QLabel *userIdLabel_;
+ QLabel *displayNameLabel_;
+
+ FlatButton *banBtn_;
+ FlatButton *kickBtn_;
+ FlatButton *ignoreBtn_;
+ FlatButton *startChat_;
+
+ QListWidget *devices_;
+};
+} // dialogs
diff --git a/src/timeline/TimelineItem.cpp b/src/timeline/TimelineItem.cpp
index 696db8de..71b3156c 100644
--- a/src/timeline/TimelineItem.cpp
+++ b/src/timeline/TimelineItem.cpp
@@ -23,6 +23,7 @@
#include "ChatPage.h"
#include "Config.h"
#include "Logging.h"
+#include "MainWindow.h"
#include "Olm.h"
#include "ui/Avatar.h"
#include "ui/Painter.h"
@@ -562,6 +563,13 @@ TimelineItem::generateBody(const QString &body)
void
TimelineItem::generateBody(const QString &user_id, const QString &displayname, const QString &body)
{
+ generateUserName(user_id, displayname);
+ generateBody(body);
+}
+
+void
+TimelineItem::generateUserName(const QString &user_id, const QString &displayname)
+{
auto sender = displayname;
if (displayname.startsWith("@")) {
@@ -598,7 +606,9 @@ TimelineItem::generateBody(const QString &user_id, const QString &displayname, c
userName_->setFont(f);
});
- generateBody(body);
+ connect(filter, &UserProfileFilter::clicked, this, [this, user_id]() {
+ MainWindow::instance()->openUserProfile(user_id, room_id_);
+ });
}
void
@@ -742,10 +752,7 @@ TimelineItem::addAvatar()
auto userid = descriptionMsg_.userid;
auto displayName = Cache::displayName(room_id_, userid);
- QFontMetrics fm(usernameFont_);
- userName_ = new QLabel(this);
- userName_->setFont(usernameFont_);
- userName_->setText(fm.elidedText(displayName, Qt::ElideRight, 500));
+ generateUserName(userid, displayName);
setupAvatarLayout(displayName);
diff --git a/src/timeline/TimelineItem.h b/src/timeline/TimelineItem.h
index 874c00df..db000078 100644
--- a/src/timeline/TimelineItem.h
+++ b/src/timeline/TimelineItem.h
@@ -137,13 +137,13 @@ public:
signals:
void hoverOff();
void hoverOn();
+ void clicked();
protected:
bool eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseButtonRelease) {
- // QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
- // TODO: Open user profile
+ emit clicked();
return true;
} else if (event->type() == QEvent::HoverLeave) {
emit hoverOff();
@@ -274,6 +274,7 @@ private:
void generateBody(const QString &body);
void generateBody(const QString &user_id, const QString &displayname, const QString &body);
void generateTimestamp(const QDateTime &time);
+ void generateUserName(const QString &userid, const QString &displayname);
void setupAvatarLayout(const QString &userName);
void setupSimpleLayout();
diff --git a/src/ui/OverlayModal.cpp b/src/ui/OverlayModal.cpp
index 6aa16b07..41e07a91 100644
--- a/src/ui/OverlayModal.cpp
+++ b/src/ui/OverlayModal.cpp
@@ -25,11 +25,11 @@ OverlayModal::OverlayModal(QWidget *parent, QWidget *content)
, content_{content}
, color_{QColor(30, 30, 30, 170)}
{
- auto layout = new QVBoxLayout();
- layout->addWidget(content);
- layout->setAlignment(Qt::AlignCenter);
-
- setLayout(layout);
+ layout_ = new QVBoxLayout(this);
+ layout_->addWidget(content);
+ layout_->setSpacing(0);
+ layout_->setContentsMargins(10, 40, 10, 20);
+ setContentAlignment(Qt::AlignTop | Qt::AlignHCenter);
content->setFocus();
}
diff --git a/src/ui/OverlayModal.h b/src/ui/OverlayModal.h
index a761e3ed..acba7a7a 100644
--- a/src/ui/OverlayModal.h
+++ b/src/ui/OverlayModal.h
@@ -20,6 +20,7 @@
#include <QKeyEvent>
#include <QMouseEvent>
#include <QPaintEvent>
+#include <QVBoxLayout>
#include "OverlayWidget.h"
@@ -31,6 +32,8 @@ public:
void setColor(QColor color) { color_ = color; }
void setDismissible(bool state) { isDismissible_ = state; }
+ void setContentAlignment(QFlags<Qt::AlignmentFlag> flag) { layout_->setAlignment(flag); }
+
protected:
void paintEvent(QPaintEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
@@ -38,6 +41,8 @@ protected:
private:
QWidget *content_;
+ QVBoxLayout *layout_;
+
QColor color_;
//! Decides whether or not the modal can be removed by clicking into it.
|