diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8013fed9..d386efbf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -231,6 +231,7 @@ set(SRC_FILES
src/Logging.cpp
src/MainWindow.cpp
src/MatrixClient.cpp
+ src/MxcImageProvider.cpp
src/QuickSwitcher.cpp
src/Olm.cpp
src/RegisterPage.cpp
diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
new file mode 100644
index 00000000..9d7b54fe
--- /dev/null
+++ b/resources/qml/Avatar.qml
@@ -0,0 +1,45 @@
+import QtQuick 2.6
+import QtGraphicalEffects 1.0
+import Qt.labs.settings 1.0
+
+Rectangle {
+ id: avatar
+ width: 48
+ height: 48
+ radius: settings.avatar_circles ? height/2 : 3
+
+ Settings {
+ id: settings
+ category: "user"
+ property bool avatar_circles: true
+ }
+
+ property alias url: img.source
+ property string displayName
+
+ Text {
+ anchors.fill: parent
+ text: String.fromCodePoint(displayName.codePointAt(0))
+ color: colors.text
+ font.pixelSize: avatar.height/2
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ }
+
+ Image {
+ id: img
+ anchors.fill: parent
+ asynchronous: true
+
+ layer.enabled: true
+ layer.effect: OpacityMask {
+ maskSource: Rectangle {
+ anchors.fill: parent
+ width: avatar.width
+ height: avatar.height
+ radius: settings.avatar_circles ? height/2 : 3
+ }
+ }
+ }
+ color: colors.dark
+}
diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml
index 5f068e57..0151686a 100644
--- a/resources/qml/TimelineView.qml
+++ b/resources/qml/TimelineView.qml
@@ -181,10 +181,11 @@ Rectangle {
Row {
height: userName.height
spacing: 4
- Rectangle {
+ Avatar {
width: 48
height: 48
- color: "green"
+ url: chat.model.avatarUrl(section.split(" ")[0]).replace("mxc://", "image://MxcImage/")
+ displayName: chat.model.displayName(section.split(" ")[0])
}
Text {
diff --git a/resources/res.qrc b/resources/res.qrc
index b18835fb..6f6d480a 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -116,6 +116,7 @@
qml/TimelineView.qml
+ qml/Avatar.qml
qml/delegates/TextMessage.qml
qml/delegates/NoticeMessage.qml
diff --git a/src/MxcImageProvider.cpp b/src/MxcImageProvider.cpp
new file mode 100644
index 00000000..305439fc
--- /dev/null
+++ b/src/MxcImageProvider.cpp
@@ -0,0 +1,79 @@
+#include "MxcImageProvider.h"
+
+#include "Cache.h"
+
+void
+MxcImageResponse::run()
+{
+ if (m_requestedSize.isValid()) {
+ QString fileName = QString("%1_%2x%3")
+ .arg(m_id)
+ .arg(m_requestedSize.width())
+ .arg(m_requestedSize.height());
+
+ auto data = cache::client()->image(fileName);
+ if (!data.isNull() && m_image.loadFromData(data)) {
+ m_image = m_image.scaled(m_requestedSize, Qt::KeepAspectRatio);
+ m_image.setText("mxc url", "mxc://" + m_id);
+ emit finished();
+ return;
+ }
+
+ mtx::http::ThumbOpts opts;
+ opts.mxc_url = "mxc://" + m_id.toStdString();
+ opts.width = m_requestedSize.width() > 0 ? m_requestedSize.width() : -1;
+ opts.height = m_requestedSize.height() > 0 ? m_requestedSize.height() : -1;
+ opts.method = "scale";
+ http::client()->get_thumbnail(
+ opts, [this, fileName](const std::string &res, mtx::http::RequestErr err) {
+ if (err) {
+ nhlog::net()->error("Failed to download image {}",
+ m_id.toStdString());
+ m_error = "Failed download";
+ emit finished();
+
+ return;
+ }
+
+ auto data = QByteArray(res.data(), res.size());
+ cache::client()->saveImage(fileName, data);
+ m_image.loadFromData(data);
+ m_image = m_image.scaled(m_requestedSize, Qt::KeepAspectRatio);
+ m_image.setText("mxc url", "mxc://" + m_id);
+
+ emit finished();
+ });
+ } else {
+ auto data = cache::client()->image(m_id);
+ if (!data.isNull() && m_image.loadFromData(data)) {
+ m_image.setText("mxc url", "mxc://" + m_id);
+ emit finished();
+ return;
+ }
+
+ http::client()->download(
+ "mxc://" + m_id.toStdString(),
+ [this](const std::string &res,
+ const std::string &,
+ const std::string &originalFilename,
+ mtx::http::RequestErr err) {
+ if (err) {
+ nhlog::net()->error("Failed to download image {}",
+ m_id.toStdString());
+ m_error = "Failed download";
+ emit finished();
+
+ return;
+ }
+
+ auto data = QByteArray(res.data(), res.size());
+ m_image.loadFromData(data);
+ m_image.setText("original filename",
+ QString::fromStdString(originalFilename));
+ m_image.setText("mxc url", "mxc://" + m_id);
+ cache::client()->saveImage(m_id, data);
+
+ emit finished();
+ });
+ }
+}
diff --git a/src/MxcImageProvider.h b/src/MxcImageProvider.h
new file mode 100644
index 00000000..8710171c
--- /dev/null
+++ b/src/MxcImageProvider.h
@@ -0,0 +1,48 @@
+#pragma once
+
+#include
+#include
+
+#include
+#include
+
+class MxcImageResponse
+ : public QQuickImageResponse
+ , public QRunnable
+{
+public:
+ MxcImageResponse(const QString &id, const QSize &requestedSize)
+ : m_id(id)
+ , m_requestedSize(requestedSize)
+ {
+ setAutoDelete(false);
+ }
+
+ QQuickTextureFactory *textureFactory() const override
+ {
+ return QQuickTextureFactory::textureFactoryForImage(m_image);
+ }
+ QString errorString() const override { return m_error; }
+
+ void run() override;
+
+ QString m_id, m_error;
+ QSize m_requestedSize;
+ QImage m_image;
+};
+
+class MxcImageProvider : public QQuickAsyncImageProvider
+{
+public:
+ QQuickImageResponse *requestImageResponse(const QString &id,
+ const QSize &requestedSize) override
+ {
+ MxcImageResponse *response = new MxcImageResponse(id, requestedSize);
+ pool.start(response);
+ return response;
+ }
+
+private:
+ QThreadPool pool;
+};
+
diff --git a/src/RoomInfoListItem.cpp b/src/RoomInfoListItem.cpp
index 8aadbea2..f135451c 100644
--- a/src/RoomInfoListItem.cpp
+++ b/src/RoomInfoListItem.cpp
@@ -142,7 +142,7 @@ RoomInfoListItem::resizeEvent(QResizeEvent *)
void
RoomInfoListItem::paintEvent(QPaintEvent *event)
{
- bool rounded = QSettings().value("user/avatar/circles", true).toBool();
+ bool rounded = QSettings().value("user/avatar_circles", true).toBool();
Q_UNUSED(event);
diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index 9fd033e9..1caea449 100644
--- a/src/UserSettingsPage.cpp
+++ b/src/UserSettingsPage.cpp
@@ -53,7 +53,7 @@ UserSettings::load()
isReadReceiptsEnabled_ = settings.value("user/read_receipts", true).toBool();
theme_ = settings.value("user/theme", defaultTheme_).toString();
font_ = settings.value("user/font_family", "default").toString();
- avatarCircles_ = settings.value("user/avatar/circles", true).toBool();
+ avatarCircles_ = settings.value("user/avatar_circles", true).toBool();
emojiFont_ = settings.value("user/emoji_font_family", "default").toString();
baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
@@ -119,9 +119,7 @@ UserSettings::save()
settings.setValue("start_in_tray", isStartInTrayEnabled_);
settings.endGroup();
- settings.beginGroup("avatar");
- settings.setValue("circles", avatarCircles_);
- settings.endGroup();
+ settings.setValue("avatar_circles", avatarCircles_);
settings.setValue("font_size", baseFontSize_);
settings.setValue("typing_notifications", isTypingNotificationsEnabled_);
diff --git a/src/timeline2/TimelineModel.cpp b/src/timeline2/TimelineModel.cpp
index 28820205..310494b4 100644
--- a/src/timeline2/TimelineModel.cpp
+++ b/src/timeline2/TimelineModel.cpp
@@ -325,6 +325,12 @@ TimelineModel::displayName(QString id) const
return Cache::displayName(room_id_, id);
}
+QString
+TimelineModel::avatarUrl(QString id) const
+{
+ return Cache::avatarUrl(room_id_, id);
+}
+
QString
TimelineModel::formatDateSeparator(QDate date) const
{
diff --git a/src/timeline2/TimelineModel.h b/src/timeline2/TimelineModel.h
index e37c6542..954da5eb 100644
--- a/src/timeline2/TimelineModel.h
+++ b/src/timeline2/TimelineModel.h
@@ -90,6 +90,7 @@ public:
Q_INVOKABLE QColor userColor(QString id, QColor background);
Q_INVOKABLE QString displayName(QString id) const;
+ Q_INVOKABLE QString avatarUrl(QString id) const;
Q_INVOKABLE QString formatDateSeparator(QDate date) const;
Q_INVOKABLE QString escapeEmoji(QString str) const;
diff --git a/src/timeline2/TimelineViewManager.cpp b/src/timeline2/TimelineViewManager.cpp
index 0e0e74e4..eb9bea54 100644
--- a/src/timeline2/TimelineViewManager.cpp
+++ b/src/timeline2/TimelineViewManager.cpp
@@ -4,6 +4,7 @@
#include
#include "Logging.h"
+#include "MxcImageProvider.h"
TimelineViewManager::TimelineViewManager(QWidget *parent)
{
@@ -18,6 +19,7 @@ TimelineViewManager::TimelineViewManager(QWidget *parent)
container = QWidget::createWindowContainer(view, parent);
container->setMinimumSize(200, 200);
view->rootContext()->setContextProperty("timelineManager", this);
+ view->engine()->addImageProvider("MxcImage", new MxcImageProvider());
view->setSource(QUrl("qrc:///qml/TimelineView.qml"));
}
diff --git a/src/ui/Avatar.cpp b/src/ui/Avatar.cpp
index 501a8968..e4a90f81 100644
--- a/src/ui/Avatar.cpp
+++ b/src/ui/Avatar.cpp
@@ -101,7 +101,7 @@ Avatar::setIcon(const QIcon &icon)
void
Avatar::paintEvent(QPaintEvent *)
{
- bool rounded = QSettings().value("user/avatar/circles", true).toBool();
+ bool rounded = QSettings().value("user/avatar_circles", true).toBool();
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);