diff --git a/src/MatrixClient.h b/src/MatrixClient.h
index 2af57267..c77b1183 100644
--- a/src/MatrixClient.h
+++ b/src/MatrixClient.h
@@ -20,16 +20,6 @@ Q_DECLARE_METATYPE(nlohmann::json)
Q_DECLARE_METATYPE(std::vector<std::string>)
Q_DECLARE_METATYPE(std::vector<QString>)
-class MediaProxy : public QObject
-{
- Q_OBJECT
-
-signals:
- void imageDownloaded(const QPixmap &);
- void imageSaved(const QString &, const QByteArray &);
- void fileDownloaded(const QByteArray &);
-};
-
namespace http {
mtx::http::Client *
client();
diff --git a/src/timeline2/TimelineModel.cpp b/src/timeline2/TimelineModel.cpp
index 36b768ba..b702686e 100644
--- a/src/timeline2/TimelineModel.cpp
+++ b/src/timeline2/TimelineModel.cpp
@@ -105,6 +105,53 @@ eventUrl(const mtx::events::RoomEvent<T> &e)
}
template<class T>
+QString
+eventFilename(const T &)
+{
+ return "";
+}
+QString
+eventFilename(const mtx::events::RoomEvent<mtx::events::msg::Audio> &e)
+{
+ // body may be the original filename
+ return QString::fromStdString(e.content.body);
+}
+QString
+eventFilename(const mtx::events::RoomEvent<mtx::events::msg::Video> &e)
+{
+ // body may be the original filename
+ return QString::fromStdString(e.content.body);
+}
+QString
+eventFilename(const mtx::events::RoomEvent<mtx::events::msg::Image> &e)
+{
+ // body may be the original filename
+ return QString::fromStdString(e.content.body);
+}
+QString
+eventFilename(const mtx::events::RoomEvent<mtx::events::msg::File> &e)
+{
+ // body may be the original filename
+ if (!e.content.filename.empty())
+ return QString::fromStdString(e.content.filename);
+ return QString::fromStdString(e.content.body);
+}
+
+template<class T>
+QString
+eventMimeType(const T &)
+{
+ return QString();
+}
+template<class T>
+auto
+eventMimeType(const mtx::events::RoomEvent<T> &e)
+ -> std::enable_if_t<std::is_same<decltype(e.content.info.mimetype), std::string>::value, QString>
+{
+ return QString::fromStdString(e.content.info.mimetype);
+}
+
+template<class T>
qml_mtx_events::EventType
toRoomEventType(const mtx::events::Event<T> &e)
{
@@ -288,6 +335,8 @@ TimelineModel::roleNames() const
{UserName, "userName"},
{Timestamp, "timestamp"},
{Url, "url"},
+ {Filename, "filename"},
+ {MimeType, "mimetype"},
{Height, "height"},
{Width, "width"},
{ProportionalHeight, "proportionalHeight"},
@@ -366,6 +415,12 @@ TimelineModel::data(const QModelIndex &index, int role) const
case Url:
return QVariant(boost::apply_visitor(
[](const auto &e) -> QString { return eventUrl(e); }, event));
+ case Filename:
+ return QVariant(boost::apply_visitor(
+ [](const auto &e) -> QString { return eventFilename(e); }, event));
+ case MimeType:
+ return QVariant(boost::apply_visitor(
+ [](const auto &e) -> QString { return eventMimeType(e); }, event));
case Height:
return QVariant(boost::apply_visitor(
[](const auto &e) -> qulonglong { return eventHeight(e); }, event));
diff --git a/src/timeline2/TimelineModel.h b/src/timeline2/TimelineModel.h
index 3d55f206..e10a0b6e 100644
--- a/src/timeline2/TimelineModel.h
+++ b/src/timeline2/TimelineModel.h
@@ -127,6 +127,8 @@ public:
UserName,
Timestamp,
Url,
+ Filename,
+ MimeType,
Height,
Width,
ProportionalHeight,
diff --git a/src/timeline2/TimelineViewManager.cpp b/src/timeline2/TimelineViewManager.cpp
index bf09ef5a..eed0682d 100644
--- a/src/timeline2/TimelineViewManager.cpp
+++ b/src/timeline2/TimelineViewManager.cpp
@@ -1,6 +1,8 @@
#include "TimelineViewManager.h"
+#include <QFileDialog>
#include <QMetaType>
+#include <QMimeDatabase>
#include <QQmlContext>
#include "Logging.h"
@@ -55,24 +57,88 @@ TimelineViewManager::setHistoryView(const QString &room_id)
}
void
-TimelineViewManager::openImageOverlay(QString url) const
+TimelineViewManager::openImageOverlay(QString mxcUrl,
+ QString originalFilename,
+ QString mimeType,
+ qml_mtx_events::EventType eventType) const
{
QQuickImageResponse *imgResponse =
- imgProvider->requestImageResponse(url.remove("image://mxcimage/"), QSize());
- connect(imgResponse, &QQuickImageResponse::finished, this, [imgResponse]() {
- if (!imgResponse->errorString().isEmpty()) {
- nhlog::ui()->error("Error when retrieving image for overlay: {}",
- imgResponse->errorString().toStdString());
- return;
- }
- auto pixmap = QPixmap::fromImage(imgResponse->textureFactory()->image());
-
- auto imgDialog = new dialogs::ImageOverlay(pixmap);
- imgDialog->show();
- // connect(imgDialog, &dialogs::ImageOverlay::saving, this,
- // &ImageItem::saveAs);
- Q_UNUSED(imgDialog);
- });
+ imgProvider->requestImageResponse(mxcUrl.remove("mxc://"), QSize());
+ connect(imgResponse,
+ &QQuickImageResponse::finished,
+ this,
+ [this, mxcUrl, originalFilename, mimeType, eventType, imgResponse]() {
+ if (!imgResponse->errorString().isEmpty()) {
+ nhlog::ui()->error("Error when retrieving image for overlay: {}",
+ imgResponse->errorString().toStdString());
+ return;
+ }
+ auto pixmap = QPixmap::fromImage(imgResponse->textureFactory()->image());
+
+ auto imgDialog = new dialogs::ImageOverlay(pixmap);
+ imgDialog->show();
+ connect(imgDialog,
+ &dialogs::ImageOverlay::saving,
+ this,
+ [this, mxcUrl, originalFilename, mimeType, eventType]() {
+ saveMedia(mxcUrl, originalFilename, mimeType, eventType);
+ });
+ });
+}
+
+void
+TimelineViewManager::saveMedia(QString mxcUrl,
+ QString originalFilename,
+ QString mimeType,
+ qml_mtx_events::EventType eventType) const
+{
+ QString dialogTitle;
+ if (eventType == qml_mtx_events::EventType::ImageMessage) {
+ dialogTitle = tr("Save image");
+ } else if (eventType == qml_mtx_events::EventType::VideoMessage) {
+ dialogTitle = tr("Save video");
+ } else if (eventType == qml_mtx_events::EventType::AudioMessage) {
+ dialogTitle = tr("Save audio");
+ } else {
+ dialogTitle = tr("Save file");
+ }
+
+ QString filterString = QMimeDatabase().mimeTypeForName(mimeType).filterString();
+
+ auto filename =
+ QFileDialog::getSaveFileName(container, dialogTitle, originalFilename, filterString);
+
+ if (filename.isEmpty())
+ return;
+
+ const auto url = mxcUrl.toStdString();
+
+ http::client()->download(
+ url,
+ [filename, url](const std::string &data,
+ const std::string &,
+ const std::string &,
+ mtx::http::RequestErr err) {
+ if (err) {
+ nhlog::net()->warn("failed to retrieve image {}: {} {}",
+ url,
+ err->matrix_error.error,
+ static_cast<int>(err->status_code));
+ return;
+ }
+
+ try {
+ QFile file(filename);
+
+ if (!file.open(QIODevice::WriteOnly))
+ return;
+
+ file.write(QByteArray(data.data(), data.size()));
+ file.close();
+ } catch (const std::exception &e) {
+ nhlog::ui()->warn("Error while saving file to: {}", e.what());
+ }
+ });
}
void
diff --git a/src/timeline2/TimelineViewManager.h b/src/timeline2/TimelineViewManager.h
index 68f6ddb0..687ae24e 100644
--- a/src/timeline2/TimelineViewManager.h
+++ b/src/timeline2/TimelineViewManager.h
@@ -35,7 +35,30 @@ public:
void clearAll() { models.clear(); }
Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; }
- Q_INVOKABLE void openImageOverlay(QString url) const;
+ void openImageOverlay(QString mxcUrl,
+ QString originalFilename,
+ QString mimeType,
+ qml_mtx_events::EventType eventType) const;
+ void saveMedia(QString mxcUrl,
+ QString originalFilename,
+ QString mimeType,
+ qml_mtx_events::EventType eventType) const;
+ // Qml can only pass enum as int
+ Q_INVOKABLE void openImageOverlay(QString mxcUrl,
+ QString originalFilename,
+ QString mimeType,
+ int eventType) const
+ {
+ openImageOverlay(
+ mxcUrl, originalFilename, mimeType, (qml_mtx_events::EventType)eventType);
+ }
+ Q_INVOKABLE void saveMedia(QString mxcUrl,
+ QString originalFilename,
+ QString mimeType,
+ int eventType) const
+ {
+ saveMedia(mxcUrl, originalFilename, mimeType, (qml_mtx_events::EventType)eventType);
+ }
signals:
void clearRoomMessageCount(QString roomid);
|