mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-26 13:08:48 +03:00
commit
4467edd18e
4 changed files with 0 additions and 468 deletions
|
@ -1,316 +0,0 @@
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QListWidget>
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <QShortcut>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include "Cache.h"
|
|
||||||
#include "ChatPage.h"
|
|
||||||
#include "Logging.h"
|
|
||||||
#include "MatrixClient.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "dialogs/UserProfile.h"
|
|
||||||
#include "ui/Avatar.h"
|
|
||||||
#include "ui/FlatButton.h"
|
|
||||||
|
|
||||||
using namespace dialogs;
|
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(std::vector<DeviceInfo>)
|
|
||||||
|
|
||||||
constexpr int BUTTON_SIZE = 36;
|
|
||||||
constexpr int BUTTON_RADIUS = BUTTON_SIZE / 2;
|
|
||||||
constexpr int WIDGET_MARGIN = 20;
|
|
||||||
constexpr int TOP_WIDGET_MARGIN = 2 * WIDGET_MARGIN;
|
|
||||||
constexpr int WIDGET_SPACING = 15;
|
|
||||||
constexpr int TEXT_SPACING = 4;
|
|
||||||
constexpr int DEVICE_SPACING = 5;
|
|
||||||
|
|
||||||
DeviceItem::DeviceItem(DeviceInfo device, QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, 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)
|
|
||||||
{
|
|
||||||
setAutoFillBackground(true);
|
|
||||||
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
|
|
||||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
|
||||||
|
|
||||||
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_RADIUS);
|
|
||||||
banBtn_->setIcon(banIcon);
|
|
||||||
banBtn_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS));
|
|
||||||
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_RADIUS);
|
|
||||||
ignoreBtn_->setIcon(ignoreIcon);
|
|
||||||
ignoreBtn_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS));
|
|
||||||
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);
|
|
||||||
kickBtn_->setFixedSize(BUTTON_SIZE, BUTTON_SIZE);
|
|
||||||
kickBtn_->setCornerRadius(BUTTON_RADIUS);
|
|
||||||
kickBtn_->setIcon(kickIcon);
|
|
||||||
kickBtn_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS));
|
|
||||||
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_RADIUS);
|
|
||||||
startChat_->setIcon(startChatIcon);
|
|
||||||
startChat_->setIconSize(QSize(BUTTON_RADIUS, BUTTON_RADIUS));
|
|
||||||
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()};
|
|
||||||
|
|
||||||
if (QMessageBox::question(
|
|
||||||
this,
|
|
||||||
tr("Confirm DM"),
|
|
||||||
tr("Do you really want to invite %1 (%2) to a direct chat?")
|
|
||||||
.arg(cache::displayName(roomId_, user_id))
|
|
||||||
.arg(user_id)) != QMessageBox::Yes)
|
|
||||||
return;
|
|
||||||
|
|
||||||
emit ChatPage::instance()->createRoom(req);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(banBtn_, &QPushButton::clicked, this, [this] {
|
|
||||||
ChatPage::instance()->banUser(userIdLabel_->text(), "");
|
|
||||||
});
|
|
||||||
connect(kickBtn_, &QPushButton::clicked, this, [this] {
|
|
||||||
ChatPage::instance()->kickUser(userIdLabel_->text(), "");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Button line
|
|
||||||
auto btnLayout = new QHBoxLayout;
|
|
||||||
btnLayout->addStretch(1);
|
|
||||||
btnLayout->addWidget(startChat_);
|
|
||||||
btnLayout->addWidget(ignoreBtn_);
|
|
||||||
|
|
||||||
btnLayout->addWidget(kickBtn_);
|
|
||||||
btnLayout->addWidget(banBtn_);
|
|
||||||
btnLayout->addStretch(1);
|
|
||||||
btnLayout->setSpacing(8);
|
|
||||||
btnLayout->setMargin(0);
|
|
||||||
|
|
||||||
avatar_ = new Avatar(this, 128);
|
|
||||||
avatar_->setLetter("X");
|
|
||||||
|
|
||||||
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(TEXT_SPACING);
|
|
||||||
textLayout->setMargin(0);
|
|
||||||
|
|
||||||
devices_ = new QListWidget{this};
|
|
||||||
devices_->setFrameStyle(QFrame::NoFrame);
|
|
||||||
devices_->setSelectionMode(QAbstractItemView::NoSelection);
|
|
||||||
devices_->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
|
||||||
devices_->setSpacing(DEVICE_SPACING);
|
|
||||||
|
|
||||||
QFont descriptionLabelFont;
|
|
||||||
descriptionLabelFont.setWeight(65);
|
|
||||||
|
|
||||||
devicesLabel_ = new QLabel(tr("Devices").toUpper(), this);
|
|
||||||
devicesLabel_->setFont(descriptionLabelFont);
|
|
||||||
devicesLabel_->hide();
|
|
||||||
devicesLabel_->setFixedSize(devicesLabel_->sizeHint());
|
|
||||||
|
|
||||||
auto okBtn = new QPushButton("OK", this);
|
|
||||||
|
|
||||||
auto closeLayout = new QHBoxLayout();
|
|
||||||
closeLayout->setSpacing(15);
|
|
||||||
closeLayout->addStretch(1);
|
|
||||||
closeLayout->addWidget(okBtn);
|
|
||||||
|
|
||||||
auto vlayout = new QVBoxLayout{this};
|
|
||||||
vlayout->addWidget(avatar_, 0, Qt::AlignCenter | Qt::AlignTop);
|
|
||||||
vlayout->addLayout(textLayout);
|
|
||||||
vlayout->addLayout(btnLayout);
|
|
||||||
vlayout->addWidget(devicesLabel_, 0, Qt::AlignLeft);
|
|
||||||
vlayout->addWidget(devices_, 1);
|
|
||||||
vlayout->addLayout(closeLayout);
|
|
||||||
|
|
||||||
QFont largeFont;
|
|
||||||
largeFont.setPointSizeF(largeFont.pointSizeF() * 1.5);
|
|
||||||
|
|
||||||
setMinimumWidth(
|
|
||||||
std::max(devices_->sizeHint().width() + 4 * WIDGET_MARGIN, conf::window::minModalWidth));
|
|
||||||
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
||||||
|
|
||||||
vlayout->setSpacing(WIDGET_SPACING);
|
|
||||||
vlayout->setContentsMargins(WIDGET_MARGIN, TOP_WIDGET_MARGIN, WIDGET_MARGIN, WIDGET_MARGIN);
|
|
||||||
|
|
||||||
qRegisterMetaType<std::vector<DeviceInfo>>();
|
|
||||||
|
|
||||||
auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
|
|
||||||
connect(closeShortcut, &QShortcut::activated, this, &UserProfile::close);
|
|
||||||
connect(okBtn, &QPushButton::clicked, this, &UserProfile::close);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UserProfile::resetToDefaults()
|
|
||||||
{
|
|
||||||
avatar_->setLetter("X");
|
|
||||||
devices_->clear();
|
|
||||||
|
|
||||||
ignoreBtn_->show();
|
|
||||||
devices_->hide();
|
|
||||||
devicesLabel_->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UserProfile::init(const QString &userId, const QString &roomId)
|
|
||||||
{
|
|
||||||
resetToDefaults();
|
|
||||||
|
|
||||||
this->roomId_ = roomId;
|
|
||||||
|
|
||||||
auto displayName = cache::displayName(roomId, userId);
|
|
||||||
|
|
||||||
userIdLabel_->setText(userId);
|
|
||||||
displayNameLabel_->setText(displayName);
|
|
||||||
avatar_->setLetter(utils::firstChar(displayName));
|
|
||||||
|
|
||||||
avatar_->setImage(roomId, userId);
|
|
||||||
|
|
||||||
auto localUser = utils::localUser();
|
|
||||||
|
|
||||||
try {
|
|
||||||
bool hasMemberRights =
|
|
||||||
cache::hasEnoughPowerLevel({mtx::events::EventType::RoomMember},
|
|
||||||
roomId.toStdString(),
|
|
||||||
localUser.toStdString());
|
|
||||||
if (!hasMemberRights) {
|
|
||||||
kickBtn_->hide();
|
|
||||||
banBtn_->hide();
|
|
||||||
} else {
|
|
||||||
kickBtn_->show();
|
|
||||||
banBtn_->show();
|
|
||||||
}
|
|
||||||
} 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()] = {};
|
|
||||||
|
|
||||||
// A proxy object is used to emit the signal instead of the original object
|
|
||||||
// which might be destroyed by the time the http call finishes.
|
|
||||||
auto proxy = std::make_shared<Proxy>();
|
|
||||||
QObject::connect(proxy.get(), &Proxy::done, this, &UserProfile::updateDeviceList);
|
|
||||||
|
|
||||||
http::client()->query_keys(
|
|
||||||
req,
|
|
||||||
[user_id = userId.toStdString(), proxy = std::move(proxy)](
|
|
||||||
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 proxy->done(QString::fromStdString(user_id), deviceInfo);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UserProfile::updateDeviceList(const QString &user_id, const std::vector<DeviceInfo> &devices)
|
|
||||||
{
|
|
||||||
if (user_id != userIdLabel_->text())
|
|
||||||
return;
|
|
||||||
|
|
||||||
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();
|
|
||||||
adjustSize();
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QString>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
class Avatar;
|
|
||||||
class FlatButton;
|
|
||||||
class QLabel;
|
|
||||||
class QListWidget;
|
|
||||||
class Toggle;
|
|
||||||
|
|
||||||
struct DeviceInfo
|
|
||||||
{
|
|
||||||
QString device_id;
|
|
||||||
QString display_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Proxy : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void done(const QString &user_id, const std::vector<DeviceInfo> &devices);
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace dialogs {
|
|
||||||
|
|
||||||
class DeviceItem : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit DeviceItem(DeviceInfo device, QWidget *parent);
|
|
||||||
|
|
||||||
private:
|
|
||||||
DeviceInfo info_;
|
|
||||||
|
|
||||||
// Toggle *verifyToggle_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class UserProfile : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit UserProfile(QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
void init(const QString &userId, const QString &roomId);
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void updateDeviceList(const QString &user_id, const std::vector<DeviceInfo> &devices);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void resetToDefaults();
|
|
||||||
|
|
||||||
Avatar *avatar_;
|
|
||||||
QString roomId_;
|
|
||||||
|
|
||||||
QLabel *userIdLabel_;
|
|
||||||
QLabel *displayNameLabel_;
|
|
||||||
|
|
||||||
FlatButton *banBtn_;
|
|
||||||
FlatButton *kickBtn_;
|
|
||||||
FlatButton *ignoreBtn_;
|
|
||||||
FlatButton *startChat_;
|
|
||||||
|
|
||||||
QLabel *devicesLabel_;
|
|
||||||
QListWidget *devices_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // dialogs
|
|
|
@ -37,68 +37,6 @@ PopupItem::paintEvent(QPaintEvent *)
|
||||||
p.fillRect(rect(), hoverColor_);
|
p.fillRect(rect(), hoverColor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserItem::UserItem(QWidget *parent)
|
|
||||||
: PopupItem(parent)
|
|
||||||
{
|
|
||||||
userName_ = new QLabel("Placeholder", this);
|
|
||||||
avatar_->setLetter("P");
|
|
||||||
topLayout_->addWidget(avatar_);
|
|
||||||
topLayout_->addWidget(userName_, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
UserItem::UserItem(QWidget *parent, const QString &user_id)
|
|
||||||
: PopupItem(parent)
|
|
||||||
, userId_{user_id}
|
|
||||||
{
|
|
||||||
auto displayName = cache::displayName(ChatPage::instance()->currentRoom(), userId_);
|
|
||||||
|
|
||||||
avatar_->setLetter(utils::firstChar(displayName));
|
|
||||||
|
|
||||||
// If it's a matrix id we use the second letter.
|
|
||||||
if (displayName.size() > 1 && displayName.at(0) == '@')
|
|
||||||
avatar_->setLetter(QChar(displayName.at(1)));
|
|
||||||
|
|
||||||
userName_ = new QLabel(displayName, this);
|
|
||||||
|
|
||||||
topLayout_->addWidget(avatar_);
|
|
||||||
topLayout_->addWidget(userName_, 1);
|
|
||||||
|
|
||||||
resolveAvatar(user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UserItem::updateItem(const QString &user_id)
|
|
||||||
{
|
|
||||||
userId_ = user_id;
|
|
||||||
|
|
||||||
auto displayName = cache::displayName(ChatPage::instance()->currentRoom(), userId_);
|
|
||||||
|
|
||||||
// If it's a matrix id we use the second letter.
|
|
||||||
if (displayName.size() > 1 && displayName.at(0) == '@')
|
|
||||||
avatar_->setLetter(QChar(displayName.at(1)));
|
|
||||||
else
|
|
||||||
avatar_->setLetter(utils::firstChar(displayName));
|
|
||||||
|
|
||||||
userName_->setText(displayName);
|
|
||||||
resolveAvatar(user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UserItem::resolveAvatar(const QString &user_id)
|
|
||||||
{
|
|
||||||
avatar_->setImage(ChatPage::instance()->currentRoom(), user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UserItem::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
if (event->buttons() != Qt::RightButton)
|
|
||||||
emit clicked(
|
|
||||||
cache::displayName(ChatPage::instance()->currentRoom(), selectedText()));
|
|
||||||
|
|
||||||
QWidget::mousePressEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
RoomItem::RoomItem(QWidget *parent, const RoomSearchResult &res)
|
RoomItem::RoomItem(QWidget *parent, const RoomSearchResult &res)
|
||||||
: PopupItem(parent)
|
: PopupItem(parent)
|
||||||
, roomId_{QString::fromStdString(res.room_id)}
|
, roomId_{QString::fromStdString(res.room_id)}
|
||||||
|
|
|
@ -43,26 +43,6 @@ protected:
|
||||||
bool hovering_;
|
bool hovering_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UserItem : public PopupItem
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
UserItem(QWidget *parent);
|
|
||||||
UserItem(QWidget *parent, const QString &user_id);
|
|
||||||
QString selectedText() const { return userId_; }
|
|
||||||
void updateItem(const QString &user_id);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void resolveAvatar(const QString &user_id);
|
|
||||||
|
|
||||||
QLabel *userName_;
|
|
||||||
QString userId_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RoomItem : public PopupItem
|
class RoomItem : public PopupItem
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
Loading…
Reference in a new issue