summary refs log tree commit diff
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2020-08-09 23:36:47 +0200
committerNicolas Werner <nicolas.werner@hotmail.de>2020-08-09 23:36:47 +0200
commit14a0aac74873c27c0454d206848f27b4eec123ae (patch)
treeb379bb81158eb8417d8da9b75b1835c7ec07e84c
parentTry to fix variable timestamp width (diff)
downloadnheko-14a0aac74873c27c0454d206848f27b4eec123ae.tar.xz
Add /clear-timeline command
-rw-r--r--src/Cache.cpp118
-rw-r--r--src/Cache_p.h3
-rw-r--r--src/ChatPage.cpp5
-rw-r--r--src/TextInputWidget.cpp24
-rw-r--r--src/TextInputWidget.h1
-rw-r--r--src/timeline/EventStore.cpp20
-rw-r--r--src/timeline/EventStore.h1
-rw-r--r--src/timeline/TimelineModel.h1
-rw-r--r--src/timeline/TimelineViewManager.h6
9 files changed, 157 insertions, 22 deletions
diff --git a/src/Cache.cpp b/src/Cache.cpp
index 0c692d07..0d879584 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -2304,6 +2304,11 @@ Cache::saveTimelineMessages(lmdb::txn &txn,
 
                 lmdb::val event_id = event_id_val;
 
+                json orderEntry        = json::object();
+                orderEntry["event_id"] = event_id_val;
+                if (first && !res.prev_batch.empty())
+                        orderEntry["prev_batch"] = res.prev_batch;
+
                 lmdb::val txn_order;
                 if (!txn_id.empty() &&
                     lmdb::dbi_get(txn, evToOrderDb, lmdb::val(txn_id), txn_order)) {
@@ -2317,7 +2322,7 @@ Cache::saveTimelineMessages(lmdb::txn &txn,
                                 lmdb::dbi_del(txn, msg2orderDb, lmdb::val(txn_id));
                         }
 
-                        lmdb::dbi_put(txn, orderDb, txn_order, event_id);
+                        lmdb::dbi_put(txn, orderDb, txn_order, lmdb::val(orderEntry.dump()));
                         lmdb::dbi_put(txn, evToOrderDb, event_id, txn_order);
                         lmdb::dbi_del(txn, evToOrderDb, lmdb::val(txn_id));
 
@@ -2389,10 +2394,6 @@ Cache::saveTimelineMessages(lmdb::txn &txn,
 
                         ++index;
 
-                        json orderEntry        = json::object();
-                        orderEntry["event_id"] = event_id_val;
-                        if (first && !res.prev_batch.empty())
-                                orderEntry["prev_batch"] = res.prev_batch;
                         first = false;
 
                         nhlog::db()->debug("saving '{}'", orderEntry.dump());
@@ -2440,6 +2441,7 @@ Cache::saveOldMessages(const std::string &room_id, const mtx::responses::Message
         auto relationsDb = getRelationsDb(txn, room_id);
 
         auto orderDb     = getEventOrderDb(txn, room_id);
+        auto evToOrderDb = getEventToOrderDb(txn, room_id);
         auto msg2orderDb = getMessageToOrderDb(txn, room_id);
         auto order2msgDb = getOrderToMessageDb(txn, room_id);
 
@@ -2483,6 +2485,7 @@ Cache::saveOldMessages(const std::string &room_id, const mtx::responses::Message
 
                 lmdb::dbi_put(
                   txn, orderDb, lmdb::val(&index, sizeof(index)), lmdb::val(orderEntry.dump()));
+                lmdb::dbi_put(txn, evToOrderDb, event_id, lmdb::val(&index, sizeof(index)));
 
                 // TODO(Nico): Allow blacklisting more event types in UI
                 if (event["type"] != "m.reaction" && event["type"] != "m.dummy") {
@@ -2516,6 +2519,94 @@ Cache::saveOldMessages(const std::string &room_id, const mtx::responses::Message
         return msgIndex;
 }
 
+void
+Cache::clearTimeline(const std::string &room_id)
+{
+        auto txn         = lmdb::txn::begin(env_);
+        auto eventsDb    = getEventsDb(txn, room_id);
+        auto relationsDb = getRelationsDb(txn, room_id);
+
+        auto orderDb     = getEventOrderDb(txn, room_id);
+        auto evToOrderDb = getEventToOrderDb(txn, room_id);
+        auto msg2orderDb = getMessageToOrderDb(txn, room_id);
+        auto order2msgDb = getOrderToMessageDb(txn, room_id);
+
+        lmdb::val indexVal, val;
+        auto cursor = lmdb::cursor::open(txn, orderDb);
+
+        bool start                   = true;
+        bool passed_pagination_token = false;
+        while (cursor.get(indexVal, val, start ? MDB_LAST : MDB_PREV)) {
+                start = false;
+                json obj;
+
+                try {
+                        obj = json::parse(std::string_view(val.data(), val.size()));
+                } catch (std::exception &) {
+                        // workaround bug in the initial db format, where we sometimes didn't store
+                        // json...
+                        obj = {{"event_id", std::string(val.data(), val.size())}};
+                }
+
+                if (passed_pagination_token) {
+                        if (obj.count("event_id") != 0) {
+                                lmdb::val event_id = obj["event_id"].get<std::string>();
+                                lmdb::dbi_del(txn, evToOrderDb, event_id);
+                                lmdb::dbi_del(txn, eventsDb, event_id);
+
+                                lmdb::dbi_del(txn, relationsDb, event_id);
+
+                                lmdb::val order{};
+                                bool exists = lmdb::dbi_get(txn, msg2orderDb, event_id, order);
+                                if (exists) {
+                                        lmdb::dbi_del(txn, order2msgDb, order);
+                                        lmdb::dbi_del(txn, msg2orderDb, event_id);
+                                }
+                        }
+                        lmdb::cursor_del(cursor);
+                } else {
+                        if (obj.count("prev_batch") != 0)
+                                passed_pagination_token = true;
+                }
+        }
+
+        auto msgCursor = lmdb::cursor::open(txn, order2msgDb);
+        start          = true;
+        while (msgCursor.get(indexVal, val, start ? MDB_LAST : MDB_PREV)) {
+                start = false;
+
+                lmdb::val eventId;
+                bool innerStart = true;
+                bool found      = false;
+                while (cursor.get(indexVal, eventId, innerStart ? MDB_LAST : MDB_PREV)) {
+                        innerStart = false;
+
+                        json obj;
+                        try {
+                                obj = json::parse(std::string_view(eventId.data(), eventId.size()));
+                        } catch (std::exception &) {
+                                obj = {{"event_id", std::string(eventId.data(), eventId.size())}};
+                        }
+
+                        if (obj["event_id"] == std::string(val.data(), val.size())) {
+                                found = true;
+                                break;
+                        }
+                }
+
+                if (!found)
+                        break;
+        }
+
+        do {
+                lmdb::cursor_del(msgCursor);
+        } while (msgCursor.get(indexVal, val, MDB_PREV));
+
+        cursor.close();
+        msgCursor.close();
+        txn.commit();
+}
+
 mtx::responses::Notifications
 Cache::getTimelineMentionsForRoom(lmdb::txn &txn, const std::string &room_id)
 {
@@ -2654,11 +2745,13 @@ Cache::deleteOldMessages()
         auto room_ids = getRoomIds(txn);
 
         for (const auto &room_id : room_ids) {
-                auto orderDb  = getEventOrderDb(txn, room_id);
-                auto o2m      = getOrderToMessageDb(txn, room_id);
-                auto m2o      = getMessageToOrderDb(txn, room_id);
-                auto eventsDb = getEventsDb(txn, room_id);
-                auto cursor   = lmdb::cursor::open(txn, orderDb);
+                auto orderDb     = getEventOrderDb(txn, room_id);
+                auto evToOrderDb = getEventToOrderDb(txn, room_id);
+                auto o2m         = getOrderToMessageDb(txn, room_id);
+                auto m2o         = getMessageToOrderDb(txn, room_id);
+                auto eventsDb    = getEventsDb(txn, room_id);
+                auto relationsDb = getRelationsDb(txn, room_id);
+                auto cursor      = lmdb::cursor::open(txn, orderDb);
 
                 uint64_t first, last;
                 if (cursor.get(indexVal, val, MDB_LAST)) {
@@ -2678,14 +2771,17 @@ Cache::deleteOldMessages()
 
                 bool start = true;
                 while (cursor.get(indexVal, val, start ? MDB_FIRST : MDB_NEXT) &&
-                       message_count-- < MAX_RESTORED_MESSAGES) {
+                       message_count-- > MAX_RESTORED_MESSAGES) {
                         start    = false;
                         auto obj = json::parse(std::string_view(val.data(), val.size()));
 
                         if (obj.count("event_id") != 0) {
                                 lmdb::val event_id = obj["event_id"].get<std::string>();
+                                lmdb::dbi_del(txn, evToOrderDb, event_id);
                                 lmdb::dbi_del(txn, eventsDb, event_id);
 
+                                lmdb::dbi_del(txn, relationsDb, event_id);
+
                                 lmdb::val order{};
                                 bool exists = lmdb::dbi_get(txn, m2o, event_id, order);
                                 if (exists) {
diff --git a/src/Cache_p.h b/src/Cache_p.h
index 61d91b0c..d3ec6ee0 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -208,6 +208,9 @@ public:
           const std::string &room_id);
         void removePendingStatus(const std::string &room_id, const std::string &txn_id);
 
+        //! clear timeline keeping only the latest batch
+        void clearTimeline(const std::string &room_id);
+
         //! Remove old unused data.
         void deleteOldMessages();
         void deleteOldData() noexcept;
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 518be31c..63d13fb9 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -155,6 +155,11 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
                 trySync();
         });
 
+        connect(text_input_,
+                &TextInputWidget::clearRoomTimeline,
+                view_manager_,
+                &TimelineViewManager::clearCurrentRoomTimeline);
+
         connect(
           new QShortcut(QKeySequence("Ctrl+Down"), this), &QShortcut::activated, this, [this]() {
                   if (isVisible())
diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp
index 3e3915bb..91846230 100644
--- a/src/TextInputWidget.cpp
+++ b/src/TextInputWidget.cpp
@@ -566,27 +566,29 @@ void
 TextInputWidget::command(QString command, QString args)
 {
         if (command == "me") {
-                sendEmoteMessage(args);
+                emit sendEmoteMessage(args);
         } else if (command == "join") {
-                sendJoinRoomRequest(args);
+                emit sendJoinRoomRequest(args);
         } else if (command == "invite") {
-                sendInviteRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
+                emit sendInviteRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
         } else if (command == "kick") {
-                sendKickRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
+                emit sendKickRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
         } else if (command == "ban") {
-                sendBanRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
+                emit sendBanRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
         } else if (command == "unban") {
-                sendUnbanRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
+                emit sendUnbanRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
         } else if (command == "roomnick") {
-                changeRoomNick(args);
+                emit changeRoomNick(args);
         } else if (command == "shrug") {
-                sendTextMessage("¯\\_(ツ)_/¯");
+                emit sendTextMessage("¯\\_(ツ)_/¯");
         } else if (command == "fliptable") {
-                sendTextMessage("(╯°□°)╯︵ ┻━┻");
+                emit sendTextMessage("(╯°□°)╯︵ ┻━┻");
         } else if (command == "unfliptable") {
-                sendTextMessage(" ┯━┯╭( º _ º╭)");
+                emit sendTextMessage(" ┯━┯╭( º _ º╭)");
         } else if (command == "sovietflip") {
-                sendTextMessage("ノ┬─┬ノ ︵ ( \\o°o)\\");
+                emit sendTextMessage("ノ┬─┬ノ ︵ ( \\o°o)\\");
+        } else if (command == "clear-timeline") {
+                emit clearRoomTimeline();
         }
 }
 
diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h
index a0105eb0..cbb6ea95 100644
--- a/src/TextInputWidget.h
+++ b/src/TextInputWidget.h
@@ -156,6 +156,7 @@ private slots:
 signals:
         void sendTextMessage(const QString &msg);
         void sendEmoteMessage(QString msg);
+        void clearRoomTimeline();
         void heightChanged(int height);
 
         void uploadMedia(const QSharedPointer<QIODevice> data,
diff --git a/src/timeline/EventStore.cpp b/src/timeline/EventStore.cpp
index a983fe01..fca1d31d 100644
--- a/src/timeline/EventStore.cpp
+++ b/src/timeline/EventStore.cpp
@@ -176,6 +176,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())
diff --git a/src/timeline/EventStore.h b/src/timeline/EventStore.h
index b5c17d10..d4353a18 100644
--- a/src/timeline/EventStore.h
+++ b/src/timeline/EventStore.h
@@ -101,6 +101,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.h b/src/timeline/TimelineModel.h
index f8a84f17..0bcf42b7 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -242,6 +242,7 @@ public slots:
                 }
         }
         void setDecryptDescription(bool decrypt) { decryptDescription = decrypt; }
+        void clearTimeline() { events.clearTimeline(); }
 
 private slots:
         void addPendingMessage(mtx::events::collections::TimelineEvents event);
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 63106916..20dbc3bb 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -92,6 +92,12 @@ public slots:
                                uint64_t dsize);
         void updateEncryptedDescriptions();
 
+        void clearCurrentRoomTimeline()
+        {
+                if (timeline_)
+                        timeline_->clearTimeline();
+        }
+
 private:
 #ifdef USE_QUICK_VIEW
         QQuickView *view;