mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 20:48:52 +03:00
parent
db9c37d336
commit
56ee290b03
3 changed files with 181 additions and 10 deletions
|
@ -1,7 +1,10 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QImageReader>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QMimeDatabase>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
#include <QShowEvent>
|
#include <QShowEvent>
|
||||||
|
@ -356,6 +359,103 @@ RoomSettings::RoomSettings(const QString &room_id, QWidget *parent)
|
||||||
else
|
else
|
||||||
avatar_->setImage(avatarImg_);
|
avatar_->setImage(avatarImg_);
|
||||||
|
|
||||||
|
if (canChangeAvatar(room_id_.toStdString(), utils::localUser().toStdString())) {
|
||||||
|
auto filter = new ClickableFilter(this);
|
||||||
|
avatar_->installEventFilter(filter);
|
||||||
|
avatar_->setCursor(Qt::PointingHandCursor);
|
||||||
|
connect(filter, &ClickableFilter::clicked, this, [this]() {
|
||||||
|
const auto fileName = QFileDialog::getOpenFileName(
|
||||||
|
this, tr("Select an avatar"), "", tr("All Files (*)"));
|
||||||
|
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QMimeDatabase db;
|
||||||
|
QMimeType mime = db.mimeTypeForFile(fileName, QMimeDatabase::MatchContent);
|
||||||
|
|
||||||
|
const auto format = mime.name().split("/")[0];
|
||||||
|
|
||||||
|
QFile file{fileName, this};
|
||||||
|
if (format != "image") {
|
||||||
|
displayErrorMessage(tr("The selected media is not an image"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly)) {
|
||||||
|
displayErrorMessage(
|
||||||
|
tr("Error while reading media: %1").arg(file.errorString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spinner_) {
|
||||||
|
startLoadingSpinner();
|
||||||
|
resetErrorLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events emitted from the http callbacks (different threads) will
|
||||||
|
// be queued back into the UI thread through this proxy object.
|
||||||
|
auto proxy = std::make_shared<ThreadProxy>();
|
||||||
|
connect(proxy.get(),
|
||||||
|
&ThreadProxy::error,
|
||||||
|
this,
|
||||||
|
&RoomSettings::displayErrorMessage);
|
||||||
|
connect(
|
||||||
|
proxy.get(), &ThreadProxy::avatarChanged, this, &RoomSettings::setAvatar);
|
||||||
|
|
||||||
|
const auto bin = file.peek(file.size());
|
||||||
|
const auto payload = std::string(bin.data(), bin.size());
|
||||||
|
const auto dimensions = QImageReader(&file).size();
|
||||||
|
|
||||||
|
// First we need to create a new mxc URI
|
||||||
|
// (i.e upload media to the Matrix content repository) for the new avatar.
|
||||||
|
http::client()->upload(
|
||||||
|
payload,
|
||||||
|
mime.name().toStdString(),
|
||||||
|
QFileInfo(fileName).fileName().toStdString(),
|
||||||
|
[proxy = std::move(proxy),
|
||||||
|
dimensions,
|
||||||
|
payload,
|
||||||
|
mimetype = mime.name().toStdString(),
|
||||||
|
size = payload.size(),
|
||||||
|
room_id = room_id_.toStdString(),
|
||||||
|
content = std::move(bin)](const mtx::responses::ContentURI &res,
|
||||||
|
mtx::http::RequestErr err) {
|
||||||
|
if (err) {
|
||||||
|
emit proxy->error(tr("Failed to upload image: %s")
|
||||||
|
.arg(QString::fromStdString(
|
||||||
|
err->matrix_error.error)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace mtx::events;
|
||||||
|
state::Avatar avatar_event;
|
||||||
|
avatar_event.image_info.w = dimensions.width();
|
||||||
|
avatar_event.image_info.h = dimensions.height();
|
||||||
|
avatar_event.image_info.mimetype = mimetype;
|
||||||
|
avatar_event.image_info.size = size;
|
||||||
|
avatar_event.url = res.content_uri;
|
||||||
|
|
||||||
|
http::client()
|
||||||
|
->send_state_event<state::Avatar, EventType::RoomAvatar>(
|
||||||
|
room_id,
|
||||||
|
avatar_event,
|
||||||
|
[content = std::move(content),
|
||||||
|
proxy = std::move(proxy)](const mtx::responses::EventId &,
|
||||||
|
mtx::http::RequestErr err) {
|
||||||
|
if (err) {
|
||||||
|
emit proxy->error(
|
||||||
|
tr("Failed to upload image: %s")
|
||||||
|
.arg(QString::fromStdString(
|
||||||
|
err->matrix_error.error)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit proxy->avatarChanged(QImage::fromData(content));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
auto roomNameLabel = new QLabel(QString::fromStdString(info_.name), this);
|
auto roomNameLabel = new QLabel(QString::fromStdString(info_.name), this);
|
||||||
roomNameLabel->setFont(doubleFont);
|
roomNameLabel->setFont(doubleFont);
|
||||||
|
|
||||||
|
@ -537,6 +637,19 @@ RoomSettings::canChangeNameAndTopic(const std::string &room_id, const std::strin
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
RoomSettings::canChangeAvatar(const std::string &room_id, const std::string &user_id) const
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return cache::client()->hasEnoughPowerLevel(
|
||||||
|
{EventType::RoomAvatar}, room_id, user_id);
|
||||||
|
} catch (const lmdb::error &e) {
|
||||||
|
nhlog::db()->warn("lmdb error: {}", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RoomSettings::updateAccessRules(const std::string &room_id,
|
RoomSettings::updateAccessRules(const std::string &room_id,
|
||||||
const mtx::events::state::JoinRules &join_rule,
|
const mtx::events::state::JoinRules &join_rule,
|
||||||
|
@ -596,6 +709,26 @@ RoomSettings::startLoadingSpinner()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RoomSettings::displayErrorMessage(const QString &msg)
|
||||||
|
{
|
||||||
|
stopLoadingSpinner();
|
||||||
|
|
||||||
|
errorLabel_->show();
|
||||||
|
errorLabel_->setText(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RoomSettings::setAvatar(const QImage &img)
|
||||||
|
{
|
||||||
|
stopLoadingSpinner();
|
||||||
|
|
||||||
|
avatarImg_ = img;
|
||||||
|
|
||||||
|
if (avatar_)
|
||||||
|
avatar_->setImage(img);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
RoomSettings::resetErrorLabel()
|
RoomSettings::resetErrorLabel()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QEvent>
|
||||||
#include <QFrame>
|
#include <QFrame>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
|
||||||
|
@ -19,6 +20,41 @@ class TextField;
|
||||||
class TextField;
|
class TextField;
|
||||||
class Toggle;
|
class Toggle;
|
||||||
|
|
||||||
|
class ClickableFilter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ClickableFilter(QWidget *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void clicked();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject *obj, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::MouseButtonRelease) {
|
||||||
|
emit clicked();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QObject::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Convenience class which connects events emmited from threads
|
||||||
|
/// outside of main with the UI code.
|
||||||
|
class ThreadProxy : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void error(const QString &msg);
|
||||||
|
void avatarChanged(const QImage &img);
|
||||||
|
};
|
||||||
|
|
||||||
class EditModal : public QWidget
|
class EditModal : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -71,36 +107,39 @@ private:
|
||||||
bool canChangeJoinRules(const std::string &room_id, const std::string &user_id) const;
|
bool canChangeJoinRules(const std::string &room_id, const std::string &user_id) const;
|
||||||
//! Whether the user has enough power level to send m.room.name & m.room.topic events.
|
//! Whether the user has enough power level to send m.room.name & m.room.topic events.
|
||||||
bool canChangeNameAndTopic(const std::string &room_id, const std::string &user_id) const;
|
bool canChangeNameAndTopic(const std::string &room_id, const std::string &user_id) const;
|
||||||
|
//! Whether the user has enough power level to send m.room.avatar event.
|
||||||
|
bool canChangeAvatar(const std::string &room_id, const std::string &user_id) const;
|
||||||
void updateAccessRules(const std::string &room_id,
|
void updateAccessRules(const std::string &room_id,
|
||||||
const mtx::events::state::JoinRules &,
|
const mtx::events::state::JoinRules &,
|
||||||
const mtx::events::state::GuestAccess &);
|
const mtx::events::state::GuestAccess &);
|
||||||
void stopLoadingSpinner();
|
void stopLoadingSpinner();
|
||||||
void startLoadingSpinner();
|
void startLoadingSpinner();
|
||||||
void resetErrorLabel();
|
void resetErrorLabel();
|
||||||
|
void displayErrorMessage(const QString &msg);
|
||||||
|
|
||||||
void setAvatar(const QImage &img) { avatarImg_ = img; }
|
void setAvatar(const QImage &img);
|
||||||
void setupEditButton();
|
void setupEditButton();
|
||||||
//! Retrieve the current room information from cache.
|
//! Retrieve the current room information from cache.
|
||||||
void retrieveRoomInfo();
|
void retrieveRoomInfo();
|
||||||
void enableEncryption();
|
void enableEncryption();
|
||||||
|
|
||||||
Avatar *avatar_;
|
Avatar *avatar_ = nullptr;
|
||||||
|
|
||||||
bool usesEncryption_ = false;
|
bool usesEncryption_ = false;
|
||||||
QHBoxLayout *btnLayout_;
|
QHBoxLayout *btnLayout_;
|
||||||
|
|
||||||
FlatButton *editFieldsBtn_;
|
FlatButton *editFieldsBtn_ = nullptr;
|
||||||
|
|
||||||
RoomInfo info_;
|
RoomInfo info_;
|
||||||
QString room_id_;
|
QString room_id_;
|
||||||
QImage avatarImg_;
|
QImage avatarImg_;
|
||||||
|
|
||||||
QLabel *errorLabel_;
|
QLabel *errorLabel_ = nullptr;
|
||||||
LoadingIndicator *spinner_;
|
LoadingIndicator *spinner_ = nullptr;
|
||||||
|
|
||||||
QComboBox *accessCombo;
|
QComboBox *accessCombo = nullptr;
|
||||||
Toggle *encryptionToggle_;
|
Toggle *encryptionToggle_ = nullptr;
|
||||||
Toggle *keyRequestsToggle_;
|
Toggle *keyRequestsToggle_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // dialogs
|
} // dialogs
|
||||||
|
|
|
@ -639,18 +639,17 @@ TimelineItem::generateUserName(const QString &user_id, const QString &displaynam
|
||||||
|
|
||||||
auto filter = new UserProfileFilter(user_id, userName_);
|
auto filter = new UserProfileFilter(user_id, userName_);
|
||||||
userName_->installEventFilter(filter);
|
userName_->installEventFilter(filter);
|
||||||
|
userName_->setCursor(Qt::PointingHandCursor);
|
||||||
|
|
||||||
connect(filter, &UserProfileFilter::hoverOn, this, [this]() {
|
connect(filter, &UserProfileFilter::hoverOn, this, [this]() {
|
||||||
QFont f = userName_->font();
|
QFont f = userName_->font();
|
||||||
f.setUnderline(true);
|
f.setUnderline(true);
|
||||||
userName_->setCursor(Qt::PointingHandCursor);
|
|
||||||
userName_->setFont(f);
|
userName_->setFont(f);
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(filter, &UserProfileFilter::hoverOff, this, [this]() {
|
connect(filter, &UserProfileFilter::hoverOff, this, [this]() {
|
||||||
QFont f = userName_->font();
|
QFont f = userName_->font();
|
||||||
f.setUnderline(false);
|
f.setUnderline(false);
|
||||||
userName_->setCursor(Qt::ArrowCursor);
|
|
||||||
userName_->setFont(f);
|
userName_->setFont(f);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue