shifted room avatar changing

This commit is contained in:
Jedi18 2021-02-11 23:39:11 +05:30
parent 473b14ed0f
commit a7d7d18e92
5 changed files with 242 additions and 23 deletions

View file

@ -33,12 +33,53 @@ ApplicationWindow {
spacing: 10 spacing: 10
Avatar { Avatar {
url: "" url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/")
height: 130 height: 130
width: 130 width: 130
displayName: ""
userid: ""
Layout.alignment: Qt.AlignHCenter 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 { ColumnLayout {

View file

@ -143,10 +143,10 @@ EditModal::applyClicked()
} }
using namespace mtx::events; using namespace mtx::events;
auto proxy = std::make_shared<ThreadProxy>(); auto proxy = std::make_shared<ThreadProxya>();
connect(proxy.get(), &ThreadProxy::topicEventSent, this, &EditModal::topicEventSent); connect(proxy.get(), &ThreadProxya::topicEventSent, this, &EditModal::topicEventSent);
connect(proxy.get(), &ThreadProxy::nameEventSent, this, &EditModal::nameEventSent); connect(proxy.get(), &ThreadProxya::nameEventSent, this, &EditModal::nameEventSent);
connect(proxy.get(), &ThreadProxy::error, this, &EditModal::error); connect(proxy.get(), &ThreadProxya::error, this, &EditModal::error);
if (newName != initialName_ && !newName.isEmpty()) { if (newName != initialName_ && !newName.isEmpty()) {
state::Name body; state::Name body;
@ -810,9 +810,9 @@ RoomSettingsOld::updateAvatar()
// Events emitted from the http callbacks (different threads) will // Events emitted from the http callbacks (different threads) will
// be queued back into the UI thread through this proxy object. // be queued back into the UI thread through this proxy object.
auto proxy = std::make_shared<ThreadProxy>(); auto proxy = std::make_shared<ThreadProxya>();
connect(proxy.get(), &ThreadProxy::error, this, &RoomSettingsOld::displayErrorMessage); connect(proxy.get(), &ThreadProxya::error, this, &RoomSettingsOld::displayErrorMessage);
connect(proxy.get(), &ThreadProxy::avatarChanged, this, &RoomSettingsOld::setAvatar); connect(proxy.get(), &ThreadProxya::avatarChanged, this, &RoomSettingsOld::setAvatar);
const auto bin = file.peek(file.size()); const auto bin = file.peek(file.size());
const auto payload = std::string(bin.data(), bin.size()); const auto payload = std::string(bin.data(), bin.size());

View file

@ -40,7 +40,7 @@ protected:
/// Convenience class which connects events emmited from threads /// Convenience class which connects events emmited from threads
/// outside of main with the UI code. /// outside of main with the UI code.
class ThreadProxy : public QObject class ThreadProxya : public QObject
{ {
Q_OBJECT Q_OBJECT

View file

@ -1,5 +1,9 @@
#include "RoomSettings.h" #include "RoomSettings.h"
#include <QFileDialog>
#include <QImageReader>
#include <QMimeDatabase>
#include <QStandardPaths>
#include <mtx/responses/common.hpp> #include <mtx/responses/common.hpp>
#include <mtx/responses/media.hpp> #include <mtx/responses/media.hpp>
@ -84,6 +88,18 @@ RoomSettings::roomVersion() const
return QString::fromStdString(info_.version); return QString::fromStdString(info_.version);
} }
bool
RoomSettings::isLoading() const
{
return isLoading_;
}
QString
RoomSettings::roomAvatarUrl()
{
return QString::fromStdString(info_.avatar_url);
}
int int
RoomSettings::memberCount() const RoomSettings::memberCount() const
{ {
@ -96,7 +112,6 @@ RoomSettings::retrieveRoomInfo()
try { try {
usesEncryption_ = cache::isRoomEncrypted(roomid_.toStdString()); usesEncryption_ = cache::isRoomEncrypted(roomid_.toStdString());
info_ = cache::singleRoomInfo(roomid_.toStdString()); info_ = cache::singleRoomInfo(roomid_.toStdString());
// setAvatar();
} catch (const lmdb::error &) { } catch (const lmdb::error &) {
nhlog::db()->warn("failed to retrieve room info from cache: {}", nhlog::db()->warn("failed to retrieve room info from cache: {}",
roomid_.toStdString()); roomid_.toStdString());
@ -143,9 +158,9 @@ RoomSettings::enableEncryption()
room_id, room_id,
err->matrix_error.error, err->matrix_error.error,
status_code); status_code);
//emit enableEncryptionError( emit displayError(
// tr("Failed to enable encryption: %1") tr("Failed to enable encryption: %1")
// .arg(QString::fromStdString(err->matrix_error.error))); .arg(QString::fromStdString(err->matrix_error.error)));
usesEncryption_ = false; usesEncryption_ = false;
emit encryptionChanged(); emit encryptionChanged();
return; return;
@ -172,6 +187,33 @@ RoomSettings::canChangeJoinRules() const
return false; 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 bool
RoomSettings::isEncryptionEnabled() const 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::JoinRules &join_rule,
const mtx::events::state::GuestAccess &guest_access) const mtx::events::state::GuestAccess &guest_access)
{ {
// startLoadingSpinner(); isLoading_ = true;
// resetErrorLabel(); emit loadingChanged();
http::client()->send_state_event( http::client()->send_state_event(
room_id, room_id,
@ -281,8 +323,9 @@ RoomSettings::updateAccessRules(const std::string &room_id,
nhlog::net()->warn("failed to send m.room.join_rule: {} {}", nhlog::net()->warn("failed to send m.room.join_rule: {} {}",
static_cast<int>(err->status_code), static_cast<int>(err->status_code),
err->matrix_error.error); 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; return;
} }
@ -294,13 +337,115 @@ RoomSettings::updateAccessRules(const std::string &room_id,
nhlog::net()->warn("failed to send m.room.guest_access: {} {}", nhlog::net()->warn("failed to send m.room.guest_access: {} {}",
static_cast<int>(err->status_code), static_cast<int>(err->status_code),
err->matrix_error.error); err->matrix_error.error);
// emit showErrorMessage( emit displayError(
// QString::fromStdString(err->matrix_error.error)); 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; return;
} }
// emit signal that stops loading spinner and reset error label emit proxy->stopLoading();
emit proxy->avatarChanged();
}); });
}); });
} }

