summary refs log tree commit diff
path: root/src/timeline
diff options
context:
space:
mode:
authorkamathmanu <manuriddle@gmail.com>2021-08-07 21:20:43 +0000
committerGitHub <noreply@github.com>2021-08-07 21:20:43 +0000
commit2dfccda73c44d97e9e3e52db3e03315e8ef656a5 (patch)
tree52c9855610cbdf6f6284cfacbaba07f676a0f621 /src/timeline
parentFix Duplicate fetched chunk (diff)
parentShow encryption errors in qml and add request keys button (diff)
downloadnheko-2dfccda73c44d97e9e3e52db3e03315e8ef656a5.tar.xz
Merge branch 'master' into nhekoRoomDirectory
Diffstat (limited to 'src/timeline')
-rw-r--r--src/timeline/EventStore.cpp256
-rw-r--r--src/timeline/EventStore.h9
-rw-r--r--src/timeline/RoomlistModel.cpp2
-rw-r--r--src/timeline/TimelineModel.cpp41
-rw-r--r--src/timeline/TimelineModel.h20
-rw-r--r--src/timeline/TimelineViewManager.cpp8
6 files changed, 173 insertions, 163 deletions
diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp
index 9a91ff79..742f8dbb 100644
--- a/src/timeline/EventStore.cpp
+++ b/src/timeline/EventStore.cpp
@@ -20,8 +20,7 @@
 
 Q_DECLARE_METATYPE(Reaction)
 
-QCache<EventStore::IdIndex, mtx::events::collections::TimelineEvents> EventStore::decryptedEvents_{
-  1000};
+QCache<EventStore::IdIndex, olm::DecryptionResult> EventStore::decryptedEvents_{1000};
 QCache<EventStore::IdIndex, mtx::events::collections::TimelineEvents> EventStore::events_by_id_{
   1000};
 QCache<EventStore::Index, mtx::events::collections::TimelineEvents> EventStore::events_{1000};
@@ -144,12 +143,16 @@ EventStore::EventStore(std::string room_id, QObject *)
                                                             mtx::events::msg::Encrypted>) {
                                                     auto event =
                                                       decryptEvent({room_id_, e.event_id}, e);
-                                                    if (auto dec =
-                                                          std::get_if<mtx::events::RoomEvent<
-                                                            mtx::events::msg::
-                                                              KeyVerificationRequest>>(event)) {
-                                                            emit updateFlowEventId(
-                                                              event_id.event_id.to_string());
+                                                    if (event->event) {
+                                                            if (auto dec = std::get_if<
+                                                                  mtx::events::RoomEvent<
+                                                                    mtx::events::msg::
+                                                                      KeyVerificationRequest>>(
+                                                                  &event->event.value())) {
+                                                                    emit updateFlowEventId(
+                                                                      event_id.event_id
+                                                                        .to_string());
+                                                            }
                                                     }
                                             }
                                     });
@@ -393,12 +396,12 @@ EventStore::handleSync(const mtx::responses::Timeline &events)
                 if (auto encrypted =
                       std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
                         &event)) {
-                        mtx::events::collections::TimelineEvents *d_event =
-                          decryptEvent({room_id_, encrypted->event_id}, *encrypted);
-                        if (std::visit(
+                        auto d_event = decryptEvent({room_id_, encrypted->event_id}, *encrypted);
+                        if (d_event->event &&
+                            std::visit(
                               [](auto e) { return (e.sender != utils::localUser().toStdString()); },
-                              *d_event)) {
-                                handle_room_verification(*d_event);
+                              *d_event->event)) {
+                                handle_room_verification(*d_event->event);
                         }
                 }
         }
@@ -599,11 +602,15 @@ EventStore::get(int idx, bool decrypt)
                 events_.insert(index, event_ptr);
         }
 
-        if (decrypt)
+        if (decrypt) {
                 if (auto encrypted =
                       std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
-                        event_ptr))
-                        return decryptEvent({room_id_, encrypted->event_id}, *encrypted);
+                        event_ptr)) {
+                        auto decrypted = decryptEvent({room_id_, encrypted->event_id}, *encrypted);
+                        if (decrypted->event)
+                                return &*decrypted->event;
+                }
+        }
 
         return event_ptr;
 }
@@ -629,7 +636,7 @@ EventStore::indexToId(int idx) const
         return cache::client()->getTimelineEventId(room_id_, toInternalIdx(idx));
 }
 
