diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h
index 9db16bae..d4fcfacf 100644
--- a/src/timeline/InputBar.h
+++ b/src/timeline/InputBar.h
@@ -41,6 +41,14 @@ 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();
@@ -70,13 +78,6 @@ 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/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 8e96cb3e..e3efe5ad 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -823,6 +823,16 @@ TimelineModel::viewRawMessage(QString id) const
}
void
+TimelineModel::forwardMessage(QString eventId, QString roomId)
+{
+ auto e = events.get(eventId.toStdString(), "");
+ if (!e)
+ return;
+
+ emit forwardToRoom(e, roomId, cache::isRoomEncrypted(room_id_.toStdString()));
+}
+
+void
TimelineModel::viewDecryptedRawMessage(QString id) const
{
auto e = events.get(id.toStdString(), "");
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 06da95c6..3e6f6f15 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -219,6 +219,7 @@ public:
Q_INVOKABLE QString formatPowerLevelEvent(QString id);
Q_INVOKABLE void viewRawMessage(QString id) const;
+ Q_INVOKABLE void forwardMessage(QString eventId, QString roomId);
Q_INVOKABLE void viewDecryptedRawMessage(QString id) const;
Q_INVOKABLE void openUserProfile(QString userid, bool global = false);
Q_INVOKABLE void openRoomSettings();
@@ -322,6 +323,9 @@ signals:
void roomNameChanged();
void roomTopicChanged();
void roomAvatarUrlChanged();
+ void forwardToRoom(mtx::events::collections::TimelineEvents *e,
+ QString roomId,
+ bool sentFromEncrypted);
private:
template<typename T>
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index f15b0b14..ae807f2d 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -4,6 +4,7 @@
#include "TimelineViewManager.h"
+#include <QBuffer>
#include <QDesktopServices>
#include <QDropEvent>
#include <QMetaType>
@@ -25,14 +26,13 @@
#include "RoomsModel.h"
#include "UserSettingsPage.h"
#include "UsersModel.h"
+#include "blurhash.hpp"
#include "dialogs/ImageOverlay.h"
#include "emoji/EmojiModel.h"
#include "emoji/Provider.h"
#include "ui/NhekoCursorShape.h"
#include "ui/NhekoDropArea.h"
-#include <iostream> //only for debugging
-
Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents)
Q_DECLARE_METATYPE(std::vector<DeviceInfo>)
@@ -332,6 +332,10 @@ TimelineViewManager::addRoom(const QString &room_id)
&TimelineModel::newEncryptedImage,
imgProvider,
&MxcImageProvider::addEncryptionInfo);
+ connect(newRoom.data(),
+ &TimelineModel::forwardToRoom,
+ this,
+ &TimelineViewManager::forwardMessageToRoom);
models.insert(room_id, std::move(newRoom));
}
}
@@ -614,3 +618,98 @@ TimelineViewManager::focusTimeline()
{
getWidget()->setFocus();
}
+
+void
+TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEvents *e,
+ QString roomId,
+ bool sentFromEncrypted)
+{
+ auto elem = *e;
+ auto room = models.find(roomId);
+ auto messageType = mtx::accessors::msg_type(elem);
+
+ 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)));
+ }
+ }
+ 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);
+ });
+ });
+ return;
+ };
+
+ std::visit(
+ [room](auto e) {
+ if constexpr (mtx::events::message_content_to_type<decltype(e.content)> ==
+ mtx::events::EventType::RoomMessage) {
+ room.value()->sendMessageEvent(e.content,
+ mtx::events::EventType::RoomMessage);
+ }
+ },
+ elem);
+}
\ No newline at end of file
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 3b405142..40bee990 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -16,6 +16,7 @@
#include "Cache.h"
#include "CallManager.h"
+#include "EventAccessors.h"
#include "Logging.h"
#include "TimelineModel.h"
#include "Utils.h"
@@ -146,6 +147,9 @@ public slots:
void backToRooms() { emit showRoomList(); }
QObject *completerFor(QString completerName, QString roomId = "");
+ void forwardMessageToRoom(mtx::events::collections::TimelineEvents *e,
+ QString roomId,
+ bool sentFromEncrypted);
private slots:
void openImageOverlayInternal(QString eventId, QImage img);
|