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
|