-mtx::events::collections::TimelineEvents *
+olm::DecryptionResult *
 EventStore::decryptEvent(const IdIndex &idx,
                          const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &e)
 {
@@ -641,57 +648,24 @@ EventStore::decryptEvent(const IdIndex &idx,
         index.session_id = e.content.session_id;
         index.sender_key = e.content.sender_key;
 
-        auto asCacheEntry = [&idx](mtx::events::collections::TimelineEvents &&event) {
-                auto event_ptr = new mtx::events::collections::TimelineEvents(std::move(event));
+        auto asCacheEntry = [&idx](olm::DecryptionResult &&event) {
+                auto event_ptr = new olm::DecryptionResult(std::move(event));
                 decryptedEvents_.insert(idx, event_ptr);
                 return event_ptr;
         };
 
         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) {
+                switch (decryptionResult.error) {
                 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,
                                               e.sender);
-                        // we may not want to request keys during initial sync and such
-                        if (suppressKeyRequests)
-                                break;
-                        // TODO: Check if this actually works and look in key backup
-                        auto copy    = e;
-                        copy.room_id = room_id_;
-                        if (pending_key_requests.count(e.content.session_id)) {
-                                pending_key_requests.at(e.content.session_id)
-                                  .events.push_back(copy);
-                        } else {
-                                PendingKeyRequests request;
-                                request.request_id =
-                                  "key_request." + http::client()->generate_txn_id();
-                                request.events.push_back(copy);
-                                olm::send_key_request_for(copy, request.request_id);
-                                pending_key_requests[e.content.session_id] = request;
-                        }
+
+                        requestSession(e, false);
                         break;
                 }
                 case olm::DecryptionErrorCode::DbError:
@@ -701,12 +675,6 @@ EventStore::decryptEvent(const IdIndex &idx,
                           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(
@@ -715,22 +683,8 @@ EventStore::decryptEvent(const IdIndex &idx,
                           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(
@@ -738,85 +692,50 @@ EventStore::decryptEvent(const IdIndex &idx,
                           e.event_id,
                           room_id_,
                           index.sender_key);
-                        dummy.content.body =
-                          tr("-- Replay 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();
+                case olm::DecryptionErrorCode::NoError:
+                        // unreachable
                         break;
                 }
-                return asCacheEntry(std::move(dummy));
-        }
-
-        std::string msg_str;
-        try {
-                auto session = cache::client()->getInboundMegolmSession(index);
-                auto res =
-                  olm::client()->decrypt_group_message(session.get(), e.content.ciphertext);
-                msg_str = std::string((char *)res.data.data(), res.data.size());
-        } catch (const lmdb::error &e) {
-                nhlog::db()->critical("failed to retrieve megolm session with index ({}, {}, {})",
-                                      index.room_id,
-                                      index.session_id,
-                                      index.sender_key,
-                                      e.what());
-                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();
-                return asCacheEntry(std::move(dummy));
-        } catch (const mtx::crypto::olm_exception &e) {
-                nhlog::crypto()->critical("failed to decrypt message with index ({}, {}, {}): {}",
-                                          index.room_id,
-                                          index.session_id,
-                                          index.sender_key,
-                                          e.what());
-                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(e.what())
-                    .toStdString();
-                return asCacheEntry(std::move(dummy));
-        }
-
-        // Add missing fields for the event.
-        json body                = json::parse(msg_str);
-        body["event_id"]         = e.event_id;
-        body["sender"]           = e.sender;
-        body["origin_server_ts"] = e.origin_server_ts;
-        body["unsigned"]         = e.unsigned_data;
-
-        // relations are unencrypted in content...
-        mtx::common::add_relations(body["content"], e.content.relations);
-
-        json event_array = json::array();
-        event_array.push_back(body);
-
-        std::vector<mtx::events::collections::TimelineEvents> temp_events;
-        mtx::responses::utils::parse_timeline_events(event_array, temp_events);
-
-        if (temp_events.size() == 1) {
-                auto encInfo = mtx::accessors::file(temp_events[0]);
-
-                if (encInfo)
-                        emit newEncryptedImage(encInfo.value());
-
-                return asCacheEntry(std::move(temp_events[0]));
+                return asCacheEntry(std::move(decryptionResult));
         }
 
         auto encInfo = mtx::accessors::file(decryptionResult.event.value());
         if (encInfo)
                 emit newEncryptedImage(encInfo.value());
 
-        return asCacheEntry(std::move(decryptionResult.event.value()));
+        return asCacheEntry(std::move(decryptionResult));
+}
+
+void
+EventStore::requestSession(const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &ev,
+                           bool manual)
+{
+        // we may not want to request keys during initial sync and such
+        if (suppressKeyRequests)
+                return;
+
+        // TODO: Look in key backup
+        auto copy    = ev;
+        copy.room_id = room_id_;
+        if (pending_key_requests.count(ev.content.session_id)) {
+                auto &r = pending_key_requests.at(ev.content.session_id);
+                r.events.push_back(copy);
+
+                // automatically request once every 10 min, manually every 1 min
+                qint64 delay = manual ? 60 : (60 * 10);
+                if (r.requested_at + delay < QDateTime::currentSecsSinceEpoch()) {
+                        r.requested_at = QDateTime::currentSecsSinceEpoch();
+                        olm::send_key_request_for(copy, r.request_id);
+                }
+        } else {
+                PendingKeyRequests request;
+                request.request_id   = "key_request." + http::client()->generate_txn_id();
+                request.requested_at = QDateTime::currentSecsSinceEpoch();
+                request.events.push_back(copy);
+                olm::send_key_request_for(copy, request.request_id);
+                pending_key_requests[ev.content.session_id] = request;
+        }
 }
 
 void
@@ -877,15 +796,56 @@ EventStore::get(std::string id, std::string_view related_to, bool decrypt, bool
                 events_by_id_.insert(index, event_ptr);
         }
 
-        if (decrypt)
+        if (decrypt) {
                 if (auto encrypted =
                       std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
-                        event_ptr))
-                        return decryptEvent(index, *encrypted);
+                        event_ptr)) {
+                        auto decrypted = decryptEvent(index, *encrypted);
+                        if (decrypted->event)
+                                return &*decrypted->event;
+                }
+        }
 
         return event_ptr;
 }
 
