diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 79cf5184..968ec3c7 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -801,7 +801,10 @@ TimelineModel::viewDecryptedRawMessage(QString id) const
void
TimelineModel::openUserProfile(QString userid, bool global)
{
- emit openProfile(new UserProfile(global ? "" : room_id_, userid, manager_, this));
+ UserProfile *userProfile = new UserProfile(global ? "" : room_id_, userid, manager_, this);
+ connect(
+ this, &TimelineModel::roomAvatarUrlChanged, userProfile, &UserProfile::updateAvatarUrl);
+ emit openProfile(userProfile);
}
void
diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp
index de02bf5e..274ed927 100644
--- a/src/ui/UserProfile.cpp
+++ b/src/ui/UserProfile.cpp
@@ -1,14 +1,17 @@
-#include "UserProfile.h"
+#include <QFileDialog>
+#include <QImageReader>
+#include <QMimeDatabase>
+#include <QStandardPaths>
+
#include "Cache_p.h"
#include "ChatPage.h"
#include "DeviceVerificationFlow.h"
#include "Logging.h"
+#include "UserProfile.h"
#include "Utils.h"
#include "mtx/responses/crypto.hpp"
#include "timeline/TimelineModel.h"
#include "timeline/TimelineViewManager.h"
-#include <mtx/responses.hpp>
-#include <mtx/responses/common.hpp>
UserProfile::UserProfile(QString roomid,
QString userid,
@@ -21,6 +24,7 @@ UserProfile::UserProfile(QString roomid,
, model(parent)
{
fetchDeviceList(this->userid_);
+ globalAvatarUrl = "";
connect(cache::client(),
&Cache::verificationStatusChanged,
@@ -53,16 +57,9 @@ UserProfile::UserProfile(QString roomid,
&UserProfile::setGlobalUsername,
Qt::QueuedConnection);
- http::client()->get_profile(
- userid_.toStdString(),
- [this](const mtx::responses::Profile &res, mtx::http::RequestErr err) {
- if (err) {
- nhlog::net()->warn("failed to retrieve own profile info");
- return;
- }
-
- emit globalUsernameRetrieved(QString::fromStdString(res.display_name));
- });
+ if (isGlobalUserProfile()) {
+ getGlobalProfileData();
+ }
}
QHash<int, QByteArray>
@@ -122,7 +119,10 @@ UserProfile::displayName()
QString
UserProfile::avatarUrl()
{
- return cache::avatarUrl(roomid_, userid_);
+ return (isGlobalUserProfile() && globalAvatarUrl != "")
+ ? globalAvatarUrl
+ : cache::avatarUrl(roomid_, userid_);
+ ;
}
bool
@@ -260,15 +260,7 @@ UserProfile::changeUsername(QString username)
.toStdString();
member.membership = mtx::events::state::Membership::Join;
- http::client()->send_state_event(
- roomid_.toStdString(),
- http::client()->user_id().to_string(),
- member,
- [](mtx::responses::EventId, mtx::http::RequestErr err) {
- if (err)
- nhlog::net()->error("Failed to set room displayname: {}",
- err->matrix_error.error);
- });
+ updateRoomMemberState(std::move(member));
}
}
@@ -294,3 +286,126 @@ UserProfile::setGlobalUsername(const QString &globalUser)
globalUsername = globalUser;
emit displayNameChanged();
}
+
+void
+UserProfile::changeAvatar()
+{
+ 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;
+ }
+
+ const auto bin = file.peek(file.size());
+ const auto payload = std::string(bin.data(), bin.size());
+ const auto dimensions = QImageReader(&file).size();
+
+ isLoading_ = true;
+ emit loadingChanged();
+
+ // 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(),
+ [this,
+ 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) {
+ nhlog::ui()->error("Failed to upload image", err->matrix_error.error);
+ return;
+ }
+
+ if (isGlobalUserProfile()) {
+ http::client()->set_avatar_url(
+ res.content_uri, [this](mtx::http::RequestErr err) {
+ if (err) {
+ nhlog::ui()->error("Failed to set user avatar url",
+ err->matrix_error.error);
+ }
+
+ isLoading_ = false;
+ emit loadingChanged();
+ getGlobalProfileData();
+ });
+ } else {
+ // change room username
+ mtx::events::state::Member member;
+ member.display_name = cache::displayName(roomid_, userid_).toStdString();
+ member.avatar_url = res.content_uri;
+ member.membership = mtx::events::state::Membership::Join;
+
+ updateRoomMemberState(std::move(member));
+ }
+ });
+}
+
+void
+UserProfile::updateRoomMemberState(mtx::events::state::Member member)
+{
+ http::client()->send_state_event(
+ roomid_.toStdString(),
+ http::client()->user_id().to_string(),
+ member,
+ [this](mtx::responses::EventId, mtx::http::RequestErr err) {
+ if (err)
+ nhlog::net()->error("Failed to update room member state : ",
+ err->matrix_error.error);
+ });
+}
+
+void
+UserProfile::updateAvatarUrl()
+{
+ isLoading_ = false;
+ emit loadingChanged();
+
+ emit avatarUrlChanged();
+}
+
+bool
+UserProfile::isLoading() const
+{
+ return isLoading_;
+}
+
+void
+UserProfile::getGlobalProfileData()
+{
+ http::client()->get_profile(
+ userid_.toStdString(),
+ [this](const mtx::responses::Profile &res, mtx::http::RequestErr err) {
+ if (err) {
+ nhlog::net()->warn("failed to retrieve own profile info");
+ return;
+ }
+
+ emit globalUsernameRetrieved(QString::fromStdString(res.display_name));
+ globalAvatarUrl = QString::fromStdString(res.avatar_url);
+ emit avatarUrlChanged();
+ });
+}
diff --git a/src/ui/UserProfile.h b/src/ui/UserProfile.h
index 11f588b6..ffc5dcae 100644
--- a/src/ui/UserProfile.h
+++ b/src/ui/UserProfile.h
@@ -4,6 +4,8 @@
#include <QObject>
#include <QString>
#include <QVector>
+#include <mtx/responses.hpp>
+#include <mtx/responses/common.hpp>
namespace verification {
Q_NAMESPACE
@@ -81,10 +83,11 @@ class UserProfile : public QObject
Q_OBJECT
Q_PROPERTY(QString displayName READ displayName NOTIFY displayNameChanged)
Q_PROPERTY(QString userid READ userid CONSTANT)
- Q_PROPERTY(QString avatarUrl READ avatarUrl CONSTANT)
+ Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged)
Q_PROPERTY(DeviceInfoModel *deviceList READ deviceList CONSTANT)
Q_PROPERTY(bool isGlobalUserProfile READ isGlobalUserProfile CONSTANT)
Q_PROPERTY(bool isUserVerified READ getUserStatus NOTIFY userStatusChanged)
+ Q_PROPERTY(bool isLoading READ isLoading NOTIFY loadingChanged)
Q_PROPERTY(
bool userVerificationEnabled READ userVerificationEnabled NOTIFY userStatusChanged)
Q_PROPERTY(bool isSelf READ isSelf CONSTANT)
@@ -103,6 +106,7 @@ public:
bool getUserStatus();
bool userVerificationEnabled() const;
bool isSelf() const;
+ bool isLoading() const;
Q_INVOKABLE void verify(QString device = "");
Q_INVOKABLE void unverify(QString device = "");
@@ -112,21 +116,34 @@ public:
Q_INVOKABLE void kickUser();
Q_INVOKABLE void startChat();
Q_INVOKABLE void changeUsername(QString username);
+ Q_INVOKABLE void changeAvatar();
signals:
void userStatusChanged();
+ void loadingChanged();
void displayNameChanged();
+ void avatarUrlChanged();
+ void displayError(const QString &errorMessage);
void globalUsernameRetrieved(const QString &globalUser);
+public slots:
+ void updateAvatarUrl();
+
protected slots:
void setGlobalUsername(const QString &globalUser);
private:
+ void updateRoomMemberState(mtx::events::state::Member member);
+ void getGlobalProfileData();
+
+private:
QString roomid_, userid_;
QString globalUsername;
+ QString globalAvatarUrl;
DeviceInfoModel deviceList_;
bool isUserVerified = false;
bool hasMasterKey = false;
+ bool isLoading_ = false;
TimelineViewManager *manager;
TimelineModel *model;
};
|