From 91d1f19058a31cc35ca1212f042a9dd6f501a7b7 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 9 Nov 2019 03:06:10 +0100 Subject: Remove old timeline --- src/timeline/TimelineViewManager.h | 111 ++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 46 deletions(-) (limited to 'src/timeline/TimelineViewManager.h') diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index b52136d9..691c8ddb 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -1,69 +1,80 @@ -/* - * nheko Copyright (C) 2017 Konstantinos Sideris - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - #pragma once +#include +#include #include -#include +#include -#include +#include +#include "Cache.h" +#include "Logging.h" +#include "TimelineModel.h" #include "Utils.h" -class QFile; +// temporary for stubs +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" -class RoomInfoListItem; -class TimelineView; -struct DescInfo; -struct SavedMessages; +class MxcImageProvider; +class ColorImageProvider; -class TimelineViewManager : public QStackedWidget +class TimelineViewManager : public QObject { Q_OBJECT -public: - TimelineViewManager(QWidget *parent); - - // Initialize with timeline events. - void initialize(const mtx::responses::Rooms &rooms); - // Empty initialization. - void initialize(const std::vector &rooms); + Q_PROPERTY( + TimelineModel *timeline MEMBER timeline_ READ activeTimeline NOTIFY activeTimelineChanged) - void addRoom(const mtx::responses::JoinedRoom &room, const QString &room_id); - void addRoom(const QString &room_id); +public: + TimelineViewManager(QWidget *parent = 0); + QWidget *getWidget() const { return container; } void sync(const mtx::responses::Rooms &rooms); - void clearAll() { views_.clear(); } - - // Check if all the timelines have been loaded. - bool hasLoaded() const; + void addRoom(const QString &room_id); - static QString chooseRandomColor(); + void clearAll() { models.clear(); } + + Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; } + 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; + Q_INVOKABLE void cacheMedia(QString mxcUrl, QString mimeType); + // 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); - void updateRoomsLastMessage(const QString &user, const DescInfo &info); + void updateRoomsLastMessage(QString roomid, const DescInfo &info); + void activeTimelineChanged(TimelineModel *timeline); + void mediaCached(QString mxcUrl, QString cacheUrl); public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); - void removeTimelineEvent(const QString &room_id, const QString &event_id); void initWithMessages(const std::map &msgs); void setHistoryView(const QString &room_id); + void updateColorPalette(); + void queueTextMessage(const QString &msg); void queueReplyMessage(const QString &reply, const RelatedInfo &related); void queueEmoteMessage(const QString &msg); @@ -90,9 +101,17 @@ public slots: uint64_t dsize); private: - //! Check if the given room id is managed by a TimelineView. - bool timelineViewExists(const QString &id) { return views_.find(id) != views_.end(); } - - QString active_room_; - std::map> views_; +#ifdef USE_QUICK_VIEW + QQuickView *view; +#else + QQuickWidget *view; +#endif + QWidget *container; + TimelineModel *timeline_ = nullptr; + MxcImageProvider *imgProvider; + ColorImageProvider *colorImgProvider; + + QHash> models; }; + +#pragma GCC diagnostic pop -- cgit 1.5.1 From c424e397b01d8191568f951bdb754e1957681fb8 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sun, 10 Nov 2019 00:30:02 +0100 Subject: Add loading spinner and restore message send queue --- resources/qml/TimelineView.qml | 13 +++-- src/timeline/TimelineModel.cpp | 97 +++++++++++++++++++++++++++++++++++- src/timeline/TimelineModel.h | 41 ++++----------- src/timeline/TimelineViewManager.cpp | 3 ++ src/timeline/TimelineViewManager.h | 14 +++--- 5 files changed, 123 insertions(+), 45 deletions(-) (limited to 'src/timeline/TimelineViewManager.h') diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index b25b3a7c..3bbaa020 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -19,13 +19,20 @@ Item { color: colors.window Text { - visible: !timelineManager.timeline + visible: !timelineManager.timeline && !timelineManager.isInitialSync anchors.centerIn: parent text: qsTr("No room open") font.pointSize: 24 color: colors.windowText } + BusyIndicator { + anchors.centerIn: parent + running: timelineManager.isInitialSync + height: 200 + width: 200 + } + ListView { id: chat @@ -47,10 +54,6 @@ Item { } else { positionViewAtIndex(model.currentIndex, ListView.End) } - - //if (contentHeight < height) { - // model.fetchHistory(); - //} } } diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 9cae4608..6b0057a4 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -332,16 +332,18 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj connect( this, &TimelineModel::oldMessagesRetrieved, this, &TimelineModel::addBackwardsEvents); connect(this, &TimelineModel::messageFailed, this, [this](QString txn_id) { - pending.remove(txn_id); + pending.removeOne(txn_id); failed.insert(txn_id); int idx = idToIndex(txn_id); if (idx < 0) { nhlog::ui()->warn("Failed index out of range"); return; } + isProcessingPending = false; emit dataChanged(index(idx, 0), index(idx, 0)); }); connect(this, &TimelineModel::messageSent, this, [this](QString txn_id, QString event_id) { + pending.removeOne(txn_id); int idx = idToIndex(txn_id); if (idx < 0) { nhlog::ui()->warn("Sent index out of range"); @@ -365,11 +367,19 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj // ask to be notified for read receipts cache::client()->addPendingReceipt(room_id_, event_id); + isProcessingPending = false; emit dataChanged(index(idx, 0), index(idx, 0)); + + if (pending.size() > 0) + emit nextPendingMessage(); }); connect(this, &TimelineModel::redactionFailed, this, [](const QString &msg) { emit ChatPage::instance()->showNotification(msg); }); + + connect( + this, &TimelineModel::nextPendingMessage, this, &TimelineModel::processOnePendingMessage); + connect(this, &TimelineModel::newMessageToSend, this, &TimelineModel::addPendingMessage); } QHash @@ -1035,6 +1045,7 @@ TimelineModel::sendEncryptedMessage(const std::string &txn_id, nlohmann::json co } catch (const lmdb::error &e) { nhlog::db()->critical( "failed to save megolm outbound session: {}", e.what()); + emit messageFailed(QString::fromStdString(txn_id)); } }); @@ -1044,13 +1055,14 @@ TimelineModel::sendEncryptedMessage(const std::string &txn_id, nlohmann::json co http::client()->query_keys( req, - [keeper = std::move(keeper), megolm_payload, this]( + [keeper = std::move(keeper), megolm_payload, txn_id, this]( const mtx::responses::QueryKeys &res, mtx::http::RequestErr err) { if (err) { nhlog::net()->warn("failed to query device keys: {} {}", err->matrix_error.error, static_cast(err->status_code)); // TODO: Mark the event as failed. Communicate with the UI. + emit messageFailed(QString::fromStdString(txn_id)); return; } @@ -1150,9 +1162,11 @@ TimelineModel::sendEncryptedMessage(const std::string &txn_id, nlohmann::json co } catch (const lmdb::error &e) { nhlog::db()->critical( "failed to open outbound megolm session ({}): {}", room_id, e.what()); + emit messageFailed(QString::fromStdString(txn_id)); } catch (const mtx::crypto::olm_exception &e) { nhlog::crypto()->critical( "failed to open outbound megolm session ({}): {}", room_id, e.what()); + emit messageFailed(QString::fromStdString(txn_id)); } } @@ -1241,3 +1255,82 @@ TimelineModel::handleClaimedKeys(std::shared_ptr keeper, (void)keeper; }); } + +struct SendMessageVisitor +{ + SendMessageVisitor(const QString &txn_id, TimelineModel *model) + : txn_id_qstr_(txn_id) + , model_(model) + {} + + template + void operator()(const mtx::events::Event &) + {} + + template::value, int> = 0> + void operator()(const mtx::events::RoomEvent &msg) + + { + if (cache::client()->isRoomEncrypted(model_->room_id_.toStdString())) { + model_->sendEncryptedMessage(txn_id_qstr_.toStdString(), + nlohmann::json(msg.content)); + } else { + QString txn_id_qstr = txn_id_qstr_; + TimelineModel *model = model_; + http::client()->send_room_message( + model->room_id_.toStdString(), + txn_id_qstr.toStdString(), + msg.content, + [txn_id_qstr, model](const mtx::responses::EventId &res, + mtx::http::RequestErr err) { + if (err) { + const int status_code = + static_cast(err->status_code); + nhlog::net()->warn("[{}] failed to send message: {} {}", + txn_id_qstr.toStdString(), + err->matrix_error.error, + status_code); + emit model->messageFailed(txn_id_qstr); + } + emit model->messageSent( + txn_id_qstr, QString::fromStdString(res.event_id.to_string())); + }); + } + } + + QString txn_id_qstr_; + TimelineModel *model_; +}; + +void +TimelineModel::processOnePendingMessage() +{ + if (isProcessingPending || pending.isEmpty()) + return; + + isProcessingPending = true; + + QString txn_id_qstr = pending.first(); + + boost::apply_visitor(SendMessageVisitor{txn_id_qstr, this}, events.value(txn_id_qstr)); +} + +void +TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event) +{ + internalAddEvents({event}); + + QString txn_id_qstr = + boost::apply_visitor([](const auto &e) -> QString { return eventId(e); }, event); + beginInsertRows(QModelIndex(), + static_cast(this->eventOrder.size()), + static_cast(this->eventOrder.size())); + pending.push_back(txn_id_qstr); + this->eventOrder.insert(this->eventOrder.end(), txn_id_qstr); + endInsertRows(); + updateLastMessage(); + + if (!isProcessingPending) + emit nextPendingMessage(); +} diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 31e41315..e7842b99 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -173,6 +173,8 @@ public slots: private slots: // Add old events at the top of the timeline. void addBackwardsEvents(const mtx::responses::Messages &msgs); + void processOnePendingMessage(); + void addPendingMessage(mtx::events::collections::TimelineEvents event); signals: void oldMessagesRetrieved(const mtx::responses::Messages &res); @@ -181,6 +183,8 @@ signals: void currentIndexChanged(int index); void redactionFailed(QString id); void eventRedacted(QString id); + void nextPendingMessage(); + void newMessageToSend(mtx::events::collections::TimelineEvents event); private: DecryptionResult decryptEvent( @@ -198,7 +202,8 @@ private: void readEvent(const std::string &id); QHash events; - QSet pending, failed, read; + QSet failed, read; + QList pending; std::vector eventOrder; QString room_id_; @@ -206,11 +211,14 @@ private: bool isInitialSync = true; bool paginationInProgress = false; + bool isProcessingPending = false; QHash userColors; QString currentId; TimelineViewManager *manager_; + + friend struct SendMessageVisitor; }; template @@ -224,35 +232,6 @@ TimelineModel::sendMessage(const T &msg) msgCopy.event_id = txn_id; msgCopy.sender = http::client()->user_id().to_string(); msgCopy.origin_server_ts = QDateTime::currentMSecsSinceEpoch(); - internalAddEvents({msgCopy}); - - QString txn_id_qstr = QString::fromStdString(txn_id); - beginInsertRows(QModelIndex(), - static_cast(this->eventOrder.size()), - static_cast(this->eventOrder.size())); - pending.insert(txn_id_qstr); - this->eventOrder.insert(this->eventOrder.end(), txn_id_qstr); - endInsertRows(); - updateLastMessage(); - if (cache::client()->isRoomEncrypted(room_id_.toStdString())) - sendEncryptedMessage(txn_id, nlohmann::json(msg)); - else - http::client()->send_room_message( - room_id_.toStdString(), - txn_id, - msg, - [this, txn_id, txn_id_qstr](const mtx::responses::EventId &res, - mtx::http::RequestErr err) { - if (err) { - const int status_code = static_cast(err->status_code); - nhlog::net()->warn("[{}] failed to send message: {} {}", - txn_id, - err->matrix_error.error, - status_code); - emit messageFailed(txn_id_qstr); - } - emit messageSent(txn_id_qstr, - QString::fromStdString(res.event_id.to_string())); - }); + emit newMessageToSend(msgCopy); } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index d733ad90..06c42a39 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -97,6 +97,9 @@ TimelineViewManager::sync(const mtx::responses::Rooms &rooms) addRoom(QString::fromStdString(it->first)); models.value(QString::fromStdString(it->first))->addEvents(it->second.timeline); } + + this->isInitialSync_ = false; + emit initialSyncChanged(false); } void diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 691c8ddb..0bc58e68 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -12,10 +12,6 @@ #include "TimelineModel.h" #include "Utils.h" -// temporary for stubs -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" - class MxcImageProvider; class ColorImageProvider; @@ -25,6 +21,8 @@ class TimelineViewManager : public QObject Q_PROPERTY( TimelineModel *timeline MEMBER timeline_ READ activeTimeline NOTIFY activeTimelineChanged) + Q_PROPERTY( + bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged) public: TimelineViewManager(QWidget *parent = 0); @@ -36,6 +34,7 @@ public: void clearAll() { models.clear(); } Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; } + Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } void openImageOverlay(QString mxcUrl, QString originalFilename, QString mimeType, @@ -66,6 +65,7 @@ signals: void clearRoomMessageCount(QString roomid); void updateRoomsLastMessage(QString roomid, const DescInfo &info); void activeTimelineChanged(TimelineModel *timeline); + void initialSyncChanged(bool isInitialSync); void mediaCached(QString mxcUrl, QString cacheUrl); public slots: @@ -107,11 +107,11 @@ private: QQuickWidget *view; #endif QWidget *container; - TimelineModel *timeline_ = nullptr; + MxcImageProvider *imgProvider; ColorImageProvider *colorImgProvider; QHash> models; + TimelineModel *timeline_ = nullptr; + bool isInitialSync_ = true; }; - -#pragma GCC diagnostic pop -- cgit 1.5.1 From b8f6e4ce6462f074c34a8b7a286cbabe0e2897aa Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Tue, 3 Dec 2019 02:26:41 +0100 Subject: Add encrypted file download --- deps/CMakeLists.txt | 4 +- resources/qml/TimelineRow.qml | 2 +- resources/qml/delegates/FileMessage.qml | 2 +- resources/qml/delegates/ImageMessage.qml | 2 +- resources/qml/delegates/PlayableMediaMessage.qml | 4 +- src/timeline/TimelineModel.cpp | 184 +++++++++++++++++++++++ src/timeline/TimelineModel.h | 3 + src/timeline/TimelineViewManager.cpp | 154 ++----------------- src/timeline/TimelineViewManager.h | 27 +--- 9 files changed, 210 insertions(+), 172 deletions(-) (limited to 'src/timeline/TimelineViewManager.h') diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index d0a715e0..c5932ab7 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -46,10 +46,10 @@ set(BOOST_SHA256 set( MTXCLIENT_URL - https://github.com/Nheko-Reborn/mtxclient/archive/6eee767cc25a9db9f125843e584656cde1ebb6c5.tar.gz + https://github.com/Nheko-Reborn/mtxclient/archive/f719236b08d373d9508f2467bbfc6dfa953b1f8d.zip ) set(MTXCLIENT_HASH - 72fe77da4fed98b3cf069299f66092c820c900359a27ec26070175f9ad208a03) + 0660756c16cf297e02b0b29c07a59fc851723cc65f305893ae7238e6dd2e41c8) set( TWEENY_URL https://github.com/mobius3/tweeny/archive/b94ce07cfb02a0eb8ac8aaf66137dabdaea857cf.tar.gz diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml index 4917e893..2c2ed02a 100644 --- a/resources/qml/TimelineRow.qml +++ b/resources/qml/TimelineRow.qml @@ -97,7 +97,7 @@ RowLayout { MenuItem { visible: model.type == MtxEvent.ImageMessage || model.type == MtxEvent.VideoMessage || model.type == MtxEvent.AudioMessage || model.type == MtxEvent.FileMessage || model.type == MtxEvent.Sticker text: qsTr("Save as") - onTriggered: timelineManager.saveMedia(model.url, model.filename, model.mimetype, model.type) + onTriggered: timelineManager.timeline.saveMedia(model.id) } } } diff --git a/resources/qml/delegates/FileMessage.qml b/resources/qml/delegates/FileMessage.qml index f4cf3f15..2c911c5e 100644 --- a/resources/qml/delegates/FileMessage.qml +++ b/resources/qml/delegates/FileMessage.qml @@ -31,7 +31,7 @@ Rectangle { } MouseArea { anchors.fill: parent - onClicked: timelineManager.saveMedia(model.url, model.filename, model.mimetype, model.type) + onClicked: timelineManager.timeline.saveMedia(model.id) cursorShape: Qt.PointingHandCursor } } diff --git a/resources/qml/delegates/ImageMessage.qml b/resources/qml/delegates/ImageMessage.qml index a1a06012..1b6e5729 100644 --- a/resources/qml/delegates/ImageMessage.qml +++ b/resources/qml/delegates/ImageMessage.qml @@ -17,7 +17,7 @@ Item { MouseArea { enabled: model.type == MtxEvent.ImageMessage anchors.fill: parent - onClicked: timelineManager.openImageOverlay(model.url, model.filename, model.mimetype, model.type) + onClicked: timelineManager.openImageOverlay(model.url, model.id) } } } diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml index 3b987545..d0d4d7cb 100644 --- a/resources/qml/delegates/PlayableMediaMessage.qml +++ b/resources/qml/delegates/PlayableMediaMessage.qml @@ -97,7 +97,7 @@ Rectangle { anchors.fill: parent onClicked: { switch (button.state) { - case "": timelineManager.cacheMedia(model.url, model.mimetype); break; + case "": timelineManager.timeline.cacheMedia(model.id); break; case "stopped": media.play(); console.log("play"); button.state = "playing" @@ -118,7 +118,7 @@ Rectangle { } Connections { - target: timelineManager + target: timelineManager.timeline onMediaCached: { if (mxcUrl == model.url) { media.source = "file://" + cacheUrl diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index b904dfd7..f606b603 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -3,11 +3,15 @@ #include #include +#include +#include #include +#include #include "ChatPage.h" #include "Logging.h" #include "MainWindow.h" +#include "MxcImageProvider.h" #include "Olm.h" #include "TimelineViewManager.h" #include "Utils.h" @@ -88,17 +92,42 @@ eventFormattedBody(const mtx::events::RoomEvent &e) } } +template +boost::optional +eventEncryptionInfo(const mtx::events::Event &) +{ + return boost::none; +} + +template +auto +eventEncryptionInfo(const mtx::events::RoomEvent &e) -> std::enable_if_t< + std::is_same>::value, + boost::optional> +{ + return e.content.file; +} + template QString eventUrl(const mtx::events::Event &) { return ""; } + +QString +eventUrl(const mtx::events::StateEvent &e) +{ + return QString::fromStdString(e.content.url); +} + template auto eventUrl(const mtx::events::RoomEvent &e) -> std::enable_if_t::value, QString> { + if (e.content.file) + return QString::fromStdString(e.content.file->url); return QString::fromStdString(e.content.url); } @@ -1342,3 +1371,158 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event) if (!isProcessingPending) emit nextPendingMessage(); } + +void +TimelineModel::saveMedia(QString eventId) const +{ + mtx::events::collections::TimelineEvents event = events.value(eventId); + + if (auto e = boost::get>(&event)) { + event = decryptEvent(*e).event; + } + + QString mxcUrl = + boost::apply_visitor([](const auto &e) -> QString { return eventUrl(e); }, event); + QString originalFilename = + boost::apply_visitor([](const auto &e) -> QString { return eventFilename(e); }, event); + QString mimeType = + boost::apply_visitor([](const auto &e) -> QString { return eventMimeType(e); }, event); + + using EncF = boost::optional; + EncF encryptionInfo = + boost::apply_visitor([](const auto &e) -> EncF { return eventEncryptionInfo(e); }, event); + + qml_mtx_events::EventType eventType = boost::apply_visitor( + [](const auto &e) -> qml_mtx_events::EventType { return toRoomEventType(e); }, event); + + 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( + manager_->getWidget(), dialogTitle, originalFilename, filterString); + + if (filename.isEmpty()) + return; + + const auto url = mxcUrl.toStdString(); + + http::client()->download( + url, + [filename, url, encryptionInfo](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(err->status_code)); + return; + } + + try { + auto temp = data; + if (encryptionInfo) + temp = mtx::crypto::to_string( + mtx::crypto::decrypt_file(temp, encryptionInfo.value())); + + QFile file(filename); + + if (!file.open(QIODevice::WriteOnly)) + return; + + file.write(QByteArray(temp.data(), (int)temp.size())); + file.close(); + } catch (const std::exception &e) { + nhlog::ui()->warn("Error while saving file to: {}", e.what()); + } + }); +} + +void +TimelineModel::cacheMedia(QString eventId) +{ + mtx::events::collections::TimelineEvents event = events.value(eventId); + + if (auto e = boost::get>(&event)) { + event = decryptEvent(*e).event; + } + + QString mxcUrl = + boost::apply_visitor([](const auto &e) -> QString { return eventUrl(e); }, event); + QString mimeType = + boost::apply_visitor([](const auto &e) -> QString { return eventMimeType(e); }, event); + + using EncF = boost::optional; + EncF encryptionInfo = + boost::apply_visitor([](const auto &e) -> EncF { return eventEncryptionInfo(e); }, event); + + // If the message is a link to a non mxcUrl, don't download it + if (!mxcUrl.startsWith("mxc://")) { + emit mediaCached(mxcUrl, mxcUrl); + return; + } + + QString suffix = QMimeDatabase().mimeTypeForName(mimeType).preferredSuffix(); + + const auto url = mxcUrl.toStdString(); + QFileInfo filename(QString("%1/media_cache/%2.%3") + .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) + .arg(QString(mxcUrl).remove("mxc://")) + .arg(suffix)); + if (QDir::cleanPath(filename.path()) != filename.path()) { + nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url); + return; + } + + QDir().mkpath(filename.path()); + + if (filename.isReadable()) { + emit mediaCached(mxcUrl, filename.filePath()); + return; + } + + http::client()->download( + url, + [this, mxcUrl, filename, url, encryptionInfo](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(err->status_code)); + return; + } + + try { + auto temp = data; + if (encryptionInfo) + temp = mtx::crypto::to_string( + mtx::crypto::decrypt_file(temp, encryptionInfo.value())); + + QFile file(filename.filePath()); + + if (!file.open(QIODevice::WriteOnly)) + return; + + file.write(QByteArray(temp.data(), temp.size())); + file.close(); + } catch (const std::exception &e) { + nhlog::ui()->warn("Error while saving file to: {}", e.what()); + } + + emit mediaCached(mxcUrl, filename.filePath()); + }); +} diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index e7842b99..f52091e6 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -159,6 +159,8 @@ public: Q_INVOKABLE void redactEvent(QString id); Q_INVOKABLE int idToIndex(QString id) const; Q_INVOKABLE QString indexToId(int index) const; + Q_INVOKABLE void cacheMedia(QString eventId); + Q_INVOKABLE void saveMedia(QString eventId) const; void addEvents(const mtx::responses::Timeline &events); template @@ -185,6 +187,7 @@ signals: void eventRedacted(QString id); void nextPendingMessage(); void newMessageToSend(mtx::events::collections::TimelineEvents event); + void mediaCached(QString mxcUrl, QString cacheUrl); private: DecryptionResult decryptEvent( diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 2a88c882..6430a426 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -1,11 +1,8 @@ #include "TimelineViewManager.h" -#include #include -#include #include #include -#include #include "ChatPage.h" #include "ColorImageProvider.h" @@ -124,146 +121,24 @@ TimelineViewManager::setHistoryView(const QString &room_id) } void -TimelineViewManager::openImageOverlay(QString mxcUrl, - QString originalFilename, - QString mimeType, - qml_mtx_events::EventType eventType) const +TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId) const { QQuickImageResponse *imgResponse = 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(err->status_code)); - return; - } - - try { - QFile file(filename); - - if (!file.open(QIODevice::WriteOnly)) - return; - - file.write(QByteArray(data.data(), (int)data.size())); - file.close(); - } catch (const std::exception &e) { - nhlog::ui()->warn("Error while saving file to: {}", e.what()); - } - }); -} - -void -TimelineViewManager::cacheMedia(QString mxcUrl, QString mimeType) -{ - // If the message is a link to a non mxcUrl, don't download it - if (!mxcUrl.startsWith("mxc://")) { - emit mediaCached(mxcUrl, mxcUrl); - return; - } - - QString suffix = QMimeDatabase().mimeTypeForName(mimeType).preferredSuffix(); - - const auto url = mxcUrl.toStdString(); - QFileInfo filename(QString("%1/media_cache/%2.%3") - .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) - .arg(QString(mxcUrl).remove("mxc://")) - .arg(suffix)); - if (QDir::cleanPath(filename.path()) != filename.path()) { - nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url); - return; - } - - QDir().mkpath(filename.path()); - - if (filename.isReadable()) { - emit mediaCached(mxcUrl, filename.filePath()); - return; - } + connect(imgResponse, &QQuickImageResponse::finished, this, [this, eventId, 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()); - http::client()->download( - url, - [this, mxcUrl, 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(err->status_code)); - return; - } - - try { - QFile file(filename.filePath()); - - 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()); - } - - emit mediaCached(mxcUrl, filename.filePath()); - }); + auto imgDialog = new dialogs::ImageOverlay(pixmap); + imgDialog->show(); + connect(imgDialog, &dialogs::ImageOverlay::saving, timeline_, [this, eventId]() { + timeline_->saveMedia(eventId); + }); + }); } void @@ -401,3 +276,4 @@ TimelineViewManager::queueVideoMessage(const QString &roomid, video.url = url.toStdString(); models.value(roomid)->sendMessage(video); } + diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 0bc58e68..1cb0de44 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -35,38 +35,13 @@ public: Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; } Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } - 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; - Q_INVOKABLE void cacheMedia(QString mxcUrl, QString mimeType); - // 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); - } + Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const; signals: void clearRoomMessageCount(QString roomid); void updateRoomsLastMessage(QString roomid, const DescInfo &info); void activeTimelineChanged(TimelineModel *timeline); void initialSyncChanged(bool isInitialSync); - void mediaCached(QString mxcUrl, QString cacheUrl); public slots: void updateReadReceipts(const QString &room_id, const std::vector &event_ids); -- cgit 1.5.1 From 43d7fe0d358edd1983257350817f7e76132c8dc8 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Thu, 5 Dec 2019 15:31:53 +0100 Subject: Implement sending encrypted files --- src/ChatPage.cpp | 206 ++++++++--------------------------- src/ChatPage.h | 21 +--- src/TextInputWidget.cpp | 28 ++--- src/TextInputWidget.h | 12 +- src/timeline/TimelineViewManager.cpp | 19 +++- src/timeline/TimelineViewManager.h | 5 + 6 files changed, 79 insertions(+), 212 deletions(-) (limited to 'src/timeline/TimelineViewManager.h') diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 091a9fa0..d6f6940b 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -54,6 +54,8 @@ constexpr int CHECK_CONNECTIVITY_INTERVAL = 15'000; constexpr int RETRY_TIMEOUT = 5'000; constexpr size_t MAX_ONETIME_KEYS = 50; +Q_DECLARE_METATYPE(boost::optional) + ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) : QWidget(parent) , isConnected_(true) @@ -62,6 +64,9 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) { setObjectName("chatPage"); + qRegisterMetaType>( + "boost::optional"); + topLayout_ = new QHBoxLayout(this); topLayout_->setSpacing(0); topLayout_->setMargin(0); @@ -299,9 +304,9 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect( text_input_, - &TextInputWidget::uploadImage, + &TextInputWidget::uploadMedia, this, - [this](QSharedPointer dev, const QString &fn) { + [this](QSharedPointer dev, QString mimeClass, const QString &fn) { QMimeDatabase db; QMimeType mime = db.mimeTypeForData(dev.data()); @@ -311,9 +316,18 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) return; } - auto bin = dev->peek(dev->size()); - auto payload = std::string(bin.data(), bin.size()); - auto dimensions = QImageReader(dev.data()).size(); + auto bin = dev->peek(dev->size()); + auto payload = std::string(bin.data(), bin.size()); + boost::optional encryptedFile; + if (cache::client()->isRoomEncrypted(current_room_.toStdString())) { + mtx::crypto::BinaryBuf buf; + std::tie(buf, encryptedFile) = mtx::crypto::encrypt_file(payload); + payload = mtx::crypto::to_string(buf); + } + + QSize dimensions; + if (mimeClass == "image") + dimensions = QImageReader(dev.data()).size(); http::client()->upload( payload, @@ -322,193 +336,61 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) [this, room_id = current_room_, filename = fn, - mime = mime.name(), - size = payload.size(), + encryptedFile, + mimeClass, + mime = mime.name(), + size = payload.size(), dimensions](const mtx::responses::ContentURI &res, mtx::http::RequestErr err) { if (err) { emit uploadFailed( - tr("Failed to upload image. Please try again.")); - nhlog::net()->warn("failed to upload image: {} {} ({})", + tr("Failed to upload media. Please try again.")); + nhlog::net()->warn("failed to upload media: {} {} ({})", err->matrix_error.error, to_string(err->matrix_error.errcode), static_cast(err->status_code)); return; } - emit imageUploaded(room_id, + emit mediaUploaded(room_id, filename, + encryptedFile, QString::fromStdString(res.content_uri), + mimeClass, mime, size, dimensions); }); }); - connect(text_input_, - &TextInputWidget::uploadFile, - this, - [this](QSharedPointer dev, const QString &fn) { - QMimeDatabase db; - QMimeType mime = db.mimeTypeForData(dev.data()); - - if (!dev->open(QIODevice::ReadOnly)) { - emit uploadFailed( - QString("Error while reading media: %1").arg(dev->errorString())); - return; - } - - auto bin = dev->readAll(); - auto payload = std::string(bin.data(), bin.size()); - - http::client()->upload( - payload, - mime.name().toStdString(), - QFileInfo(fn).fileName().toStdString(), - [this, - room_id = current_room_, - filename = fn, - mime = mime.name(), - size = payload.size()](const mtx::responses::ContentURI &res, - mtx::http::RequestErr err) { - if (err) { - emit uploadFailed( - tr("Failed to upload file. Please try again.")); - nhlog::net()->warn("failed to upload file: {} ({})", - err->matrix_error.error, - static_cast(err->status_code)); - return; - } - - emit fileUploaded(room_id, - filename, - QString::fromStdString(res.content_uri), - mime, - size); - }); - }); - - connect(text_input_, - &TextInputWidget::uploadAudio, - this, - [this](QSharedPointer dev, const QString &fn) { - QMimeDatabase db; - QMimeType mime = db.mimeTypeForData(dev.data()); - - if (!dev->open(QIODevice::ReadOnly)) { - emit uploadFailed( - QString("Error while reading media: %1").arg(dev->errorString())); - return; - } - - auto bin = dev->readAll(); - auto payload = std::string(bin.data(), bin.size()); - - http::client()->upload( - payload, - mime.name().toStdString(), - QFileInfo(fn).fileName().toStdString(), - [this, - room_id = current_room_, - filename = fn, - mime = mime.name(), - size = payload.size()](const mtx::responses::ContentURI &res, - mtx::http::RequestErr err) { - if (err) { - emit uploadFailed( - tr("Failed to upload audio. Please try again.")); - nhlog::net()->warn("failed to upload audio: {} ({})", - err->matrix_error.error, - static_cast(err->status_code)); - return; - } - - emit audioUploaded(room_id, - filename, - QString::fromStdString(res.content_uri), - mime, - size); - }); - }); - connect(text_input_, - &TextInputWidget::uploadVideo, - this, - [this](QSharedPointer dev, const QString &fn) { - QMimeDatabase db; - QMimeType mime = db.mimeTypeForData(dev.data()); - - if (!dev->open(QIODevice::ReadOnly)) { - emit uploadFailed( - QString("Error while reading media: %1").arg(dev->errorString())); - return; - } - - auto bin = dev->readAll(); - auto payload = std::string(bin.data(), bin.size()); - - http::client()->upload( - payload, - mime.name().toStdString(), - QFileInfo(fn).fileName().toStdString(), - [this, - room_id = current_room_, - filename = fn, - mime = mime.name(), - size = payload.size()](const mtx::responses::ContentURI &res, - mtx::http::RequestErr err) { - if (err) { - emit uploadFailed( - tr("Failed to upload video. Please try again.")); - nhlog::net()->warn("failed to upload video: {} ({})", - err->matrix_error.error, - static_cast(err->status_code)); - return; - } - - emit videoUploaded(room_id, - filename, - QString::fromStdString(res.content_uri), - mime, - size); - }); - }); - connect(this, &ChatPage::uploadFailed, this, [this](const QString &msg) { text_input_->hideUploadSpinner(); emit showNotification(msg); }); connect(this, - &ChatPage::imageUploaded, + &ChatPage::mediaUploaded, this, [this](QString roomid, QString filename, + boost::optional encryptedFile, QString url, + QString mimeClass, QString mime, qint64 dsize, QSize dimensions) { text_input_->hideUploadSpinner(); - view_manager_->queueImageMessage( - roomid, filename, url, mime, dsize, dimensions); - }); - connect(this, - &ChatPage::fileUploaded, - this, - [this](QString roomid, QString filename, QString url, QString mime, qint64 dsize) { - text_input_->hideUploadSpinner(); - view_manager_->queueFileMessage(roomid, filename, url, mime, dsize); - }); - connect(this, - &ChatPage::audioUploaded, - this, - [this](QString roomid, QString filename, QString url, QString mime, qint64 dsize) { - text_input_->hideUploadSpinner(); - view_manager_->queueAudioMessage(roomid, filename, url, mime, dsize); - }); - connect(this, - &ChatPage::videoUploaded, - this, - [this](QString roomid, QString filename, QString url, QString mime, qint64 dsize) { - text_input_->hideUploadSpinner(); - view_manager_->queueVideoMessage(roomid, filename, url, mime, dsize); + + if (mimeClass == "image") + view_manager_->queueImageMessage( + roomid, filename, encryptedFile, url, mime, dsize, dimensions); + else if (mimeClass == "audio") + view_manager_->queueAudioMessage( + roomid, filename, encryptedFile, url, mime, dsize); + else if (mimeClass == "video") + view_manager_->queueVideoMessage( + roomid, filename, encryptedFile, url, mime, dsize); + else + view_manager_->queueFileMessage( + roomid, filename, encryptedFile, url, mime, dsize); }); connect(room_list_, &RoomList::roomAvatarChanged, this, &ChatPage::updateTopBarAvatar); diff --git a/src/ChatPage.h b/src/ChatPage.h index 1898f1a7..20e156af 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -18,7 +18,9 @@ #pragma once #include +#include #include +#include #include #include @@ -94,27 +96,14 @@ signals: const QPoint widgetPos); void uploadFailed(const QString &msg); - void imageUploaded(const QString &roomid, + void mediaUploaded(const QString &roomid, const QString &filename, + const boost::optional &file, const QString &url, + const QString &mimeClass, const QString &mime, qint64 dsize, const QSize &dimensions); - void fileUploaded(const QString &roomid, - const QString &filename, - const QString &url, - const QString &mime, - qint64 dsize); - void audioUploaded(const QString &roomid, - const QString &filename, - const QString &url, - const QString &mime, - qint64 dsize); - void videoUploaded(const QString &roomid, - const QString &filename, - const QString &url, - const QString &mime, - qint64 dsize); void contentLoaded(); void closing(); diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp index f723c01a..66700dbc 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp @@ -458,21 +458,16 @@ FilteredTextEdit::textChanged() } void -FilteredTextEdit::uploadData(const QByteArray data, const QString &media, const QString &filename) +FilteredTextEdit::uploadData(const QByteArray data, + const QString &mediaType, + const QString &filename) { QSharedPointer buffer{new QBuffer{this}}; buffer->setData(data); emit startedUpload(); - if (media == "image") - emit image(buffer, filename); - else if (media == "audio") - emit audio(buffer, filename); - else if (media == "video") - emit video(buffer, filename); - else - emit file(buffer, filename); + emit media(buffer, mediaType, filename); } void @@ -580,10 +575,7 @@ TextInputWidget::TextInputWidget(QWidget *parent) connect(input_, &FilteredTextEdit::message, this, &TextInputWidget::sendTextMessage); connect(input_, &FilteredTextEdit::reply, this, &TextInputWidget::sendReplyMessage); connect(input_, &FilteredTextEdit::command, this, &TextInputWidget::command); - connect(input_, &FilteredTextEdit::image, this, &TextInputWidget::uploadImage); - connect(input_, &FilteredTextEdit::audio, this, &TextInputWidget::uploadAudio); - connect(input_, &FilteredTextEdit::video, this, &TextInputWidget::uploadVideo); - connect(input_, &FilteredTextEdit::file, this, &TextInputWidget::uploadFile); + connect(input_, &FilteredTextEdit::media, this, &TextInputWidget::uploadMedia); connect(emojiBtn_, SIGNAL(emojiSelected(const QString &)), this, @@ -642,14 +634,8 @@ TextInputWidget::openFileSelection() const auto format = mime.name().split("/")[0]; QSharedPointer file{new QFile{fileName, this}}; - if (format == "image") - emit uploadImage(file, fileName); - else if (format == "audio") - emit uploadAudio(file, fileName); - else if (format == "video") - emit uploadVideo(file, fileName); - else - emit uploadFile(file, fileName); + + emit uploadMedia(file, format, fileName); showUploadSpinner(); } diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h index 71f794d1..d498be72 100644 --- a/src/TextInputWidget.h +++ b/src/TextInputWidget.h @@ -63,10 +63,7 @@ signals: void message(QString); void reply(QString, const RelatedInfo &); void command(QString name, QString args); - void image(QSharedPointer data, const QString &filename); - void audio(QSharedPointer data, const QString &filename); - void video(QSharedPointer data, const QString &filename); - void file(QSharedPointer data, const QString &filename); + void media(QSharedPointer data, QString mimeClass, const QString &filename); //! Trigger the suggestion popup. void showSuggestions(const QString &query); @@ -179,10 +176,9 @@ signals: void sendEmoteMessage(QString msg); void heightChanged(int height); - void uploadImage(const QSharedPointer data, const QString &filename); - void uploadFile(const QSharedPointer data, const QString &filename); - void uploadAudio(const QSharedPointer data, const QString &filename); - void uploadVideo(const QSharedPointer data, const QString &filename); + void uploadMedia(const QSharedPointer data, + QString mimeClass, + const QString &filename); void sendJoinRoomRequest(const QString &room); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 25f72a6d..6e18d111 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -222,6 +222,7 @@ TimelineViewManager::queueEmoteMessage(const QString &msg) void TimelineViewManager::queueImageMessage(const QString &roomid, const QString &filename, + const boost::optional &file, const QString &url, const QString &mime, uint64_t dsize, @@ -234,27 +235,32 @@ TimelineViewManager::queueImageMessage(const QString &roomid, image.url = url.toStdString(); image.info.h = dimensions.height(); image.info.w = dimensions.width(); + image.file = file; models.value(roomid)->sendMessage(image); } void -TimelineViewManager::queueFileMessage(const QString &roomid, - const QString &filename, - const QString &url, - const QString &mime, - uint64_t dsize) +TimelineViewManager::queueFileMessage( + const QString &roomid, + const QString &filename, + const boost::optional &encryptedFile, + const QString &url, + const QString &mime, + uint64_t dsize) { mtx::events::msg::File file; file.info.mimetype = mime.toStdString(); file.info.size = dsize; file.body = filename.toStdString(); file.url = url.toStdString(); + file.file = encryptedFile; models.value(roomid)->sendMessage(file); } void TimelineViewManager::queueAudioMessage(const QString &roomid, const QString &filename, + const boost::optional &file, const QString &url, const QString &mime, uint64_t dsize) @@ -264,12 +270,14 @@ TimelineViewManager::queueAudioMessage(const QString &roomid, audio.info.size = dsize; audio.body = filename.toStdString(); audio.url = url.toStdString(); + audio.file = file; models.value(roomid)->sendMessage(audio); } void TimelineViewManager::queueVideoMessage(const QString &roomid, const QString &filename, + const boost::optional &file, const QString &url, const QString &mime, uint64_t dsize) @@ -279,5 +287,6 @@ TimelineViewManager::queueVideoMessage(const QString &roomid, video.info.size = dsize; video.body = filename.toStdString(); video.url = url.toStdString(); + video.file = file; models.value(roomid)->sendMessage(video); } diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 1cb0de44..9e8de616 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -5,6 +5,7 @@ #include #include +#include #include #include "Cache.h" @@ -55,22 +56,26 @@ public slots: void queueEmoteMessage(const QString &msg); void queueImageMessage(const QString &roomid, const QString &filename, + const boost::optional &file, const QString &url, const QString &mime, uint64_t dsize, const QSize &dimensions); void queueFileMessage(const QString &roomid, const QString &filename, + const boost::optional &file, const QString &url, const QString &mime, uint64_t dsize); void queueAudioMessage(const QString &roomid, const QString &filename, + const boost::optional &file, const QString &url, const QString &mime, uint64_t dsize); void queueVideoMessage(const QString &roomid, const QString &filename, + const boost::optional &file, const QString &url, const QString &mime, uint64_t dsize); -- cgit 1.5.1