+olm::DecryptionErrorCode
+EventStore::decryptionError(std::string id)
+{
+        if (this->thread() != QThread::currentThread())
+                nhlog::db()->warn("{} called from a different thread!", __func__);
+
+        if (id.empty())
+                return olm::DecryptionErrorCode::NoError;
+
+        IdIndex index{room_id_, std::move(id)};
+        auto edits_ = edits(index.id);
+        if (!edits_.empty()) {
+                index.id = mtx::accessors::event_id(edits_.back());
+                auto event_ptr =
+                  new mtx::events::collections::TimelineEvents(std::move(edits_.back()));
+                events_by_id_.insert(index, event_ptr);
+        }
+
+        auto event_ptr = events_by_id_.object(index);
+        if (!event_ptr) {
+                auto event = cache::client()->getEvent(room_id_, index.id);
+                if (!event) {
+                        return olm::DecryptionErrorCode::NoError;
+                }
+                event_ptr = new mtx::events::collections::TimelineEvents(std::move(event->data));
+                events_by_id_.insert(index, event_ptr);
+        }
+
+        if (auto encrypted =
+              std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(event_ptr)) {
+                auto decrypted = decryptEvent(index, *encrypted);
+                return decrypted->error;
+        }
+
+        return olm::DecryptionErrorCode::NoError;
+}
+
 void
 EventStore::fetchMore()
 {
diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h
index 7c404102..59c1c7c0 100644
--- a/src/timeline/EventStore.h
+++ b/src/timeline/EventStore.h
@@ -15,6 +15,7 @@
 #include <mtx/responses/messages.hpp>
 #include <mtx/responses/sync.hpp>
 
+#include "Olm.h"
 #include "Reaction.h"
 
 class EventStore : public QObject
@@ -78,6 +79,9 @@ public:
         mtx::events::collections::TimelineEvents *get(int idx, bool decrypt = true);
 
         QVariantList reactions(const std::string &event_id);
+        olm::DecryptionErrorCode decryptionError(std::string id);
+        void requestSession(const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &ev,
+                            bool manual);
 
         int size() const
         {
@@ -119,7 +123,7 @@ public slots:
 
 private:
         std::vector<mtx::events::collections::TimelineEvents> edits(const std::string &event_id);
-        mtx::events::collections::TimelineEvents *decryptEvent(
+        olm::DecryptionResult *decryptEvent(
           const IdIndex &idx,
           const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &e);
         void handle_room_verification(mtx::events::collections::TimelineEvents event);
@@ -129,7 +133,7 @@ private:
         uint64_t first = std::numeric_limits<uint64_t>::max(),
                  last  = std::numeric_limits<uint64_t>::max();
 
-        static QCache<IdIndex, mtx::events::collections::TimelineEvents> decryptedEvents_;
+        static QCache<IdIndex, olm::DecryptionResult> decryptedEvents_;
         static QCache<Index, mtx::events::collections::TimelineEvents> events_;
         static QCache<IdIndex, mtx::events::collections::TimelineEvents> events_by_id_;
 
@@ -137,6 +141,7 @@ private:
         {
                 std::string request_id;
                 std::vector<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>> events;
+                qint64 requested_at;
         };
         std::map<std::string, PendingKeyRequests> pending_key_requests;
 
diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp
index f7f377fb..f4c927ac 100644
--- a/src/timeline/RoomlistModel.cpp
+++ b/src/timeline/RoomlistModel.cpp
@@ -533,6 +533,8 @@ RoomlistModel::initializeRooms()
         for (const auto &id : cache::client()->roomIds())
                 addRoom(id, true);
 
+        nhlog::db()->info("Restored {} rooms from cache", rowCount());
+
         endResetModel();
 }
 
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index ee5564a5..99e00a67 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -28,9 +28,9 @@
 #include "MemberList.h"
 #include "MxcImageProvider.h"
 #include "Olm.h"
+#include "ReadReceiptsModel.h"
 #include "TimelineViewManager.h"
 #include "Utils.h"
-#include "dialogs/RawMessage.h"
 
 Q_DECLARE_METATYPE(QModelIndex)
 
@@ -308,6 +308,15 @@ qml_mtx_events::fromRoomEventType(qml_mtx_events::EventType t)
         case qml_mtx_events::KeyVerificationDone:
         case qml_mtx_events::KeyVerificationReady:
                 return mtx::events::EventType::RoomMessage;
+                //! m.image_pack, currently im.ponies.room_emotes
+        case qml_mtx_events::ImagePackInRoom:
+                return mtx::events::EventType::ImagePackRooms;
+        //! m.image_pack, currently im.ponies.user_emotes
+        case qml_mtx_events::ImagePackInAccountData:
+                return mtx::events::EventType::ImagePackInAccountData;
+        //! m.image_pack.rooms, currently im.ponies.emote_rooms
+        case qml_mtx_events::ImagePackRooms:
+                return mtx::events::EventType::ImagePackRooms;
         default:
                 return mtx::events::EventType::Unsupported;
         };
@@ -443,6 +452,7 @@ TimelineModel::roleNames() const
           {IsEditable, "isEditable"},
           {IsEncrypted, "isEncrypted"},
           {Trustlevel, "trustlevel"},
+          {EncryptionError, "encryptionError"},
           {ReplyTo, "replyTo"},
           {Reactions, "reactions"},
           {RoomId, "roomId"},
@@ -630,6 +640,9 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
                 return crypto::Trust::Unverified;
         }
 
