mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
shifted room avatar changing
This commit is contained in:
parent
473b14ed0f
commit
a7d7d18e92
5 changed files with 242 additions and 23 deletions
|
@ -33,12 +33,53 @@ ApplicationWindow {
|
|||
spacing: 10
|
||||
|
||||
Avatar {
|
||||
url: ""
|
||||
url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
|
||||
height: 130
|
||||
width: 130
|
||||
displayName: ""
|
||||
userid: ""
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onClicked: {
|
||||
if(roomSettings.canChangeAvatar) {
|
||||
roomSettings.updateAvatar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
running: roomSettings.isLoading
|
||||
visible: roomSettings.isLoading
|
||||
}
|
||||
|
||||
Text {
|
||||
id: errorText
|
||||
text: "Error Text"
|
||||
color: "red"
|
||||
visible: opacity > 0
|
||||
opacity: 0
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: hideErrorAnimation
|
||||
running: false
|
||||
PauseAnimation {
|
||||
duration: 4000
|
||||
}
|
||||
NumberAnimation {
|
||||
target: errorText
|
||||
property: 'opacity'
|
||||
to: 0
|
||||
duration: 1000
|
||||
}
|
||||
}
|
||||
|
||||
Connections{
|
||||
target: roomSettings
|
||||
onDisplayError: {
|
||||
errorText.text = errorMessage
|
||||
errorText.opacity = 1
|
||||
hideErrorAnimation.restart()
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
|
|
|
@ -143,10 +143,10 @@ EditModal::applyClicked()
|
|||
}
|
||||
|
||||
using namespace mtx::events;
|
||||
auto proxy = std::make_shared<ThreadProxy>();
|
||||
connect(proxy.get(), &ThreadProxy::topicEventSent, this, &EditModal::topicEventSent);
|
||||
connect(proxy.get(), &ThreadProxy::nameEventSent, this, &EditModal::nameEventSent);
|
||||
connect(proxy.get(), &ThreadProxy::error, this, &EditModal::error);
|
||||
auto proxy = std::make_shared<ThreadProxya>();
|
||||
connect(proxy.get(), &ThreadProxya::topicEventSent, this, &EditModal::topicEventSent);
|
||||
connect(proxy.get(), &ThreadProxya::nameEventSent, this, &EditModal::nameEventSent);
|
||||
connect(proxy.get(), &ThreadProxya::error, this, &EditModal::error);
|
||||
|
||||
if (newName != initialName_ && !newName.isEmpty()) {
|
||||
state::Name body;
|
||||
|
@ -810,9 +810,9 @@ RoomSettingsOld::updateAvatar()
|
|||
|
||||
// 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, &RoomSettingsOld::displayErrorMessage);
|
||||
connect(proxy.get(), &ThreadProxy::avatarChanged, this, &RoomSettingsOld::setAvatar);
|
||||
auto proxy = std::make_shared<ThreadProxya>();
|
||||
connect(proxy.get(), &ThreadProxya::error, this, &RoomSettingsOld::displayErrorMessage);
|
||||
connect(proxy.get(), &ThreadProxya::avatarChanged, this, &RoomSettingsOld::setAvatar);
|
||||
|
||||
const auto bin = file.peek(file.size());
|
||||
const auto payload = std::string(bin.data(), bin.size());
|
||||
|
|
|
@ -40,7 +40,7 @@ protected:
|
|||
|
||||
/// Convenience class which connects events emmited from threads
|
||||
/// outside of main with the UI code.
|
||||
class ThreadProxy : public QObject
|
||||
class ThreadProxya : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#include "RoomSettings.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QImageReader>
|
||||
#include <QMimeDatabase>
|
||||
#include <QStandardPaths>
|
||||
#include <mtx/responses/common.hpp>
|
||||
#include <mtx/responses/media.hpp>
|
||||
|
||||
|
@ -84,6 +88,18 @@ RoomSettings::roomVersion() const
|
|||
return QString::fromStdString(info_.version);
|
||||
}
|
||||
|
||||
bool
|
||||
RoomSettings::isLoading() const
|
||||
{
|
||||
return isLoading_;
|
||||
}
|
||||
|
||||
QString
|
||||
RoomSettings::roomAvatarUrl()
|
||||
{
|
||||
return QString::fromStdString(info_.avatar_url);
|
||||
}
|
||||
|
||||
int
|
||||
RoomSettings::memberCount() const
|
||||
{
|
||||
|
@ -96,7 +112,6 @@ RoomSettings::retrieveRoomInfo()
|
|||
try {
|
||||
usesEncryption_ = cache::isRoomEncrypted(roomid_.toStdString());
|
||||
info_ = cache::singleRoomInfo(roomid_.toStdString());
|
||||
// setAvatar();
|
||||
} catch (const lmdb::error &) {
|
||||
nhlog::db()->warn("failed to retrieve room info from cache: {}",
|
||||
roomid_.toStdString());
|
||||
|
@ -143,9 +158,9 @@ RoomSettings::enableEncryption()
|
|||
room_id,
|
||||
err->matrix_error.error,
|
||||
status_code);
|
||||
//emit enableEncryptionError(
|
||||
// tr("Failed to enable encryption: %1")
|
||||
// .arg(QString::fromStdString(err->matrix_error.error)));
|
||||
emit displayError(
|
||||
tr("Failed to enable encryption: %1")
|
||||
.arg(QString::fromStdString(err->matrix_error.error)));
|
||||
usesEncryption_ = false;
|
||||
emit encryptionChanged();
|
||||
return;
|
||||
|
@ -172,6 +187,33 @@ RoomSettings::canChangeJoinRules() const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RoomSettings::canChangeNameAndTopic() const
|
||||
{
|
||||
try {
|
||||
return cache::hasEnoughPowerLevel({EventType::RoomName, EventType::RoomTopic},
|
||||
roomid_.toStdString(),
|
||||
utils::localUser().toStdString());
|
||||
} catch (const lmdb::error &e) {
|
||||
nhlog::db()->warn("lmdb error: {}", e.what());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RoomSettings::canChangeAvatar() const
|
||||
{
|
||||
try {
|
||||
return cache::hasEnoughPowerLevel(
|
||||
{EventType::RoomAvatar}, roomid_.toStdString(), utils::localUser().toStdString());
|
||||
} catch (const lmdb::error &e) {
|
||||
nhlog::db()->warn("lmdb error: {}", e.what());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
RoomSettings::isEncryptionEnabled() const
|
||||
{
|
||||
|
@ -269,8 +311,8 @@ RoomSettings::updateAccessRules(const std::string &room_id,
|
|||
const mtx::events::state::JoinRules &join_rule,
|
||||
const mtx::events::state::GuestAccess &guest_access)
|
||||
{
|
||||
// startLoadingSpinner();
|
||||
// resetErrorLabel();
|
||||
isLoading_ = true;
|
||||
emit loadingChanged();
|
||||
|
||||
http::client()->send_state_event(
|
||||
room_id,
|
||||
|
@ -281,8 +323,9 @@ RoomSettings::updateAccessRules(const std::string &room_id,
|
|||
nhlog::net()->warn("failed to send m.room.join_rule: {} {}",
|
||||
static_cast<int>(err->status_code),
|
||||
err->matrix_error.error);
|
||||
// emit showErrorMessage(QString::fromStdString(err->matrix_error.error));
|
||||
|
||||
emit displayError(QString::fromStdString(err->matrix_error.error));
|
||||
isLoading_ = false;
|
||||
emit loadingChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -294,13 +337,115 @@ RoomSettings::updateAccessRules(const std::string &room_id,
|
|||
nhlog::net()->warn("failed to send m.room.guest_access: {} {}",
|
||||
static_cast<int>(err->status_code),
|
||||
err->matrix_error.error);
|
||||
// emit showErrorMessage(
|
||||
// QString::fromStdString(err->matrix_error.error));
|
||||
emit displayError(
|
||||
QString::fromStdString(err->matrix_error.error));
|
||||
}
|
||||
|
||||
isLoading_ = false;
|
||||
emit loadingChanged();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
RoomSettings::stopLoading()
|
||||
{
|
||||
isLoading_ = false;
|
||||
emit loadingChanged();
|
||||
}
|
||||
|
||||
void
|
||||
RoomSettings::avatarChanged()
|
||||
{
|
||||
retrieveRoomInfo();
|
||||
emit avatarUrlChanged();
|
||||
}
|
||||
|
||||
void
|
||||
RoomSettings::updateAvatar()
|
||||
{
|
||||
const QString picturesFolder =
|
||||
QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
|
||||
const QString fileName = QFileDialog::getOpenFileName(
|
||||
nullptr, tr("Select an avatar"), picturesFolder, 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") {
|
||||
emit displayError(tr("The selected file is not an image"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
emit displayError(tr("Error while reading file: %1").arg(file.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading_ = true;
|
||||
emit loadingChanged();
|
||||
|
||||
// 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::displayError);
|
||||
connect(proxy.get(), &ThreadProxy::avatarChanged, this, &RoomSettings::avatarChanged);
|
||||
connect(proxy.get(), &ThreadProxy::stopLoading, this, &RoomSettings::stopLoading);
|
||||
|
||||
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 = roomid_.toStdString(),
|
||||
content = std::move(bin)](const mtx::responses::ContentURI &res,
|
||||
mtx::http::RequestErr err) {
|
||||
if (err) {
|
||||
emit proxy->stopLoading();
|
||||
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(
|
||||
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 signal that stops loading spinner and reset error label
|
||||
emit proxy->stopLoading();
|
||||
emit proxy->avatarChanged();
|
||||
});
|
||||
});
|
||||
}
|
|
@ -7,16 +7,34 @@
|
|||
|
||||
#include "CacheStructs.h"
|
||||
|
||||
/// 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();
|
||||
void nameEventSent(const QString &);
|
||||
void topicEventSent();
|
||||
void stopLoading();
|
||||
};
|
||||
|
||||
class RoomSettings : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString roomName READ roomName CONSTANT)
|
||||
Q_PROPERTY(QString roomId READ roomId CONSTANT)
|
||||
Q_PROPERTY(QString roomVersion READ roomVersion CONSTANT)
|
||||
Q_PROPERTY(QString roomAvatarUrl READ roomAvatarUrl NOTIFY avatarUrlChanged)
|
||||
Q_PROPERTY(int memberCount READ memberCount CONSTANT)
|
||||
Q_PROPERTY(int notifications READ notifications NOTIFY notificationsChanged)
|
||||
Q_PROPERTY(int accessJoinRules READ accessJoinRules NOTIFY accessJoinRulesChanged)
|
||||
Q_PROPERTY(bool isLoading READ isLoading NOTIFY loadingChanged)
|
||||
Q_PROPERTY(bool canChangeJoinRules READ canChangeJoinRules CONSTANT)
|
||||
Q_PROPERTY(bool canChangeNameAndTopic READ canChangeNameAndTopic CONSTANT)
|
||||
Q_PROPERTY(bool canChangeAvatar READ canChangeAvatar CONSTANT)
|
||||
Q_PROPERTY(bool isEncryptionEnabled READ isEncryptionEnabled NOTIFY encryptionChanged)
|
||||
Q_PROPERTY(bool respondsToKeyRequests READ respondsToKeyRequests NOTIFY keyRequestsChanged)
|
||||
|
||||
|
@ -26,24 +44,38 @@ public:
|
|||
QString roomName() const;
|
||||
QString roomId() const;
|
||||
QString roomVersion() const;
|
||||
QString roomAvatarUrl();
|
||||
int memberCount() const;
|
||||
int notifications();
|
||||
int accessJoinRules();
|
||||
bool respondsToKeyRequests();
|
||||
bool isLoading() const;
|
||||
//! Whether the user has enough power level to send m.room.join_rules events.
|
||||
bool canChangeJoinRules() const;
|
||||
//! Whether the user has enough power level to send m.room.name & m.room.topic events.
|
||||
bool canChangeNameAndTopic() const;
|
||||
//! Whether the user has enough power level to send m.room.avatar event.
|
||||
bool canChangeAvatar() const;
|
||||
bool isEncryptionEnabled() const;
|
||||
|
||||
Q_INVOKABLE void changeNotifications(int currentIndex);
|
||||
Q_INVOKABLE void changeAccessRules(int index);
|
||||
Q_INVOKABLE void changeKeyRequestsPreference(bool isOn);
|
||||
Q_INVOKABLE void enableEncryption();
|
||||
Q_INVOKABLE void updateAvatar();
|
||||
|
||||
signals:
|
||||
void notificationsChanged();
|
||||
void accessJoinRulesChanged();
|
||||
void keyRequestsChanged();
|
||||
void encryptionChanged();
|
||||
void avatarUrlChanged();
|
||||
void loadingChanged();
|
||||
void displayError(const QString &errorMessage);
|
||||
|
||||
public slots:
|
||||
void avatarChanged();
|
||||
void stopLoading();
|
||||
|
||||
private:
|
||||
void retrieveRoomInfo();
|
||||
|
@ -54,6 +86,7 @@ private:
|
|||
private:
|
||||
QString roomid_;
|
||||
bool usesEncryption_ = false;
|
||||
bool isLoading_ = false;
|
||||
RoomInfo info_;
|
||||
int notifications_ = 0;
|
||||
int accessRules_ = 0;
|
||||
|
|
Loading…
Reference in a new issue