View file

@ -7,16 +7,34 @@
#include "CacheStructs.h" #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 class RoomSettings : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString roomName READ roomName CONSTANT) Q_PROPERTY(QString roomName READ roomName CONSTANT)
Q_PROPERTY(QString roomId READ roomId CONSTANT) Q_PROPERTY(QString roomId READ roomId CONSTANT)
Q_PROPERTY(QString roomVersion READ roomVersion 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 memberCount READ memberCount CONSTANT)
Q_PROPERTY(int notifications READ notifications NOTIFY notificationsChanged) Q_PROPERTY(int notifications READ notifications NOTIFY notificationsChanged)
Q_PROPERTY(int accessJoinRules READ accessJoinRules NOTIFY accessJoinRulesChanged) 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 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 isEncryptionEnabled READ isEncryptionEnabled NOTIFY encryptionChanged)
Q_PROPERTY(bool respondsToKeyRequests READ respondsToKeyRequests NOTIFY keyRequestsChanged) Q_PROPERTY(bool respondsToKeyRequests READ respondsToKeyRequests NOTIFY keyRequestsChanged)
@ -26,24 +44,38 @@ public:
QString roomName() const; QString roomName() const;
QString roomId() const; QString roomId() const;
QString roomVersion() const; QString roomVersion() const;
QString roomAvatarUrl();
int memberCount() const; int memberCount() const;
int notifications(); int notifications();
int accessJoinRules(); int accessJoinRules();
bool respondsToKeyRequests(); bool respondsToKeyRequests();
bool isLoading() const;
//! Whether the user has enough power level to send m.room.join_rules events. //! Whether the user has enough power level to send m.room.join_rules events.
bool canChangeJoinRules() const; 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; bool isEncryptionEnabled() const;
Q_INVOKABLE void changeNotifications(int currentIndex); Q_INVOKABLE void changeNotifications(int currentIndex);
Q_INVOKABLE void changeAccessRules(int index); Q_INVOKABLE void changeAccessRules(int index);
Q_INVOKABLE void changeKeyRequestsPreference(bool isOn); Q_INVOKABLE void changeKeyRequestsPreference(bool isOn);
Q_INVOKABLE void enableEncryption(); Q_INVOKABLE void enableEncryption();
Q_INVOKABLE void updateAvatar();
signals: signals:
void notificationsChanged(); void notificationsChanged();
void accessJoinRulesChanged(); void accessJoinRulesChanged();
void keyRequestsChanged(); void keyRequestsChanged();
void encryptionChanged(); void encryptionChanged();
void avatarUrlChanged();
void loadingChanged();
void displayError(const QString &errorMessage);
public slots:
void avatarChanged();
void stopLoading();
private: private:
void retrieveRoomInfo(); void retrieveRoomInfo();
@ -54,6 +86,7 @@ private:
private: private:
QString roomid_; QString roomid_;
bool usesEncryption_ = false; bool usesEncryption_ = false;
bool isLoading_ = false;
RoomInfo info_; RoomInfo info_;
int notifications_ = 0; int notifications_ = 0;
int accessRules_ = 0; int accessRules_ = 0;