+        case EncryptionError:
+                return events.decryptionError(event_id(event));
+
         case ReplyTo:
                 return QVariant(QString::fromStdString(relations(event).reply_to().value_or("")));
         case Reactions: {
@@ -681,6 +694,7 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
                 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)));
+                m.insert(names[EncryptionError], data(event, static_cast<int>(EncryptionError)));
 
                 return QVariant(m);
         }
@@ -1025,14 +1039,13 @@ TimelineModel::formatDateSeparator(QDate date) const
 }
 
 void
-TimelineModel::viewRawMessage(QString id) const
+TimelineModel::viewRawMessage(QString id)
 {
         auto e = events.get(id.toStdString(), "", false);
         if (!e)
                 return;
         std::string ev = mtx::accessors::serialize_event(*e).dump(4);
-        auto dialog    = new dialogs::RawMessage(QString::fromStdString(ev));
-        Q_UNUSED(dialog);
+        emit showRawMessageDialog(QString::fromStdString(ev));
 }
 
 void
@@ -1046,15 +1059,14 @@ TimelineModel::forwardMessage(QString eventId, QString roomId)
 }
 
 void
-TimelineModel::viewDecryptedRawMessage(QString id) const
+TimelineModel::viewDecryptedRawMessage(QString id)
 {
         auto e = events.get(id.toStdString(), "");
         if (!e)
                 return;
 
         std::string ev = mtx::accessors::serialize_event(*e).dump(4);
-        auto dialog    = new dialogs::RawMessage(QString::fromStdString(ev));
-        Q_UNUSED(dialog);
+        emit showRawMessageDialog(QString::fromStdString(ev));
 }
 
 void
@@ -1089,9 +1101,9 @@ TimelineModel::relatedInfo(QString id)
 }
 
 void
-TimelineModel::readReceiptsAction(QString id) const
+TimelineModel::showReadReceipts(QString id)
 {
-        MainWindow::instance()->openReadReceiptsDialog(id);
+        emit openReadReceiptsDialog(new ReadReceiptsProxy{id, roomId(), this});
 }
 
 void
