Compare commits
1 Commits
release/24
...
work/tobia
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de3d3abc53 |
@@ -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
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <QIcon>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QNetworkProxyFactory>
|
||||
#include <QNetworkReply>
|
||||
#include <QObject>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
@@ -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")));
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2019 Kitsune Ral <kitsune-ral@users.sf.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include "matriximageprovider.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
#include <QThread>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include "controller.h"
|
||||
#include "neochatconnection.h"
|
||||
|
||||
#include <Quotient/connection.h>
|
||||
|
||||
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"
|
||||
@@ -1,80 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2019 Kitsune Ral <kitsune-ral@users.sf.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QQuickAsyncImageProvider>
|
||||
|
||||
#include <Quotient/jobs/mediathumbnailjob.h>
|
||||
|
||||
#include <QReadWriteLock>
|
||||
|
||||
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;
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user