forked from mirror/nheko
parent
ac1fbbb69f
commit
08028d5c57
@ -1,305 +0,0 @@ |
||||
#include <QHBoxLayout> |
||||
#include <QLabel> |
||||
#include <QListWidget> |
||||
#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()}; |
||||
|
||||
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(); |
||||
|
||||
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,69 +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_; |
||||
|
||||
QLabel *userIdLabel_; |
||||
QLabel *displayNameLabel_; |
||||
|
||||
FlatButton *banBtn_; |
||||
FlatButton *kickBtn_; |
||||
FlatButton *ignoreBtn_; |
||||
FlatButton *startChat_; |
||||
|
||||
QLabel *devicesLabel_; |
||||
QListWidget *devices_; |
||||
}; |
||||
|
||||
} // dialogs
|
@ -1,63 +0,0 @@ |
||||
#include "UserProfileModel.h" |
||||
#include <QModelIndex> |
||||
|
||||
UserProfileModel::UserProfileModel(QObject *parent) |
||||
: QAbstractListModel(parent) |
||||
, deviceList(nullptr) |
||||
{ |
||||
this->deviceList = new UserProfile(this); |
||||
|
||||
connect(this->deviceList, &UserProfile::userIdChanged, this, [this]() { |
||||
emit this->deviceList->updateDeviceList(); |
||||
}); |
||||
connect(this->deviceList, &UserProfile::deviceListUpdated, this, [this]() { |
||||
beginResetModel(); |
||||
this->beginInsertRows( |
||||
QModelIndex(), 0, this->deviceList->getDeviceList().size() - 1); |
||||
this->endInsertRows(); |
||||
endResetModel(); |
||||
}); |
||||
} |
||||
|
||||
int |
||||
UserProfileModel::rowCount(const QModelIndex &parent) const |
||||
{ |
||||
if (parent.isValid() || !this->deviceList) |
||||
return 0; |
||||
return this->deviceList->getDeviceList().size(); |
||||
} |
||||
|
||||
QVariant |
||||
UserProfileModel::data(const QModelIndex &index, int role) const |
||||
{ |
||||
if (!index.isValid() && |
||||
static_cast<int>(this->deviceList->getDeviceList().size()) <= index.row()) |
||||
return QVariant(); |
||||
|
||||
const DeviceInfo device = this->deviceList->getDeviceList().at(index.row()); |
||||
switch (role) { |
||||
case DEVICEID: |
||||
return QVariant(device.device_id); |
||||
case DISPLAYNAME: |
||||
return QVariant(device.display_name); |
||||
case VERIFIED_STATUS: |
||||
return device.verification_status; |
||||
} |
||||
return QVariant(); |
||||
} |
||||
|
||||
QHash<int, QByteArray> |
||||
UserProfileModel::roleNames() const |
||||
{ |
||||
QHash<int, QByteArray> names; |
||||
names[DEVICEID] = "deviceID"; |
||||
names[DISPLAYNAME] = "displayName"; |
||||
names[VERIFIED_STATUS] = "verified_status"; |
||||
return names; |
||||
} |
||||
|
||||
UserProfile * |
||||
UserProfileModel::getList() const |
||||
{ |
||||
return (this->deviceList); |
||||
} |
@ -1,30 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include "UserProfile.h" |
||||
#include <QAbstractListModel> |
||||
|
||||
class UserProfile; // forward declaration of the class UserProfile
|
||||
|
||||
class UserProfileModel : public QAbstractListModel |
||||
{ |
||||
Q_OBJECT |
||||
Q_PROPERTY(UserProfile *deviceList READ getList) |
||||
|
||||
public: |
||||
explicit UserProfileModel(QObject *parent = nullptr); |
||||
|
||||
enum |
||||
{ |
||||
DEVICEID, |
||||
DISPLAYNAME, |
||||
VERIFIED_STATUS |
||||
}; |
||||
UserProfile *getList() const; |
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
||||
QVariant data(const QModelIndex &index, int role) const override; |
||||
virtual QHash<int, QByteArray> roleNames() const override; |
||||
|
||||
private: |
||||
UserProfile *deviceList; |
||||
}; |
Loading…
Reference in new issue