summary refs log tree commit diff
path: root/src/timeline
diff options
context:
space:
mode:
authorJoseph Donofry <joedonofry@gmail.com>2021-02-01 18:42:38 -0500
committerJoseph Donofry <joedonofry@gmail.com>2021-02-01 18:42:38 -0500
commit53c653a228f529bab3753ca99dee18a5bf5342a2 (patch)
treead0ba40d27ca5a515f1f33c29c910e5cf7b20dd7 /src/timeline
parentRemove redundant import and fix visible warning (diff)
parentFix emojis with fe0f in the middle (diff)
downloadnheko-53c653a228f529bab3753ca99dee18a5bf5342a2.tar.xz
Merge remote-tracking branch 'nheko-im/master' into privacy_screen
Diffstat (limited to 'src/timeline')
-rw-r--r--src/timeline/EventStore.cpp40
-rw-r--r--src/timeline/InputBar.cpp10
-rw-r--r--src/timeline/InputBar.h9
-rw-r--r--src/timeline/TimelineModel.cpp46
-rw-r--r--src/timeline/TimelineModel.h4
-rw-r--r--src/timeline/TimelineViewManager.cpp4
6 files changed, 89 insertions, 24 deletions
diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp

index 41001081..be4bc09e 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp
@@ -229,6 +229,9 @@ EventStore::clearTimeline() } nhlog::ui()->info("Range {} {}", this->last, this->first); + decryptedEvents_.clear(); + events_.clear(); + emit endResetModel(); } @@ -239,9 +242,12 @@ EventStore::receivedSessionKey(const std::string &session_id) return; auto request = pending_key_requests.at(session_id); - pending_key_requests.erase(session_id); - olm::send_key_request_for(request.events.front(), request.request_id, true); + // Don't request keys again until Nheko is restarted (for now) + pending_key_requests[session_id].events.clear(); + + if (!request.events.empty()) + olm::send_key_request_for(request.events.front(), request.request_id, true); for (const auto &e : request.events) { auto idx = idToIndex(e.event_id); @@ -265,6 +271,9 @@ EventStore::handleSync(const mtx::responses::Timeline &events) emit beginResetModel(); this->first = std::numeric_limits<uint64_t>::max(); this->last = std::numeric_limits<uint64_t>::max(); + + decryptedEvents_.clear(); + events_.clear(); emit endResetModel(); return; } @@ -273,6 +282,9 @@ EventStore::handleSync(const mtx::responses::Timeline &events) emit beginResetModel(); this->last = range->last; this->first = range->first; + + decryptedEvents_.clear(); + events_.clear(); emit endResetModel(); } else if (range->last > this->last) { emit beginInsertRows(toExternalIdx(this->last + 1), toExternalIdx(range->last)); @@ -543,12 +555,21 @@ EventStore::decryptEvent(const IdIndex &idx, if (decryptionResult.error) { switch (*decryptionResult.error) { - case olm::DecryptionErrorCode::MissingSession: { - dummy.content.body = - tr("-- Encrypted Event (No keys found for decryption) --", - "Placeholder, when the message was not decrypted yet or can't be " - "decrypted.") - .toStdString(); + case olm::DecryptionErrorCode::MissingSession: + case olm::DecryptionErrorCode::MissingSessionIndex: { + if (decryptionResult.error == olm::DecryptionErrorCode::MissingSession) + dummy.content.body = + tr("-- Encrypted Event (No keys found for decryption) --", + "Placeholder, when the message was not decrypted yet or can't " + "be " + "decrypted.") + .toStdString(); + else + dummy.content.body = + tr("-- Encrypted Event (Key not valid for this index) --", + "Placeholder, when the message can't be decrypted with this " + "key since it is not valid for this index ") + .toStdString(); nhlog::crypto()->info("Could not find inbound megolm session ({}, {}, {})", index.room_id, index.session_id, @@ -760,7 +781,8 @@ EventStore::fetchMore() if (cache::client()->previousBatchToken(room_id_) != opts.from) { nhlog::net()->warn("Cache cleared while fetching more messages, dropping " "/messages response"); - emit fetchedMore(); + if (!opts.to.empty()) + emit fetchedMore(); return; } if (err) { diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index 3cddd613..b31c1f76 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp
@@ -251,12 +251,14 @@ InputBar::openFileSelection() } void -InputBar::message(QString msg) +InputBar::message(QString msg, MarkdownOverride useMarkdown) { mtx::events::msg::Text text = {}; text.body = msg.trimmed().toStdString(); - if (ChatPage::instance()->userSettings()->markdown()) { + if ((ChatPage::instance()->userSettings()->markdown() && + useMarkdown == MarkdownOverride::NOT_SPECIFIED) || + useMarkdown == MarkdownOverride::ON) { text.formatted_body = utils::markdownToHtml(msg).toStdString(); // Don't send formatted_body, when we don't need to @@ -477,6 +479,10 @@ InputBar::command(QString command, QString args) room->clearTimeline(); } else if (command == "rotate-megolm-session") { cache::dropOutboundMegolmSession(room->roomId().toStdString()); + } else if (command == "md") { + message(args, MarkdownOverride::ON); + } else if (command == "plain") { + message(args, MarkdownOverride::OFF); } } diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h
index c729a6fc..f173bbc0 100644 --- a/src/timeline/InputBar.h +++ b/src/timeline/InputBar.h
@@ -12,6 +12,13 @@ class QMimeData; class QDropEvent; class QStringList; +enum class MarkdownOverride +{ + NOT_SPECIFIED, // no override set + ON, + OFF, +}; + class InputBar : public QObject { Q_OBJECT @@ -41,7 +48,7 @@ public slots: void updateState(int selectionStart, int selectionEnd, int cursorPosition, QString text); void openFileSelection(); bool uploading() const { return uploading_; } - void message(QString body); + void message(QString body, MarkdownOverride useMarkdown = MarkdownOverride::NOT_SPECIFIED); QObject *completerFor(QString completerName); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 5db6f0c2..79cf5184 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp
@@ -5,6 +5,7 @@ #include <type_traits> #include <QCache> +#include <QDesktopServices> #include <QFileDialog> #include <QMimeDatabase> #include <QRegularExpression> @@ -353,7 +354,8 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r return QVariant(emojiCount); } case Body: - return QVariant(utils::replaceEmoji(QString::fromStdString(body(event)))); + return QVariant( + utils::replaceEmoji(QString::fromStdString(body(event)).toHtmlEscaped())); case FormattedBody: { const static QRegularExpression replyFallback( "<mx-reply>.*</mx-reply>", QRegularExpression::DotMatchesEverythingOption); @@ -797,9 +799,9 @@ TimelineModel::viewDecryptedRawMessage(QString id) const } void -TimelineModel::openUserProfile(QString userid) +TimelineModel::openUserProfile(QString userid, bool global) { - emit openProfile(new UserProfile(room_id_, userid, manager_, this)); + emit openProfile(new UserProfile(global ? "" : room_id_, userid, manager_, this)); } void @@ -1072,6 +1074,14 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event) std::visit(SendMessageVisitor{this}, event); } +void +TimelineModel::openMedia(QString eventId) +{ + cacheMedia(eventId, [](QString filename) { + QDesktopServices::openUrl(QUrl::fromLocalFile(filename)); + }); +} + bool TimelineModel::saveMedia(QString eventId) const { @@ -1148,7 +1158,7 @@ TimelineModel::saveMedia(QString eventId) const } void -TimelineModel::cacheMedia(QString eventId) +TimelineModel::cacheMedia(QString eventId, std::function<void(const QString)> callback) { mtx::events::collections::TimelineEvents *event = events.get(eventId.toStdString(), ""); if (!event) @@ -1168,12 +1178,13 @@ TimelineModel::cacheMedia(QString eventId) QString suffix = QMimeDatabase().mimeTypeForName(mimeType).preferredSuffix(); - const auto url = mxcUrl.toStdString(); + const auto url = mxcUrl.toStdString(); + const auto name = QString(mxcUrl).remove("mxc://"); QFileInfo filename(QString("%1/media_cache/%2.%3") .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) - .arg(QString(mxcUrl).remove("mxc://")) + .arg(name) .arg(suffix)); - if (QDir::cleanPath(filename.path()) != filename.path()) { + if (QDir::cleanPath(name) != name) { nhlog::net()->warn("mxcUrl '{}' is not safe, not downloading file", url); return; } @@ -1182,15 +1193,18 @@ TimelineModel::cacheMedia(QString eventId) if (filename.isReadable()) { emit mediaCached(mxcUrl, filename.filePath()); + if (callback) { + callback(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) { + [this, callback, 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, @@ -1212,6 +1226,10 @@ TimelineModel::cacheMedia(QString eventId) file.write(QByteArray(temp.data(), (int)temp.size())); file.close(); + + if (callback) { + callback(filename.filePath()); + } } catch (const std::exception &e) { nhlog::ui()->warn("Error while saving file to: {}", e.what()); } @@ -1220,6 +1238,12 @@ TimelineModel::cacheMedia(QString eventId) }); } +void +TimelineModel::cacheMedia(QString eventId) +{ + cacheMedia(eventId, NULL); +} + QString TimelineModel::formatTypingUsers(const std::vector<QString> &users, QColor bg) { diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index b6b3b5ae..51b8049e 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h
@@ -212,14 +212,16 @@ public: Q_INVOKABLE void viewRawMessage(QString id) const; Q_INVOKABLE void viewDecryptedRawMessage(QString id) const; - Q_INVOKABLE void openUserProfile(QString userid); + Q_INVOKABLE void openUserProfile(QString userid, bool global = false); Q_INVOKABLE void replyAction(QString id); Q_INVOKABLE void readReceiptsAction(QString id) const; Q_INVOKABLE void redactEvent(QString id); Q_INVOKABLE int idToIndex(QString id) const; Q_INVOKABLE QString indexToId(int index) const; + Q_INVOKABLE void openMedia(QString eventId); Q_INVOKABLE void cacheMedia(QString eventId); Q_INVOKABLE bool saveMedia(QString eventId) const; + void cacheMedia(QString eventId, std::function<void(const QString filename)> callback); std::vector<::Reaction> reactions(const std::string &event_id) { diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 97af0065..93451976 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp
@@ -128,6 +128,10 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par "UserProfile needs to be instantiated on the C++ side"); static auto self = this; + qmlRegisterSingletonType<MainWindow>( + "im.nheko", 1, 0, "MainWindow", [](QQmlEngine *, QJSEngine *) -> QObject * { + return MainWindow::instance(); + }); qmlRegisterSingletonType<TimelineViewManager>( "im.nheko", 1, 0, "TimelineManager", [](QQmlEngine *, QJSEngine *) -> QObject * { return self;