@@ -1545,6 +1557,17 @@ TimelineModel::scrollTimerEvent()
 }
 
 void
+TimelineModel::requestKeyForEvent(QString id)
+{
+        auto encrypted_event = events.get(id.toStdString(), "", false);
+        if (encrypted_event) {
+                if (auto ev = std::get_if<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>>(
+                      encrypted_event))
+                        events.requestSession(*ev, true);
+        }
+}
+
+void
 TimelineModel::copyLinkToEvent(QString eventId) const
 {
         QStringList vias;
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 0e2ce153..ad7cfbbb 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -20,6 +20,7 @@
 #include "InviteesModel.h"
 #include "MemberList.h"
 #include "Permissions.h"
+#include "ReadReceiptsModel.h"
 #include "ui/RoomSettings.h"
 #include "ui/UserProfile.h"
 
@@ -106,7 +107,13 @@ enum EventType
         KeyVerificationCancel,
         KeyVerificationKey,
         KeyVerificationDone,
-        KeyVerificationReady
+        KeyVerificationReady,
+        //! m.image_pack, currently im.ponies.room_emotes
+        ImagePackInRoom,
+        //! m.image_pack, currently im.ponies.user_emotes
+        ImagePackInAccountData,
+        //! m.image_pack.rooms, currently im.ponies.emote_rooms
+        ImagePackRooms,
 };
 Q_ENUM_NS(EventType)
 mtx::events::EventType fromRoomEventType(qml_mtx_events::EventType);
@@ -205,6 +212,7 @@ public:
                 IsEditable,
                 IsEncrypted,
                 Trustlevel,
+                EncryptionError,
                 ReplyTo,
                 Reactions,
                 RoomId,
@@ -235,13 +243,13 @@ public:
         Q_INVOKABLE QString formatGuestAccessEvent(QString id);
         Q_INVOKABLE QString formatPowerLevelEvent(QString id);
 
-        Q_INVOKABLE void viewRawMessage(QString id) const;
+        Q_INVOKABLE void viewRawMessage(QString id);
         Q_INVOKABLE void forwardMessage(QString eventId, QString roomId);
-        Q_INVOKABLE void viewDecryptedRawMessage(QString id) const;
+        Q_INVOKABLE void viewDecryptedRawMessage(QString id);
         Q_INVOKABLE void openUserProfile(QString userid);
         Q_INVOKABLE void editAction(QString id);
         Q_INVOKABLE void replyAction(QString id);
-        Q_INVOKABLE void readReceiptsAction(QString id) const;
+        Q_INVOKABLE void showReadReceipts(QString id);
         Q_INVOKABLE void redactEvent(QString id);
         Q_INVOKABLE int idToIndex(QString id) const;
         Q_INVOKABLE QString indexToId(int index) const;
@@ -257,6 +265,8 @@ public:
                 endResetModel();
         }
 
+        Q_INVOKABLE void requestKeyForEvent(QString id);
+
         std::vector<::Reaction> reactions(const std::string &event_id)
         {
                 auto list = events.reactions(event_id);
@@ -348,6 +358,8 @@ signals:
         void typingUsersChanged(std::vector<QString> users);
         void replyChanged(QString reply);
         void editChanged(QString reply);
+        void openReadReceiptsDialog(ReadReceiptsProxy *rr);
+        void showRawMessageDialog(QString rawMessage);
         void paginationInProgressChanged(const bool);
         void newCallEvent(const mtx::events::collections::TimelineEvents &event);
         void scrollToIndex(int index);
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index fd5528d8..da68d503 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -162,6 +162,8 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
                                          "MtxEvent",
                                          "Can't instantiate enum!");
         qmlRegisterUncreatableMetaObject(
+          olm::staticMetaObject, "im.nheko", 1, 0, "Olm", "Can't instantiate enum!");
+        qmlRegisterUncreatableMetaObject(
           crypto::staticMetaObject, "im.nheko", 1, 0, "Crypto", "Can't instantiate enum!");
         qmlRegisterUncreatableMetaObject(verification::staticMetaObject,
                                          "im.nheko",
@@ -210,6 +212,12 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
           0,
           "InviteesModel",
           "InviteesModel needs to be instantiated on the C++ side");
+        qmlRegisterUncreatableType<ReadReceiptsProxy>(
+          "im.nheko",
+          1,
+          0,
+          "ReadReceiptsProxy",
+          "ReadReceiptsProxy needs to be instantiated on the C++ side");
 
         static auto self = this;
         qmlRegisterSingletonType<MainWindow>(