From 9d718fccf46f8d8c94d4fe7409c42cf082e3b4f4 Mon Sep 17 00:00:00 2001
From: Konstantinos Sideris <sideris.konstantin@gmail.com>
Date: Sat, 21 Jul 2018 12:09:23 +0300
Subject: [PATCH] Clear timeline widgets when they exceed a certain limit
 (#158)

That's a fix to deal with long running sessions which will end
up taking more & more memory given enough time.
---
 src/timeline/TimelineView.cpp | 44 +++++++++++++++++++++++++++++++++--
 src/timeline/TimelineView.h   |  3 +++
 2 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/src/timeline/TimelineView.cpp b/src/timeline/TimelineView.cpp
index 61a507b39..95cde0f4e 100644
--- a/src/timeline/TimelineView.cpp
+++ b/src/timeline/TimelineView.cpp
@@ -38,6 +38,10 @@
 
 using TimelineEvent = mtx::events::collections::TimelineEvents;
 
+//! Maximum number of widgets to keep in the timeline layout.
+constexpr int MAX_RETAINED_WIDGETS = 100;
+constexpr int MIN_SCROLLBAR_HANDLE = 60;
+
 //! Retrieve the timestamp of the event represented by the given widget.
 QDateTime
 getDate(QWidget *widget)
@@ -481,8 +485,7 @@ TimelineView::addEvents(const mtx::responses::Timeline &timeline)
 void
 TimelineView::init()
 {
-        QSettings settings;
-        local_user_ = settings.value("auth/user_id").toString();
+        local_user_ = utils::localUser();
 
         QIcon icon;
         icon.addFile(":/icons/icons/ui/angle-arrow-down.png");
@@ -965,6 +968,19 @@ TimelineView::showEvent(QShowEvent *event)
         QWidget::showEvent(event);
 }
 
+void
+TimelineView::hideEvent(QHideEvent *event)
+{
+        const auto handleHeight = scroll_area_->verticalScrollBar()->sizeHint().height();
+        const auto widgetsNum   = scroll_layout_->count();
+
+        // Remove widgets from the timeline to reduce the memory footprint.
+        if (handleHeight < MIN_SCROLLBAR_HANDLE && widgetsNum > MAX_RETAINED_WIDGETS)
+                clearTimeline();
+
+        QWidget::hideEvent(event);
+}
+
 bool
 TimelineView::event(QEvent *event)
 {
@@ -974,6 +990,30 @@ TimelineView::event(QEvent *event)
         return QWidget::event(event);
 }
 
+void
+TimelineView::clearTimeline()
+{
+        // Delete all widgets.
+        QLayoutItem *item;
+        while ((item = scroll_layout_->takeAt(0)) != nullptr) {
+                delete item->widget();
+                delete item;
+        }
+
+        // The next call to /messages will be without a prev token.
+        prev_batch_token_.clear();
+        eventIds_.clear();
+
+        // Clear queues with pending messages to be rendered.
+        bottomMessages_.clear();
+        topMessages_.clear();
+
+        firstSender_.clear();
+        lastSender_.clear();
+
+        scroll_layout_->addStretch(1);
+}
+
 void
 TimelineView::toggleScrollDownButton()
 {
diff --git a/src/timeline/TimelineView.h b/src/timeline/TimelineView.h
index 5c42415a3..d622b6988 100644
--- a/src/timeline/TimelineView.h
+++ b/src/timeline/TimelineView.h
@@ -161,6 +161,7 @@ signals:
 protected:
         void paintEvent(QPaintEvent *event) override;
         void showEvent(QShowEvent *event) override;
+        void hideEvent(QHideEvent *event) override;
         bool event(QEvent *event) override;
 
 private:
@@ -271,6 +272,8 @@ private:
 
         //! Store the event id associated with the given widget.
         void saveEventId(QWidget *widget);
+        //! Remove all widgets from the timeline layout.
+        void clearTimeline();
 
         QVBoxLayout *top_layout_;
         QVBoxLayout *scroll_layout_;
-- 
GitLab