summary refs log tree commit diff
path: root/src/timeline
diff options
context:
space:
mode:
authorCH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>2020-08-30 22:27:14 +0530
committerCH Chethan Reddy <40890937+Chethan2k1@users.noreply.github.com>2020-08-30 22:27:14 +0530
commitb174bd938084a60fde5030dfc4dd0067a39f5a3f (patch)
tree6f2bb24fb2ba01adb2957aea67aecda501aa41ac /src/timeline
parentChange the tag for mtxclient (diff)
parentMerge pull request #265 from trilene/voip (diff)
downloadnheko-b174bd938084a60fde5030dfc4dd0067a39f5a3f.tar.xz
Merge remote-tracking branch 'upstream/master' into device-verification
Diffstat (limited to 'src/timeline')
-rw-r--r--src/timeline/EventStore.cpp122
-rw-r--r--src/timeline/EventStore.h1
-rw-r--r--src/timeline/TimelineModel.cpp235
-rw-r--r--src/timeline/TimelineModel.h20
-rw-r--r--src/timeline/TimelineViewManager.cpp71
-rw-r--r--src/timeline/TimelineViewManager.h21
6 files changed, 359 insertions, 111 deletions
diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp

index 6326e98e..3ecd4c75 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp
@@ -186,6 +186,26 @@ EventStore::addPending(mtx::events::collections::TimelineEvents event) } void +EventStore::clearTimeline() +{ + emit beginResetModel(); + + cache::client()->clearTimeline(room_id_); + auto range = cache::client()->getTimelineRange(room_id_); + if (range) { + nhlog::db()->info("Range {} {}", range->last, range->first); + this->last = range->last; + this->first = range->first; + } else { + this->first = std::numeric_limits<uint64_t>::max(); + this->last = std::numeric_limits<uint64_t>::max(); + } + nhlog::ui()->info("Range {} {}", this->last, this->first); + + emit endResetModel(); +} + +void EventStore::handleSync(const mtx::responses::Timeline &events) { if (this->thread() != QThread::currentThread()) @@ -448,36 +468,89 @@ EventStore::decryptEvent(const IdIndex &idx, index.session_id = e.content.session_id; index.sender_key = e.content.sender_key; - mtx::events::RoomEvent<mtx::events::msg::Notice> dummy; - dummy.origin_server_ts = e.origin_server_ts; - dummy.event_id = e.event_id; - dummy.sender = e.sender; - 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(); - auto asCacheEntry = [&idx](mtx::events::collections::TimelineEvents &&event) { auto event_ptr = new mtx::events::collections::TimelineEvents(std::move(event)); decryptedEvents_.insert(idx, event_ptr); return event_ptr; }; - try { - if (!cache::client()->inboundMegolmSessionExists(index)) { + auto decryptionResult = olm::decryptEvent(index, e); + + mtx::events::RoomEvent<mtx::events::msg::Notice> dummy; + dummy.origin_server_ts = e.origin_server_ts; + dummy.event_id = e.event_id; + dummy.sender = e.sender; + + 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(); nhlog::crypto()->info("Could not find inbound megolm session ({}, {}, {})", index.room_id, index.session_id, e.sender); - // TODO: request megolm session_id & session_key from the sender. - return asCacheEntry(std::move(dummy)); + // TODO: Check if this actually works and look in key backup + olm::send_key_request_for(room_id_, e); + break; + case olm::DecryptionErrorCode::DbError: + nhlog::db()->critical( + "failed to retrieve megolm session with index ({}, {}, {})", + index.room_id, + index.session_id, + index.sender_key, + decryptionResult.error_message.value_or("")); + dummy.content.body = + tr("-- Decryption Error (failed to retrieve megolm keys from db) --", + "Placeholder, when the message can't be decrypted, because the DB " + "access " + "failed.") + .toStdString(); + break; + case olm::DecryptionErrorCode::DecryptionFailed: + nhlog::crypto()->critical( + "failed to decrypt message with index ({}, {}, {}): {}", + index.room_id, + index.session_id, + index.sender_key, + decryptionResult.error_message.value_or("")); + dummy.content.body = + tr("-- Decryption Error (%1) --", + "Placeholder, when the message can't be decrypted. In this case, the " + "Olm " + "decrytion returned an error, which is passed as %1.") + .arg( + QString::fromStdString(decryptionResult.error_message.value_or(""))) + .toStdString(); + break; + case olm::DecryptionErrorCode::ParsingFailed: + dummy.content.body = + tr("-- Encrypted Event (Unknown event type) --", + "Placeholder, when the message was decrypted, but we couldn't parse " + "it, because " + "Nheko/mtxclient don't support that event type yet.") + .toStdString(); + break; + case olm::DecryptionErrorCode::ReplayAttack: + nhlog::crypto()->critical( + "Reply attack while decryptiong event {} in room {} from {}!", + e.event_id, + room_id_, + index.sender_key); + dummy.content.body = + tr("-- Reply attack! This message index was reused! --").toStdString(); + break; + case olm::DecryptionErrorCode::UnknownFingerprint: + // TODO: don't fail, just show in UI. + nhlog::crypto()->critical("Message by unverified fingerprint {}", + index.sender_key); + dummy.content.body = + tr("-- Message by unverified device! --").toStdString(); + break; } - } catch (const lmdb::error &e) { - nhlog::db()->critical("failed to check megolm session's existence: {}", e.what()); - dummy.content.body = tr("-- Decryption Error (failed to communicate with DB) --", - "Placeholder, when the message can't be decrypted, because " - "the DB access failed when trying to lookup the session.") - .toStdString(); return asCacheEntry(std::move(dummy)); } @@ -547,6 +620,11 @@ EventStore::decryptEvent(const IdIndex &idx, "Nheko/mtxclient don't support that event type yet.") .toStdString(); return asCacheEntry(std::move(dummy)); + auto encInfo = mtx::accessors::file(decryptionResult.event.value()); + if (encInfo) + emit newEncryptedImage(encInfo.value()); + + return asCacheEntry(std::move(decryptionResult.event.value())); } mtx::events::collections::TimelineEvents * @@ -608,6 +686,12 @@ EventStore::fetchMore() http::client()->messages( opts, [this, opts](const mtx::responses::Messages &res, mtx::http::RequestErr err) { + if (cache::client()->previousBatchToken(room_id_) != opts.from) { + nhlog::net()->warn("Cache cleared while fetching more messages, dropping " + "/messages response"); + emit fetchedMore(); + return; + } if (err) { nhlog::net()->error("failed to call /messages ({}): {} - {} - {}", opts.room_id, diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h
index 55a66f49..4ff4fa3b 100644 --- a/src/timeline/EventStore.h +++ b/src/timeline/EventStore.h
@@ -104,6 +104,7 @@ signals: public slots: void addPending(mtx::events::collections::TimelineEvents event); + void clearTimeline(); private: mtx::events::collections::TimelineEvents *decryptEvent( diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index dc5eb8cc..aea2645a 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp
@@ -160,6 +160,26 @@ struct RoomEventType { return qml_mtx_events::EventType::Redacted; } + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::CallInvite> &) + { + return qml_mtx_events::EventType::CallInvite; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::CallAnswer> &) + { + return qml_mtx_events::EventType::CallAnswer; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::CallHangUp> &) + { + return qml_mtx_events::EventType::CallHangUp; + } + qml_mtx_events::EventType operator()( + const mtx::events::Event<mtx::events::msg::CallCandidates> &) + { + return qml_mtx_events::EventType::CallCandidates; + } // ::EventType::Type operator()(const Event<mtx::events::msg::Location> &e) { return // ::EventType::LocationMessage; } }; @@ -271,6 +291,7 @@ TimelineModel::roleNames() const {RoomId, "roomId"}, {RoomName, "roomName"}, {RoomTopic, "roomTopic"}, + {CallType, "callType"}, {Dump, "dump"}, }; } @@ -422,6 +443,8 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r return QVariant(QString::fromStdString(room_name(event))); case RoomTopic: return QVariant(QString::fromStdString(room_topic(event))); + case CallType: + return QVariant(QString::fromStdString(call_type(event))); case Dump: { QVariantMap m; auto names = roleNames(); @@ -452,6 +475,7 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r m.insert(names[ReplyTo], data(event, static_cast<int>(ReplyTo))); m.insert(names[RoomName], data(event, static_cast<int>(RoomName))); m.insert(names[RoomTopic], data(event, static_cast<int>(RoomTopic))); + m.insert(names[CallType], data(event, static_cast<int>(CallType))); return QVariant(m); } @@ -548,8 +572,32 @@ TimelineModel::addEvents(const mtx::responses::Timeline &timeline) events.handleSync(timeline); - if (!timeline.events.empty()) - updateLastMessage(); + using namespace mtx::events; + for (auto e : timeline.events) { + if (auto encryptedEvent = std::get_if<EncryptedEvent<msg::Encrypted>>(&e)) { + MegolmSessionIndex index; + index.room_id = room_id_.toStdString(); + index.session_id = encryptedEvent->content.session_id; + index.sender_key = encryptedEvent->content.sender_key; + + auto result = olm::decryptEvent(index, *encryptedEvent); + if (result.event) + e = result.event.value(); + } + + if (std::holds_alternative<RoomEvent<msg::CallCandidates>>(e) || + std::holds_alternative<RoomEvent<msg::CallInvite>>(e) || + std::holds_alternative<RoomEvent<msg::CallAnswer>>(e) || + std::holds_alternative<RoomEvent<msg::CallHangUp>>(e)) + std::visit( + [this](auto &event) { + event.room_id = room_id_.toStdString(); + if (event.sender != http::client()->user_id().to_string()) + emit newCallEvent(event); + }, + e); + } + updateLastMessage(); } template<typename T> @@ -574,6 +622,23 @@ isMessage(const mtx::events::EncryptedEvent<T> &) return true; } +auto +isMessage(const mtx::events::RoomEvent<mtx::events::msg::CallInvite> &) +{ + return true; +} + +auto +isMessage(const mtx::events::RoomEvent<mtx::events::msg::CallAnswer> &) +{ + return true; +} +auto +isMessage(const mtx::events::RoomEvent<mtx::events::msg::CallHangUp> &) +{ + return true; +} + // Workaround. We also want to see a room at the top, if we just joined it auto isYourJoin(const mtx::events::StateEvent<mtx::events::state::Member> &e) @@ -806,15 +871,16 @@ TimelineModel::markEventsAsRead(const std::vector<QString> &event_ids) template<typename T> void -TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent<T> msg) +TimelineModel::sendEncryptedMessage(mtx::events::RoomEvent<T> msg, mtx::events::EventType eventType) { const auto room_id = room_id_.toStdString(); using namespace mtx::events; using namespace mtx::identifiers; - json doc = { - {"type", to_string(msg.type)}, {"content", json(msg.content)}, {"room_id", room_id}}; + json doc = {{"type", mtx::events::to_string(eventType)}, + {"content", msg.content}, + {"room_id", room_id}}; try { // Check if we have already an outbound megolm session then we can use. @@ -1093,69 +1159,115 @@ struct SendMessageVisitor : model_(model) {} - void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &msg) + template<typename T, mtx::events::EventType Event> + void sendRoomEvent(mtx::events::RoomEvent<T> msg) { - model_->sendEncryptedMessage(msg); + if (cache::isRoomEncrypted(model_->room_id_.toStdString())) { + auto encInfo = mtx::accessors::file(msg); + if (encInfo) + emit model_->newEncryptedImage(encInfo.value()); + + model_->sendEncryptedMessage(msg, Event); + } else { + msg.type = Event; + emit model_->addPendingMessageToStore(msg); + } } - void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationReady> &msg) + + // Do-nothing operator for all unhandled events + template<typename T> + void operator()(const mtx::events::Event<T> &) + {} + + // Operator for m.room.message events that contain a msgtype in their content + template<typename T, + std::enable_if_t<std::is_same<decltype(T::msgtype), std::string>::value, int> = 0> + void operator()(mtx::events::RoomEvent<T> msg) { - model_->sendEncryptedMessage(msg); + sendRoomEvent<T, mtx::events::EventType::RoomMessage>(msg); } - void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationStart> &msg) + + // Special operator for reactions, which are a type of m.room.message, but need to be + // handled distinctly for their differences from normal room messages. Specifically, + // reactions need to have the relation outside of ciphertext, or synapse / the homeserver + // cannot handle it correctly. See the MSC for more details: + // https://github.com/matrix-org/matrix-doc/blob/matthew/msc1849/proposals/1849-aggregations.md#end-to-end-encryption + void operator()(mtx::events::RoomEvent<mtx::events::msg::Reaction> msg) { - model_->sendEncryptedMessage(msg); + msg.type = mtx::events::EventType::Reaction; + emit model_->addPendingMessageToStore(msg); } - void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationAccept> &msg) + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::CallInvite> &event) { - model_->sendEncryptedMessage(msg); + sendRoomEvent<mtx::events::msg::CallInvite, mtx::events::EventType::CallInvite>( + event); } - void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationMac> &msg) + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::CallCandidates> &event) { - model_->sendEncryptedMessage(msg); + sendRoomEvent<mtx::events::msg::CallCandidates, + mtx::events::EventType::CallCandidates>(event); } - void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationKey> &msg) + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::CallAnswer> &event) { - model_->sendEncryptedMessage(msg); + sendRoomEvent<mtx::events::msg::CallAnswer, mtx::events::EventType::CallAnswer>( + event); } - void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationDone> &msg) + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::CallHangUp> &event) { - model_->sendEncryptedMessage(msg); + sendRoomEvent<mtx::events::msg::CallHangUp, mtx::events::EventType::CallHangUp>( + event); } - void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel> &msg) + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &msg) { - model_->sendEncryptedMessage(msg); + sendRoomEvent<mtx::events::msg::KeyVerificationRequest, + mtx::events::EventType::RoomMessage>(msg); } - // Do-nothing operator for all unhandled events - template<typename T> - void operator()(const mtx::events::Event<T> &) - {} - // Operator for m.room.message events that contain a msgtype in their content - template<typename T, - std::enable_if_t<std::is_same<decltype(T::msgtype), std::string>::value, int> = 0> - void operator()(const mtx::events::RoomEvent<T> &msg) + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationReady> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationReady, + mtx::events::EventType::KeyVerificationReady>(msg); + } + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationStart> &msg) { - if (cache::isRoomEncrypted(model_->room_id_.toStdString())) { - auto encInfo = mtx::accessors::file(msg); - if (encInfo) - emit model_->newEncryptedImage(encInfo.value()); + sendRoomEvent<mtx::events::msg::KeyVerificationStart, + mtx::events::EventType::KeyVerificationStart>(msg); + } - model_->sendEncryptedMessage(msg); - } else { - emit model_->addPendingMessageToStore(msg); - } + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationAccept> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationAccept, + mtx::events::EventType::KeyVerificationAccept>(msg); } - // Special operator for reactions, which are a type of m.room.message, but need to be - // handled distinctly for their differences from normal room messages. Specifically, - // reactions need to have the relation outside of ciphertext, or synapse / the homeserver - // cannot handle it correctly. See the MSC for more details: - // https://github.com/matrix-org/matrix-doc/blob/matthew/msc1849/proposals/1849-aggregations.md#end-to-end-encryption - void operator()(mtx::events::RoomEvent<mtx::events::msg::Reaction> msg) + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationMac> &msg) { - msg.type = mtx::events::EventType::Reaction; - emit model_->addPendingMessageToStore(msg); + sendRoomEvent<mtx::events::msg::KeyVerificationMac, + mtx::events::EventType::KeyVerificationMac>(msg); + } + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationKey> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationKey, + mtx::events::EventType::KeyVerificationKey>(msg); + } + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationDone> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationDone, + mtx::events::EventType::KeyVerificationDone>(msg); + } + + void operator()(const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel> &msg) + { + sendRoomEvent<mtx::events::msg::KeyVerificationCancel, + mtx::events::EventType::KeyVerificationCancel>(msg); } TimelineModel *model_; @@ -1173,39 +1285,6 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event) }, event); - if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationReady>>(&event)) { - std::visit( - [](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationReady; }, - event); - } - if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationStart>>(&event)) { - std::visit( - [](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationStart; }, - event); - } - if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationKey>>(&event)) { - std::visit([](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationKey; }, - event); - } - if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationMac>>(&event)) { - std::visit([](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationMac; }, - event); - } - if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationDone>>(&event)) { - std::visit( - [](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationDone; }, event); - } - if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationCancel>>(&event)) { - std::visit( - [](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationCancel; }, - event); - } - if (std::get_if<mtx::events::RoomEvent<mtx::events::msg::KeyVerificationAccept>>(&event)) { - std::visit( - [](auto &msg) { msg.type = mtx::events::EventType::KeyVerificationAccept; }, - event); - } - std::visit(SendMessageVisitor{this}, event); } diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index fb9921d3..390fa1ed 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h
@@ -37,6 +37,14 @@ enum EventType Aliases, /// m.room.avatar Avatar, + /// m.call.invite + CallInvite, + /// m.call.answer + CallAnswer, + /// m.call.hangup + CallHangUp, + /// m.call.candidates + CallCandidates, /// m.room.canonical_alias CanonicalAlias, /// m.room.create @@ -173,6 +181,7 @@ public: RoomId, RoomName, RoomTopic, + CallType, Dump, }; @@ -218,7 +227,7 @@ public: void updateLastMessage(); void addEvents(const mtx::responses::Timeline &events); template<class T> - void sendMessage(const T &msg); + void sendMessageEvent(const T &content, mtx::events::EventType eventType); RelatedInfo relatedInfo(QString id); public slots: @@ -251,6 +260,7 @@ public slots: } } void setDecryptDescription(bool decrypt) { decryptDescription = decrypt; } + void clearTimeline() { events.clearTimeline(); } private slots: void addPendingMessage(mtx::events::collections::TimelineEvents event); @@ -264,6 +274,7 @@ signals: void typingUsersChanged(std::vector<QString> users); void replyChanged(QString reply); void paginationInProgressChanged(const bool); + void newCallEvent(const mtx::events::collections::TimelineEvents &event); void openProfile(UserProfile *profile); @@ -273,7 +284,7 @@ signals: private: template<typename T> - void sendEncryptedMessage(mtx::events::RoomEvent<T> msg); + void sendEncryptedMessage(mtx::events::RoomEvent<T> msg, mtx::events::EventType eventType); void handleClaimedKeys(std::shared_ptr<StateKeeper> keeper, const std::map<std::string, std::string> &room_key, const std::map<std::string, DevicePublicKeys> &pks, @@ -304,9 +315,10 @@ private: template<class T> void -TimelineModel::sendMessage(const T &msg) +TimelineModel::sendMessageEvent(const T &content, mtx::events::EventType eventType) { mtx::events::RoomEvent<T> msgCopy = {}; - msgCopy.content = msg; + msgCopy.content = content; + msgCopy.type = eventType; emit newMessageToSend(msgCopy); } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index fb4a094e..f199578f 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp
@@ -1,11 +1,14 @@ #include "TimelineViewManager.h" +#include <QDesktopServices> #include <QMetaType> #include <QPalette> #include <QQmlContext> #include <QQmlEngine> +#include <QString> #include "BlurhashProvider.h" +#include "CallManager.h" #include "ChatPage.h" #include "ColorImageProvider.h" #include "DelegateChooser.h" @@ -97,10 +100,13 @@ TimelineViewManager::userStatus(QString id) const return QString::fromStdString(cache::statusMessage(id.toStdString())); } -TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettings, QWidget *parent) +TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettings, + CallManager *callManager, + QWidget *parent) : imgProvider(new MxcImageProvider()) , colorImgProvider(new ColorImageProvider()) , blurhashProvider(new BlurhashProvider()) + , callManager_(callManager) , settings(userSettings) { qRegisterMetaType<mtx::events::msg::KeyVerificationAccept>(); @@ -285,6 +291,10 @@ TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettin } } }); + connect(dynamic_cast<ChatPage *>(parent), &ChatPage::loggedOut, this, [this]() { + isInitialSync_ = true; + emit initialSyncChanged(true); + }); } void @@ -294,7 +304,17 @@ TimelineViewManager::sync(const mtx::responses::Rooms &rooms) // addRoom will only add the room, if it doesn't exist addRoom(QString::fromStdString(room_id)); const auto &room_model = models.value(QString::fromStdString(room_id)); + if (!isInitialSync_) + connect(room_model.data(), + &TimelineModel::newCallEvent, + callManager_, + &CallManager::syncEvent); room_model->addEvents(room.timeline); + if (!isInitialSync_) + disconnect(room_model.data(), + &TimelineModel::newCallEvent, + callManager_, + &CallManager::syncEvent); if (ChatPage::instance()->userSettings()->typingNotifications()) { std::vector<QString> typing; @@ -372,6 +392,12 @@ TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId) const } void +TimelineViewManager::openLink(QString link) const +{ + QDesktopServices::openUrl(link); +} + +void TimelineViewManager::updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids) { @@ -440,7 +466,7 @@ TimelineViewManager::queueTextMessage(const QString &msg) timeline_->resetReply(); } - timeline_->sendMessage(text); + timeline_->sendMessageEvent(text, mtx::events::EventType::RoomMessage); } void @@ -462,7 +488,7 @@ TimelineViewManager::queueEmoteMessage(const QString &msg) } if (timeline_) - timeline_->sendMessage(emote); + timeline_->sendMessageEvent(emote, mtx::events::EventType::RoomMessage); } void @@ -491,7 +517,7 @@ TimelineViewManager::queueReactionMessage(const QString &reactedEvent, const QSt reaction.relates_to.event_id = reactedEvent.toStdString(); reaction.relates_to.key = reactionKey.toStdString(); - timeline_->sendMessage(reaction); + timeline_->sendMessageEvent(reaction, mtx::events::EventType::Reaction); // Otherwise, we have previously reacted and the reaction should be redacted } else { timeline_->redactEvent(selfReactedEvent); @@ -527,7 +553,7 @@ TimelineViewManager::queueImageMessage(const QString &roomid, model->resetReply(); } - model->sendMessage(image); + model->sendMessageEvent(image, mtx::events::EventType::RoomMessage); } void @@ -555,7 +581,7 @@ TimelineViewManager::queueFileMessage( model->resetReply(); } - model->sendMessage(file); + model->sendMessageEvent(file, mtx::events::EventType::RoomMessage); } void @@ -583,7 +609,7 @@ TimelineViewManager::queueAudioMessage(const QString &roomid, model->resetReply(); } - model->sendMessage(audio); + model->sendMessageEvent(audio, mtx::events::EventType::RoomMessage); } void @@ -610,5 +636,34 @@ TimelineViewManager::queueVideoMessage(const QString &roomid, model->resetReply(); } - model->sendMessage(video); + model->sendMessageEvent(video, mtx::events::EventType::RoomMessage); +} + +void +TimelineViewManager::queueCallMessage(const QString &roomid, + const mtx::events::msg::CallInvite &callInvite) +{ + models.value(roomid)->sendMessageEvent(callInvite, mtx::events::EventType::CallInvite); +} + +void +TimelineViewManager::queueCallMessage(const QString &roomid, + const mtx::events::msg::CallCandidates &callCandidates) +{ + models.value(roomid)->sendMessageEvent(callCandidates, + mtx::events::EventType::CallCandidates); +} + +void +TimelineViewManager::queueCallMessage(const QString &roomid, + const mtx::events::msg::CallAnswer &callAnswer) +{ + models.value(roomid)->sendMessageEvent(callAnswer, mtx::events::EventType::CallAnswer); +} + +void +TimelineViewManager::queueCallMessage(const QString &roomid, + const mtx::events::msg::CallHangUp &callHangUp) +{ + models.value(roomid)->sendMessageEvent(callHangUp, mtx::events::EventType::CallHangUp); } diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 031d07cc..19406872 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h
@@ -19,6 +19,7 @@ class MxcImageProvider; class BlurhashProvider; +class CallManager; class ColorImageProvider; class UserSettings; @@ -46,7 +47,9 @@ class TimelineViewManager : public QObject bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged) public: - TimelineViewManager(QSharedPointer<UserSettings> userSettings, QWidget *parent = nullptr); + TimelineViewManager(QSharedPointer<UserSettings> userSettings, + CallManager *callManager, + QWidget *parent = nullptr); QWidget *getWidget() const { return container; } void sync(const mtx::responses::Rooms &rooms); @@ -62,6 +65,8 @@ public: Q_INVOKABLE QString userPresence(QString id) const; Q_INVOKABLE QString userStatus(QString id) const; + Q_INVOKABLE void openLink(QString link) const; + signals: void clearRoomMessageCount(QString roomid); void updateRoomsLastMessage(QString roomid, const DescInfo &info); @@ -110,8 +115,19 @@ public slots: const QString &url, const QString &mime, uint64_t dsize); + void queueCallMessage(const QString &roomid, const mtx::events::msg::CallInvite &); + void queueCallMessage(const QString &roomid, const mtx::events::msg::CallCandidates &); + void queueCallMessage(const QString &roomid, const mtx::events::msg::CallAnswer &); + void queueCallMessage(const QString &roomid, const mtx::events::msg::CallHangUp &); + void updateEncryptedDescriptions(); + void clearCurrentRoomTimeline() + { + if (timeline_) + timeline_->clearTimeline(); + } + private: #ifdef USE_QUICK_VIEW QQuickView *view; @@ -125,7 +141,8 @@ private: BlurhashProvider *blurhashProvider; QHash<QString, QSharedPointer<TimelineModel>> models; - TimelineModel *timeline_ = nullptr; + TimelineModel *timeline_ = nullptr; + CallManager *callManager_ = nullptr; bool isInitialSync_ = true;