From 9f798e76ede3672d91276b1be7dd20de5459c9df Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Thu, 17 Jun 2021 22:27:37 +0300 Subject: [PATCH 1/4] Allow editing unsent messages As of 0db4d71ec2483c7ac5a7b536737fee8fc53a76d7 (Prevent edits of unsent messages), messages that are edits of (or replies to) unsent messages were not allowed. This change was made because otherwise the edits were discarded due to use of txnid rather than mxid in the "m.relates_to" object. Remove this restriction and fix the issue by replacing txnid with mxid in all related events when the message is sent (and we obtain mxid from the server). --- src/Cache.cpp | 21 +++++++++++++++++++++ src/Cache_p.h | 3 +++ src/timeline/EventStore.cpp | 32 ++++++++++++++++++++++++++++++++ src/timeline/TimelineModel.cpp | 18 +++++++++++------- 4 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/Cache.cpp b/src/Cache.cpp index 0d75ac51..2178bbfb 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -1681,6 +1681,27 @@ Cache::storeEvent(const std::string &room_id, txn.commit(); } +void +Cache::replaceEvent(const std::string &room_id, + const std::string &event_id, + const mtx::events::collections::TimelineEvent &event) +{ + auto txn = lmdb::txn::begin(env_); + auto eventsDb = getEventsDb(txn, room_id); + auto relationsDb = getRelationsDb(txn, room_id); + auto event_json = mtx::accessors::serialize_event(event.data).dump(); + + { + eventsDb.del(txn, event_id); + eventsDb.put(txn, event_id, event_json); + for (auto relation : mtx::accessors::relations(event.data).relations) { + relationsDb.put(txn, relation.event_id, event_id); + } + } + + txn.commit(); +} + std::vector Cache::relatedEvents(const std::string &room_id, const std::string &event_id) { diff --git a/src/Cache_p.h b/src/Cache_p.h index f2911622..669f1895 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -184,6 +184,9 @@ public: void storeEvent(const std::string &room_id, const std::string &event_id, const mtx::events::collections::TimelineEvent &event); + void replaceEvent(const std::string &room_id, + const std::string &event_id, + const mtx::events::collections::TimelineEvent &event); std::vector relatedEvents(const std::string &room_id, const std::string &event_id); diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 4a9f0fff..3667433b 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -185,6 +185,33 @@ EventStore::EventStore(std::string room_id, QObject *) [this](std::string txn_id, std::string event_id) { nhlog::ui()->debug("sent {}", txn_id); + // Replace the event_id in pending edits/replies/redactions with the actual + // event_id of this event + for (auto related_event_id : cache::client()->relatedEvents(room_id_, txn_id)) { + if (cache::client()->getEvent(room_id_, related_event_id)) { + auto related_event = + cache::client()->getEvent(room_id_, related_event_id).value(); + auto relations = mtx::accessors::relations(related_event.data); + + for (mtx::common::Relation &rel : relations.relations) { + if (rel.event_id == txn_id) + rel.event_id = event_id; + } + + mtx::accessors::set_relations(related_event.data, relations); + + cache::client()->replaceEvent( + room_id_, related_event_id, related_event); + + auto id = idToIndex(event_id); + + events_by_id_.remove({room_id_, related_event_id}); + events_.remove({room_id_, toInternalIdx(*id)}); + + emit dataChanged(*id, *id); + } + } + http::client()->read_event( room_id_, event_id, [this, event_id](mtx::http::RequestErr err) { if (err) { @@ -193,6 +220,11 @@ EventStore::EventStore(std::string room_id, QObject *) } }); + auto id = idToIndex(event_id); + + if (id) + emit dataChanged(id.value(), id.value()); + cache::client()->removePendingStatus(room_id_, txn_id); this->current_txn = ""; this->current_txn_error_count = 0; diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index f29f929e..e2e5551b 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -375,6 +375,15 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj connect(&events, &EventStore::updateFlowEventId, this, [this](std::string event_id) { this->updateFlowEventId(event_id); }); + connect(&events, + &EventStore::messageSent, + this, + [this](std::string txn_id, std::string event_id) { + if (edit_.toStdString() == txn_id) { + edit_ = QString::fromStdString(event_id); + emit editChanged(edit_); + } + }); showEventTimer.callOnTimeout(this, &TimelineModel::scrollTimerEvent); } @@ -568,10 +577,8 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r case IsEdited: return QVariant(relations(event).replaces().has_value()); case IsEditable: - return QVariant(!is_state_event(event) && - mtx::accessors::sender(event) == - http::client()->user_id().to_string() && - !event_id(event).empty() && event_id(event).front() == '$'); + return QVariant(!is_state_event(event) && mtx::accessors::sender(event) == + http::client()->user_id().to_string()); case IsEncrypted: { auto id = event_id(event); auto encrypted_event = events.get(id, "", false); @@ -1796,9 +1803,6 @@ TimelineModel::formatMemberEvent(QString id) void TimelineModel::setEdit(QString newEdit) { - if (edit_.startsWith('m')) - return; - if (newEdit.isEmpty()) { resetEdit(); return; From f8d2564e462abbaf4d33f089d1fb8af7d5100234 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Fri, 18 Jun 2021 20:25:44 +0300 Subject: [PATCH 2/4] fixup! Allow editing unsent messages --- src/timeline/EventStore.cpp | 25 ++++++++++++++++++------- src/timeline/TimelineModel.cpp | 3 +++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 3667433b..956698da 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -193,6 +193,19 @@ EventStore::EventStore(std::string room_id, QObject *) cache::client()->getEvent(room_id_, related_event_id).value(); auto relations = mtx::accessors::relations(related_event.data); + // Replace the blockquote in fallback reply + auto related_text = + std::get_if>( + &related_event.data); + if (related_text && relations.reply_to() == txn_id) { + size_t index = + related_text->content.formatted_body.find(txn_id); + if (index != std::string::npos) { + related_text->content.formatted_body.replace( + index, event_id.length(), event_id); + } + } + for (mtx::common::Relation &rel : relations.relations) { if (rel.event_id == txn_id) rel.event_id = event_id; @@ -203,12 +216,10 @@ EventStore::EventStore(std::string room_id, QObject *) cache::client()->replaceEvent( room_id_, related_event_id, related_event); - auto id = idToIndex(event_id); + auto idx = idToIndex(related_event_id); events_by_id_.remove({room_id_, related_event_id}); - events_.remove({room_id_, toInternalIdx(*id)}); - - emit dataChanged(*id, *id); + events_.remove({room_id_, toInternalIdx(*idx)}); } } @@ -220,10 +231,10 @@ EventStore::EventStore(std::string room_id, QObject *) } }); - auto id = idToIndex(event_id); + auto idx = idToIndex(event_id); - if (id) - emit dataChanged(id.value(), id.value()); + if (idx) + emit dataChanged(*idx, *idx); cache::client()->removePendingStatus(room_id_, txn_id); this->current_txn = ""; diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index e2e5551b..321179b4 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -383,6 +383,9 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj edit_ = QString::fromStdString(event_id); emit editChanged(edit_); } + if (reply_.toStdString() == txn_id) { + reply_ = QString::fromStdString(event_id); + } }); showEventTimer.callOnTimeout(this, &TimelineModel::scrollTimerEvent); From 42bf8799ee63874f4abbaa4e89b54a0076f0ba73 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Sat, 19 Jun 2021 23:11:34 +0300 Subject: [PATCH 3/4] fixup! Allow editing unsent messages --- src/timeline/TimelineModel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 321179b4..4bff15eb 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -385,6 +385,7 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj } if (reply_.toStdString() == txn_id) { reply_ = QString::fromStdString(event_id); + emit replyChanged(reply_); } }); From 358a39e6d2a5db9181a6dac1cb000ac8b85e15f2 Mon Sep 17 00:00:00 2001 From: Alexander Bantyev Date: Sat, 19 Jun 2021 23:26:21 +0300 Subject: [PATCH 4/4] fixup! Allow editing unsent messages --- src/timeline/EventStore.cpp | 6 +++++- src/timeline/TimelineModel.cpp | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp index 956698da..04f7ef76 100644 --- a/src/timeline/EventStore.cpp +++ b/src/timeline/EventStore.cpp @@ -186,7 +186,11 @@ EventStore::EventStore(std::string room_id, QObject *) nhlog::ui()->debug("sent {}", txn_id); // Replace the event_id in pending edits/replies/redactions with the actual - // event_id of this event + // event_id of this event. This allows one to edit and reply to events that are + // currently pending. + + // FIXME (introduced by balsoft): this doesn't work for encrypted events, but + // allegedly it's hard to fix so I'll leave my first contribution at that for (auto related_event_id : cache::client()->relatedEvents(room_id_, txn_id)) { if (cache::client()->getEvent(room_id_, related_event_id)) { auto related_event = diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 4bff15eb..99547b15 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -375,6 +375,8 @@ TimelineModel::TimelineModel(TimelineViewManager *manager, QString room_id, QObj connect(&events, &EventStore::updateFlowEventId, this, [this](std::string event_id) { this->updateFlowEventId(event_id); }); + // When a message is sent, check if the current edit/reply relates to that message, + // and update the event_id so that it points to the sent message and not the pending one. connect(&events, &EventStore::messageSent, this,