Create user profile modal
@ -152,6 +152,7 @@ set(SRC_FILES
|
||||
src/dialogs/MemberList.cpp
|
||||
src/dialogs/LeaveRoom.cpp
|
||||
src/dialogs/Logout.cpp
|
||||
src/dialogs/UserProfile.cpp
|
||||
src/dialogs/ReadReceipts.cpp
|
||||
src/dialogs/ReCaptcha.cpp
|
||||
src/dialogs/RoomSettings.cpp
|
||||
@ -265,6 +266,7 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||
src/dialogs/MemberList.h
|
||||
src/dialogs/LeaveRoom.h
|
||||
src/dialogs/Logout.h
|
||||
src/dialogs/UserProfile.h
|
||||
src/dialogs/ReadReceipts.h
|
||||
src/dialogs/ReCaptcha.h
|
||||
src/dialogs/RoomSettings.h
|
||||
|
BIN
resources/icons/ui/black-bubble-speech.png
Normal file
After Width: | Height: | Size: 511 B |
BIN
resources/icons/ui/black-bubble-speech@2x.png
Normal file
After Width: | Height: | Size: 627 B |
Before Width: | Height: | Size: 811 B After Width: | Height: | Size: 499 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 640 B |
BIN
resources/icons/ui/do-not-disturb-rounded-sign.png
Normal file
After Width: | Height: | Size: 746 B |
BIN
resources/icons/ui/do-not-disturb-rounded-sign@2x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 596 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 872 B |
BIN
resources/icons/ui/round-remove-button.png
Normal file
After Width: | Height: | Size: 659 B |
BIN
resources/icons/ui/round-remove-button@2x.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 775 B |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 1.3 KiB |
BIN
resources/icons/ui/volume-off-indicator.png
Normal file
After Width: | Height: | Size: 827 B |
BIN
resources/icons/ui/volume-off-indicator@2x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
@ -1,5 +1,14 @@
|
||||
<RCC>
|
||||
<qresource prefix="/icons">
|
||||
<file>icons/ui/volume-off-indicator.png</file>
|
||||
<file>icons/ui/volume-off-indicator@2x.png</file>
|
||||
<file>icons/ui/black-bubble-speech.png</file>
|
||||
<file>icons/ui/black-bubble-speech@2x.png</file>
|
||||
<file>icons/ui/do-not-disturb-rounded-sign.png</file>
|
||||
<file>icons/ui/do-not-disturb-rounded-sign@2x.png</file>
|
||||
<file>icons/ui/round-remove-button.png</file>
|
||||
<file>icons/ui/round-remove-button@2x.png</file>
|
||||
|
||||
<file>icons/ui/double-tick-indicator.png</file>
|
||||
<file>icons/ui/double-tick-indicator@2x.png</file>
|
||||
<file>icons/ui/lock.png</file>
|
||||
|
@ -147,6 +147,7 @@ dialogs--ReadReceipts,
|
||||
dialogs--JoinRoom,
|
||||
dialogs--MemberList,
|
||||
dialogs--PreviewUploadOverlay,
|
||||
dialogs--UserProfile,
|
||||
dialogs--CreateRoom > QLineEdit,
|
||||
dialogs--InviteUsers > QLineEdit,
|
||||
EditModal,
|
||||
|
@ -149,10 +149,11 @@ dialogs--ReadReceipts,
|
||||
dialogs--MemberList,
|
||||
dialogs--JoinRoom,
|
||||
dialogs--PreviewUploadOverlay,
|
||||
dialogs--UserProfile,
|
||||
EditModal,
|
||||
QListWidget {
|
||||
background-color: white;
|
||||
color: #333;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
TopSection {
|
||||
|
@ -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(
|
||||
|
@ -309,6 +309,18 @@ MainWindow::hasActiveUser()
|
||||
settings.contains("auth/user_id");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@ -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();
|
||||
|
@ -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_;
|
||||
};
|
||||
|
153
src/dialogs/UserProfile.cpp
Normal file
@ -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);
|
||||
}
|
53
src/dialogs/UserProfile.h
Normal file
@ -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
|
@ -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"
|
||||
@ -561,6 +562,13 @@ TimelineItem::generateBody(const QString &body)
|
||||
// The username/timestamp is displayed along with the message 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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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.
|
||||
|