diff --git a/src/dialogs/RoomSettingsOld.cpp b/src/dialogs/RoomSettingsOld.cpp
index 7eb34c20..bc34715e 100644
--- a/src/dialogs/RoomSettingsOld.cpp
+++ b/src/dialogs/RoomSettingsOld.cpp
@@ -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());
diff --git a/src/dialogs/RoomSettingsOld.h b/src/dialogs/RoomSettingsOld.h
index e517676a..ad8dd5bd 100644
--- a/src/dialogs/RoomSettingsOld.h
+++ b/src/dialogs/RoomSettingsOld.h
@@ -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
diff --git a/src/ui/RoomSettings.cpp b/src/ui/RoomSettings.cpp
index 49e48e40..3ff1d5d5 100644
--- a/src/ui/RoomSettings.cpp
+++ b/src/ui/RoomSettings.cpp
@@ -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;
@@ -173,6 +188,33 @@ RoomSettings::canChangeJoinRules() const
}
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
{
return usesEncryption_;
@@ -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();
});
});
}
\ No newline at end of file
diff --git a/src/ui/RoomSettings.h b/src/ui/RoomSettings.h
index f5cc043c..09295a58 100644
--- a/src/ui/RoomSettings.h
+++ b/src/ui/RoomSettings.h
@@ -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;
|