diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2e5f08724..78faf4557 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,8 +16,6 @@ add_library(neochat STATIC models/customemojimodel.h clipboard.cpp clipboard.h - matriximageprovider.cpp - matriximageprovider.h models/messageeventmodel.cpp models/messageeventmodel.h models/messagefiltermodel.cpp diff --git a/src/main.cpp b/src/main.cpp index a08f44cae..8fc5b90c1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,6 @@ #include "colorschemer.h" #include "controller.h" #include "logger.h" -#include "matriximageprovider.h" #include "neochatconfig.h" #include "roommanager.h" #include "windowcontroller.h" @@ -247,7 +247,6 @@ int main(int argc, char *argv[]) }); } - engine.addImageProvider(QLatin1String("mxc"), MatrixImageProvider::create(&engine, &engine)); engine.addImageProvider(QLatin1String("blurhash"), new BlurhashImageProvider); engine.load(QUrl(QStringLiteral("qrc:/org/kde/neochat/qml/main.qml"))); diff --git a/src/matriximageprovider.cpp b/src/matriximageprovider.cpp deleted file mode 100644 index 59b49aec0..000000000 --- a/src/matriximageprovider.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// SPDX-FileCopyrightText: 2018-2019 Black Hat -// SPDX-FileCopyrightText: 2019 Kitsune Ral -// SPDX-License-Identifier: GPL-3.0-only - -#include "matriximageprovider.h" - -#include -#include -#include -#include -#include - -#include - -#include "controller.h" -#include "neochatconnection.h" - -#include - -using namespace Quotient; - -ThumbnailResponse::ThumbnailResponse(QString id, QSize size, NeoChatConnection *connection) - : mediaId(std::move(id)) - , requestedSize(size) - , localFile(QStringLiteral("%1/image_provider/%2-%3x%4.png") - .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation), - mediaId, - QString::number(requestedSize.width()), - QString::number(requestedSize.height()))) - , m_connection(connection) - , errorStr("Image request hasn't started"_ls) -{ - if (requestedSize.isEmpty()) { - requestedSize.setHeight(100); - requestedSize.setWidth(100); - } - if (mediaId.count(QLatin1Char('/')) != 1) { - if (mediaId.startsWith(QLatin1Char('/'))) { - mediaId = mediaId.mid(1); - } else { - errorStr = i18n("Media id '%1' doesn't follow server/mediaId pattern", mediaId); - Q_EMIT finished(); - return; - } - } - - mediaId = mediaId.split(QLatin1Char('?'))[0]; - - QImage cachedImage; - if (cachedImage.load(localFile)) { - image = cachedImage; - errorStr.clear(); - Q_EMIT finished(); - return; - } - - if (!m_connection) { - qWarning() << "Current connection is null"; - return; - } - - // Execute a request on the main thread asynchronously - moveToThread(m_connection->thread()); - QMetaObject::invokeMethod(this, &ThumbnailResponse::startRequest, Qt::QueuedConnection); -} - -void ThumbnailResponse::startRequest() -{ - if (!m_connection) { - return; - } - // Runs in the main thread, not QML thread - Q_ASSERT(QThread::currentThread() == m_connection->thread()); - job = m_connection->getThumbnail(mediaId, requestedSize); - // Connect to any possible outcome including abandonment - // to make sure the QML thread is not left stuck forever. - connect(job, &BaseJob::finished, this, &ThumbnailResponse::prepareResult); -} - -void ThumbnailResponse::prepareResult() -{ - Q_ASSERT(QThread::currentThread() == job->thread()); - Q_ASSERT(job->error() != BaseJob::Pending); - { - QWriteLocker _(&lock); - if (job->error() == BaseJob::Success) { - image = job->thumbnail(); - - QString localPath = QFileInfo(localFile).absolutePath(); - QDir dir; - if (!dir.exists(localPath)) { - dir.mkpath(localPath); - } - - image.save(localFile); - - errorStr.clear(); - } else if (job->error() == BaseJob::Abandoned) { - errorStr = i18n("Image request has been cancelled"); - // qDebug() << "ThumbnailResponse: cancelled for" << mediaId; - } else { - errorStr = job->errorString(); - qWarning() << "ThumbnailResponse: no valid image for" << mediaId << "-" << errorStr; - } - job = nullptr; - } - Q_EMIT finished(); -} - -QQuickTextureFactory *ThumbnailResponse::textureFactory() const -{ - QReadLocker _(&lock); - return QQuickTextureFactory::textureFactoryForImage(image); -} - -QString ThumbnailResponse::errorString() const -{ - QReadLocker _(&lock); - return errorStr; -} - -QQuickImageResponse *MatrixImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize) -{ - return new ThumbnailResponse(id, requestedSize, m_connection); -} - -#include "moc_matriximageprovider.cpp" diff --git a/src/matriximageprovider.h b/src/matriximageprovider.h deleted file mode 100644 index 393ba928d..000000000 --- a/src/matriximageprovider.h +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-FileCopyrightText: 2018-2019 Black Hat -// SPDX-FileCopyrightText: 2019 Kitsune Ral -// SPDX-License-Identifier: GPL-3.0-only - -#pragma once - -#include - -#include - -#include - -class NeoChatConnection; - -/** - * @class ThumbnailResponse - * - * A QQuickImageResponse for an mxc image. - * - * @sa QQuickImageResponse - */ -class ThumbnailResponse : public QQuickImageResponse -{ - Q_OBJECT -public: - explicit ThumbnailResponse(QString mediaId, QSize requestedSize, NeoChatConnection *m_connection); - ~ThumbnailResponse() override = default; - -private Q_SLOTS: - void startRequest(); - void prepareResult(); - -private: - QString mediaId; - QSize requestedSize; - const QString localFile; - Quotient::MediaThumbnailJob *job = nullptr; - NeoChatConnection *m_connection; - - QImage image; - QString errorStr; - mutable QReadWriteLock lock; // Guards ONLY these two members above - - QQuickTextureFactory *textureFactory() const override; - QString errorString() const override; -}; - -/** - * @class MatrixImageProvider - * - * A QQuickAsyncImageProvider for mxc images. - * - * @sa QQuickAsyncImageProvider - */ -class MatrixImageProvider : public QQuickAsyncImageProvider -{ - Q_OBJECT - QML_ELEMENT - QML_SINGLETON - - Q_PROPERTY(NeoChatConnection *connection MEMBER m_connection) -public: - static MatrixImageProvider *create(QQmlEngine *engine, QJSEngine *) - { - static MatrixImageProvider *instance = new MatrixImageProvider; - engine->setObjectOwnership(instance, QQmlEngine::CppOwnership); - return instance; - } - - /** - * @brief Return a job to provide the image with the given ID. - * - * @sa QQuickAsyncImageProvider::requestImageResponse - */ - QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; - -private: - NeoChatConnection *m_connection = nullptr; - MatrixImageProvider() = default; -}; diff --git a/src/models/userdirectorylistmodel.cpp b/src/models/userdirectorylistmodel.cpp index 1f28c2651..6bc99305e 100644 --- a/src/models/userdirectorylistmodel.cpp +++ b/src/models/userdirectorylistmodel.cpp @@ -141,12 +141,10 @@ QVariant UserDirectoryListModel::data(const QModelIndex &index, int role) const return QStringLiteral("Unknown User"); } if (role == AvatarRole) { - auto avatarUrl = user.avatarUrl; - - if (avatarUrl.isEmpty()) { - return QString(); + if (user.avatarUrl.isEmpty() || user.avatarUrl.scheme() != QStringLiteral("mxc")) { + return QUrl(); } - return avatarUrl.url().remove(0, 6); + return m_connection->makeMediaUrl(user.avatarUrl); } if (role == UserIDRole) { return user.userId; diff --git a/src/neochatroom.cpp b/src/neochatroom.cpp index 062c0671d..6c0bc4faa 100644 --- a/src/neochatroom.cpp +++ b/src/neochatroom.cpp @@ -452,17 +452,18 @@ QVariantMap NeoChatRoom::getUser(User *user) const }; } -QString NeoChatRoom::avatarMediaId() const +QUrl NeoChatRoom::avatarMediaId() const { if (const auto avatar = Room::avatarMediaId(); !avatar.isEmpty()) { - return avatar; + return connection()->makeMediaUrl(QUrl(QStringLiteral("mxc://%1").arg(avatar))); } // Use the first (excluding self) user's avatar for direct chats const auto dcUsers = directChatUsers(); for (const auto u : dcUsers) { if (u != localUser()) { - return u->avatarMediaId(this); + const auto &avatar = u->avatarMediaId(this); + return avatar.isEmpty() ? QUrl() : connection()->makeMediaUrl(QUrl(QStringLiteral("mxc://%1").arg(avatar))); } } diff --git a/src/neochatroom.h b/src/neochatroom.h index bb68692f9..bd50bb9f7 100644 --- a/src/neochatroom.h +++ b/src/neochatroom.h @@ -110,7 +110,7 @@ class NeoChatRoom : public Quotient::Room /** * @brief The avatar image to be used for the room. */ - Q_PROPERTY(QString avatarMediaId READ avatarMediaId NOTIFY avatarChanged STORED false) + Q_PROPERTY(QUrl avatarMediaId READ avatarMediaId NOTIFY avatarChanged STORED false) /** * @brief Get a user object for the other person in a direct chat. @@ -501,7 +501,7 @@ public: */ [[nodiscard]] QString subtitleText(); - [[nodiscard]] QString avatarMediaId() const; + [[nodiscard]] QUrl avatarMediaId() const; Quotient::User *directChatRemoteUser() const; diff --git a/src/qml/AccountEditorPage.qml b/src/qml/AccountEditorPage.qml index 634969308..adae48765 100644 --- a/src/qml/AccountEditorPage.qml +++ b/src/qml/AccountEditorPage.qml @@ -32,8 +32,7 @@ FormCard.FormCardPage { implicitHeight: implicitWidth padding: 0 - - source: root.connection && root.connection.localUser.avatarMediaId ? ("image://mxc/" + root.connection.localUser.avatarMediaId) : "" + source: root.connection && root.connection.localUser.avatarMediaId ? root.connection.makeMediaUrl("mxc://" + root.connection.localUser.avatarMediaId) : "" name: root.connection.localUser.displayName onClicked: { diff --git a/src/qml/AccountsPage.qml b/src/qml/AccountsPage.qml index 1e341a136..77e33567f 100644 --- a/src/qml/AccountsPage.qml +++ b/src/qml/AccountsPage.qml @@ -38,7 +38,7 @@ FormCard.FormCardPage { contentItem: RowLayout { KirigamiComponents.Avatar { name: accountDelegate.connection.localUser.displayName - source: accountDelegate.connection.localUser.avatarMediaId ? ("image://mxc/" + accountDelegate.connection.localUser.avatarMediaId) : "" + source: accountDelegate.connection.localUser.avatarMediaId ? accountDelegate.connection.makeMediaUrl("mxc://" + accountDelegate.connection.localUser.avatarMediaId) : "" Layout.rightMargin: Kirigami.Units.largeSpacing implicitWidth: Kirigami.Units.iconSizes.medium diff --git a/src/qml/CollapsedRoomDelegate.qml b/src/qml/CollapsedRoomDelegate.qml index cb2432d2d..e726225e1 100644 --- a/src/qml/CollapsedRoomDelegate.qml +++ b/src/qml/CollapsedRoomDelegate.qml @@ -33,7 +33,7 @@ QQC2.ItemDelegate { visible: root.categoryVisible || filterText.length > 0 || Config.mergeRoomList contentItem: KirigamiComponents.Avatar { - source: root.avatar ? `image://mxc/${root.avatar}` : "" + source: root.avatar name: root.displayName sourceSize { diff --git a/src/qml/ContextMenu.qml b/src/qml/ContextMenu.qml index abc96ed1c..97e818363 100644 --- a/src/qml/ContextMenu.qml +++ b/src/qml/ContextMenu.qml @@ -166,7 +166,7 @@ Loader { spacing: Kirigami.Units.largeSpacing KirigamiComponents.Avatar { id: avatar - source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : "" + source: room.avatarMediaId name: room.displayName Layout.preferredWidth: Kirigami.Units.gridUnit * 3 Layout.preferredHeight: Kirigami.Units.gridUnit * 3 diff --git a/src/qml/DirectChatDrawerHeader.qml b/src/qml/DirectChatDrawerHeader.qml index 96d053c1b..651d63c0f 100644 --- a/src/qml/DirectChatDrawerHeader.qml +++ b/src/qml/DirectChatDrawerHeader.qml @@ -38,7 +38,7 @@ ColumnLayout { contentItem: KirigamiComponents.Avatar { name: root.room ? root.room.displayName : "" - source: root.room ? ("image://mxc/" + root.room.avatarMediaId) : "" + source: root.room ? root.room.avatarMediaId : "" Rectangle { visible: root.room.usesEncryption diff --git a/src/qml/General.qml b/src/qml/General.qml index a3a106ebe..645010cbd 100644 --- a/src/qml/General.qml +++ b/src/qml/General.qml @@ -35,7 +35,7 @@ FormCard.FormCardPage { id: avatar Layout.alignment: Qt.AlignRight name: room.name - source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : "" + source: room.avatarMediaId implicitWidth: Kirigami.Units.iconSizes.enormous implicitHeight: Kirigami.Units.iconSizes.enormous } diff --git a/src/qml/GroupChatDrawerHeader.qml b/src/qml/GroupChatDrawerHeader.qml index 830943eaa..ca1c78ad7 100644 --- a/src/qml/GroupChatDrawerHeader.qml +++ b/src/qml/GroupChatDrawerHeader.qml @@ -31,7 +31,7 @@ ColumnLayout { Layout.preferredHeight: Kirigami.Units.iconSizes.large name: root.room ? root.room.displayName : "" - source: root.room && root.room.avatarMediaId ? ("image://mxc/" + root.room.avatarMediaId) : "" + source: root.room ? root.room.avatarMediaId : "" Rectangle { visible: room.usesEncryption diff --git a/src/qml/InviteUserPage.qml b/src/qml/InviteUserPage.qml index 5b714a7c8..fa322f155 100644 --- a/src/qml/InviteUserPage.qml +++ b/src/qml/InviteUserPage.qml @@ -85,7 +85,7 @@ Kirigami.ScrollablePage { KirigamiComponents.Avatar { Layout.preferredWidth: Kirigami.Units.iconSizes.medium Layout.preferredHeight: Kirigami.Units.iconSizes.medium - source: delegate.avatar ? ("image://mxc/" + delegate.avatar) : "" + source: delegate.avatar name: delegate.name } diff --git a/src/qml/Permissions.qml b/src/qml/Permissions.qml index d35d2a8c8..fb9ff04c2 100644 --- a/src/qml/Permissions.qml +++ b/src/qml/Permissions.qml @@ -179,7 +179,7 @@ FormCard.FormCardPage { KirigamiComponents.Avatar { Layout.preferredWidth: Kirigami.Units.iconSizes.medium Layout.preferredHeight: Kirigami.Units.iconSizes.medium - source: userListItem.avatar ? ("image://" + userListItem.avatar) : "" + source: userListItem.avatar name: userListItem.name } diff --git a/src/qml/RoomDelegate.qml b/src/qml/RoomDelegate.qml index c542ac636..4c17d55df 100644 --- a/src/qml/RoomDelegate.qml +++ b/src/qml/RoomDelegate.qml @@ -55,7 +55,7 @@ Delegates.RoundedItemDelegate { spacing: Kirigami.Units.largeSpacing Components.Avatar { - source: root.avatar ? "image://mxc/" + root.avatar : "" + source: root.avatar name: root.displayName visible: Config.showAvatarInRoomDrawer implicitHeight: Kirigami.Units.gridUnit + (Config.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2) diff --git a/src/qml/SpaceDrawer.qml b/src/qml/SpaceDrawer.qml index 7b784f6ab..befdb5c03 100644 --- a/src/qml/SpaceDrawer.qml +++ b/src/qml/SpaceDrawer.qml @@ -98,7 +98,7 @@ QQC2.Control { Layout.maximumHeight: width - Kirigami.Units.smallSpacing text: displayName - source: avatar ? ("image://mxc/" + avatar) : "" + source: spaceDelegate.avatar onSelected: root.selectedSpaceId = roomId checked: root.selectedSpaceId === roomId diff --git a/src/qml/SpaceListContextMenu.qml b/src/qml/SpaceListContextMenu.qml index 0df44c1d3..7ad28db09 100644 --- a/src/qml/SpaceListContextMenu.qml +++ b/src/qml/SpaceListContextMenu.qml @@ -92,7 +92,7 @@ Loader { KirigamiComponents.Avatar { id: avatar - source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : "" + source: room.avatarMediaId Layout.preferredWidth: Kirigami.Units.gridUnit * 3 Layout.preferredHeight: Kirigami.Units.gridUnit * 3 Layout.alignment: Qt.AlignTop diff --git a/src/qml/StartChatPage.qml b/src/qml/StartChatPage.qml index 3526374b5..9c7d29f97 100644 --- a/src/qml/StartChatPage.qml +++ b/src/qml/StartChatPage.qml @@ -76,7 +76,7 @@ Kirigami.ScrollablePage { KirigamiComponents.Avatar { Layout.preferredWidth: Kirigami.Units.iconSizes.medium Layout.preferredHeight: Kirigami.Units.iconSizes.medium - source: delegate.avatar ? ("image://mxc/" + delegate.avatar) : "" + source: delegate.avatar name: delegate.name } diff --git a/src/qml/TimelineEndDelegate.qml b/src/qml/TimelineEndDelegate.qml index 82e98873f..5a13ca882 100644 --- a/src/qml/TimelineEndDelegate.qml +++ b/src/qml/TimelineEndDelegate.qml @@ -30,7 +30,7 @@ TimelineDelegate { Layout.preferredHeight: Kirigami.Units.iconSizes.large name: root.room ? root.room.displayName : "" - source: root.room && root.room.avatarMediaId ? ("image://mxc/" + root.room.avatarMediaId) : "" + source: root.room ? root.room.avatarMediaId : "" Rectangle { visible: room.usesEncryption diff --git a/src/qml/UserInfo.qml b/src/qml/UserInfo.qml index 758720ee0..3cde201ed 100644 --- a/src/qml/UserInfo.qml +++ b/src/qml/UserInfo.qml @@ -63,9 +63,7 @@ RowLayout { text: i18n("Edit this account") contentItem: KirigamiComponents.Avatar { - readonly property string mediaId: root.connection.localUser.avatarMediaId - - source: mediaId ? ("image://mxc/" + mediaId) : "" + source: root.connection.localUser.avatarMediaId ? root.connection.makeMediaUrl("mxc://" + root.connection.localUser.avatarMediaId) : "" name: root.connection.localUser.displayName ?? root.connection.localUser.id } } @@ -249,7 +247,7 @@ RowLayout { width: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing height: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing } - source: userDelegate.connection.localUser.avatarMediaId ? ("image://mxc/" + userDelegate.connection.localUser.avatarMediaId) : "" + source: userDelegate.connection.localUser.avatarMediaId ? userDelegate.connection.makeMediaUrl("mxc://" + userDelegate.connection.localUser.avatarMediaId) : "" name: userDelegate.connection.localUser.displayName ?? userDelegate.connection.localUser.id } diff --git a/src/qml/main.qml b/src/qml/main.qml index cddddd5cb..e8fb0ed0b 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -50,7 +50,6 @@ Kirigami.ApplicationWindow { onConnectionChanged: { CustomEmojiModel.connection = root.connection - MatrixImageProvider.connection = root.connection RoomManager.connection = root.connection SpaceHierarchyCache.connection = root.connection } @@ -216,7 +215,6 @@ Kirigami.ApplicationWindow { Component.onCompleted: { CustomEmojiModel.connection = root.connection - MatrixImageProvider.connection = root.connection RoomManager.connection = root.connection SpaceHierarchyCache.connection = root.connection WindowController.setBlur(pageStack, Config.blur && !Config.compactLayout);