diff options
-rw-r--r-- | src/timeline/InputBar.h | 15 | ||||
-rw-r--r-- | src/timeline/TimelineViewManager.cpp | 128 | ||||
-rw-r--r-- | src/timeline/TimelineViewManager.h | 45 |
3 files changed, 109 insertions, 79 deletions
diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h index d4fcfacf..9db16bae 100644 --- a/src/timeline/InputBar.h +++ b/src/timeline/InputBar.h @@ -41,14 +41,6 @@ public: connect(&typingTimeout_, &QTimer::timeout, this, &InputBar::stopTyping); } - void image(const QString &filename, - const std::optional<mtx::crypto::EncryptedFile> &file, - const QString &url, - const QString &mime, - uint64_t dsize, - const QSize &dimensions, - const QString &blurhash); - public slots: QString text() const; QString previousText(); @@ -78,6 +70,13 @@ private: void emote(QString body, bool rainbowify); void notice(QString body, bool rainbowify); void command(QString name, QString args); + void image(const QString &filename, + const std::optional<mtx::crypto::EncryptedFile> &file, + const QString &url, + const QString &mime, + uint64_t dsize, + const QSize &dimensions, + const QString &blurhash); void file(const QString &filename, const std::optional<mtx::crypto::EncryptedFile> &encryptedFile, const QString &url, diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index ae807f2d..f71fd42b 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -627,86 +627,72 @@ TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEven auto elem = *e; auto room = models.find(roomId); auto messageType = mtx::accessors::msg_type(elem); + auto content = mtx::accessors::url(elem); + + if (sentFromEncrypted) { + std::optional<mtx::crypto::EncryptedFile> encryptionInfo = + mtx::accessors::file(elem); + + http::client()->download( + content, + [this, roomId, e, encryptionInfo](const std::string &res, + const std::string &content_type, + const std::string &originalFilename, + mtx::http::RequestErr err) { + if (err) { + return; + } - if (sentFromEncrypted && messageType == mtx::events::MessageType::Image) { - auto body = mtx::accessors::body(elem); - auto mimetype = mtx::accessors::mimetype(elem); - auto imageHeight = mtx::accessors::media_height(elem); - auto imageWidth = mtx::accessors::media_height(elem); - - QString mxcUrl = QString::fromStdString(mtx::accessors::url(elem)); - MxcImageProvider::download( - mxcUrl.remove("mxc://"), - QSize(imageWidth, imageHeight), - [this, roomId, body, mimetype](QString, QSize, QImage image, QString) { - QByteArray data = - QByteArray::fromRawData((const char *)image.bits(), image.byteCount()); - - auto payload = std::string(data.data(), data.size()); - std::optional<mtx::crypto::EncryptedFile> encryptedFile; - - QSize dimensions; - QString blurhash; - auto mimeClass = QString::fromStdString(mimetype).split("/")[0]; - - dimensions = image.size(); - if (image.height() > 200 && image.width() > 360) - image = image.scaled(360, 200, Qt::KeepAspectRatioByExpanding); - std::vector<unsigned char> data_; - for (int y = 0; y < image.height(); y++) { - for (int x = 0; x < image.width(); x++) { - auto p = image.pixel(x, y); - data_.push_back(static_cast<unsigned char>(qRed(p))); - data_.push_back(static_cast<unsigned char>(qGreen(p))); - data_.push_back(static_cast<unsigned char>(qBlue(p))); + assert(encryptionInfo); + + auto data = mtx::crypto::to_string( + mtx::crypto::decrypt_file(res, encryptionInfo.value())); + + http::client()->upload( + data, + content_type, + originalFilename, + [this, roomId, e](const mtx::responses::ContentURI &res, + mtx::http::RequestErr err) mutable { + if (err) { + nhlog::net()->warn("failed to upload media: {} {} ({})", + err->matrix_error.error, + to_string(err->matrix_error.errcode), + static_cast<int>(err->status_code)); + return; } - } - blurhash = QString::fromStdString( - blurhash::encode(data_.data(), image.width(), image.height(), 4, 3)); - - http::client()->upload( - payload, - encryptedFile ? "application/octet-stream" : mimetype, - body, - [this, - roomId, - filename = body, - encryptedFile = std::move(encryptedFile), - mimeClass, - mimetype, - size = payload.size(), - dimensions, - blurhash](const mtx::responses::ContentURI &res, - mtx::http::RequestErr err) mutable { - if (err) { - nhlog::net()->warn("failed to upload media: {} {} ({})", - err->matrix_error.error, - to_string(err->matrix_error.errcode), - static_cast<int>(err->status_code)); - return; - } - - auto url = QString::fromStdString(res.content_uri); - if (encryptedFile) - encryptedFile->url = res.content_uri; - - auto r = models.find(roomId); - r.value()->input()->image(QString::fromStdString(filename), - encryptedFile, - url, - QString::fromStdString(mimetype), - size, - dimensions, - blurhash); - }); + + std::visit( + [this, roomId, e, url = res.content_uri](auto ev) { + if constexpr (mtx::events::message_content_to_type< + decltype(ev.content)> == + mtx::events::EventType::RoomMessage) { + if constexpr (messageWithFileAndUrl(ev)) { + ev.content.relations.relations.clear(); + ev.content.file.reset(); + ev.content.url = url; + + auto room = models.find(roomId); + room.value()->sendMessageEvent( + ev.content, + mtx::events::EventType::RoomMessage); + } + } + }, + *e); + }); + + return; }); + return; - }; + } std::visit( [room](auto e) { if constexpr (mtx::events::message_content_to_type<decltype(e.content)> == mtx::events::EventType::RoomMessage) { + e.content.relations.relations.clear(); room.value()->sendMessageEvent(e.content, mtx::events::EventType::RoomMessage); } diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 40bee990..9d1b4b1d 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -13,6 +13,7 @@ #include <mtx/common.hpp> #include <mtx/responses/messages.hpp> #include <mtx/responses/sync.hpp> +#include <type_traits> #include "Cache.h" #include "CallManager.h" @@ -31,6 +32,33 @@ class UserSettings; class ChatPage; class DeviceVerificationFlow; +struct nonesuch +{ + ~nonesuch() = delete; + nonesuch(nonesuch const &) = delete; + void operator=(nonesuch const &) = delete; +}; + +namespace detail { +template<class Default, class AlwaysVoid, template<class...> class Op, class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template<class Default, template<class...> class Op, class... Args> +struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op<Args...>; +}; + +} // namespace detail + +template<template<class...> class Op, class... Args> +using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t; + class TimelineViewManager : public QObject { Q_OBJECT @@ -155,6 +183,23 @@ private slots: void openImageOverlayInternal(QString eventId, QImage img); private: + template<class Content> + using f_t = decltype(Content::file); + + template<class Content> + using u_t = decltype(Content::url); + + template<typename T> + static constexpr bool messageWithFileAndUrl(const mtx::events::Event<T> &e) + { + if constexpr (is_detected<f_t, T>::value && is_detected<u_t, T>::value) { + return true; + } + + return false; + } + +private: #ifdef USE_QUICK_VIEW QQuickView *view; #else |