From 208f9579118307d7749de9bf9be537bf1a1d2b27 Mon Sep 17 00:00:00 2001
From: Konstantinos Sideris <sideris.konstantin@gmail.com>
Date: Sat, 30 Dec 2017 17:29:57 +0200
Subject: [PATCH] Re-order room list based on activity

fixes #2
---
 include/ChatPage.h         |  8 ++++-
 include/RoomInfoListItem.h |  1 +
 include/RoomList.h         | 12 +++++++-
 include/UserSettingsPage.h | 16 +++++++++-
 src/ChatPage.cc            |  8 +++--
 src/MainWindow.cc          |  2 +-
 src/RoomList.cc            | 62 +++++++++++++++++++++++++++++++++++++-
 src/UserSettingsPage.cc    | 32 ++++++++++++++------
 8 files changed, 125 insertions(+), 16 deletions(-)

diff --git a/include/ChatPage.h b/include/ChatPage.h
index 14d44ff32..24fc6a252 100644
--- a/include/ChatPage.h
+++ b/include/ChatPage.h
@@ -40,6 +40,7 @@ class TimelineViewManager;
 class TopRoomBar;
 class TypingDisplay;
 class UserInfoWidget;
+class UserSettings;
 
 constexpr int CONSENSUS_TIMEOUT      = 1000;
 constexpr int SHOW_CONTENT_TIMEOUT   = 3000;
@@ -50,7 +51,9 @@ class ChatPage : public QWidget
         Q_OBJECT
 
 public:
-        ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
+        ChatPage(QSharedPointer<MatrixClient> client,
+                 QSharedPointer<UserSettings> userSettings,
+                 QWidget *parent = 0);
         ~ChatPage();
 
         // Initialize all the components of the UI.
@@ -150,6 +153,9 @@ private:
         // Matrix Client API provider.
         QSharedPointer<MatrixClient> client_;
 
+        // Global user settings.
+        QSharedPointer<UserSettings> userSettings_;
+
         // LMDB wrapper.
         QSharedPointer<Cache> cache_;
 
diff --git a/include/RoomInfoListItem.h b/include/RoomInfoListItem.h
index bb8e0f1a8..799e95bb1 100644
--- a/include/RoomInfoListItem.h
+++ b/include/RoomInfoListItem.h
@@ -79,6 +79,7 @@ public:
 
         void setAvatar(const QImage &avatar_image);
         void setDescriptionMessage(const DescInfo &info);
+        DescInfo lastMessageInfo() const { return lastMsgInfo_; }
 
         QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; }
         QColor hoverBackgroundColor() const { return hoverBackgroundColor_; }
diff --git a/include/RoomList.h b/include/RoomList.h
index ed05e0bef..6b2151a24 100644
--- a/include/RoomList.h
+++ b/include/RoomList.h
@@ -36,6 +36,7 @@ class RoomInfoListItem;
 class RoomSettings;
 class RoomState;
 class Sync;
+class UserSettings;
 struct DescInfo;
 
 class RoomList : public QWidget
@@ -43,7 +44,9 @@ class RoomList : public QWidget
         Q_OBJECT
 
 public:
-        RoomList(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
+        RoomList(QSharedPointer<MatrixClient> client,
+                 QSharedPointer<UserSettings> userSettings,
+                 QWidget *parent = 0);
         ~RoomList();
 
         void setCache(QSharedPointer<Cache> cache) { cache_ = cache; }
@@ -81,6 +84,10 @@ public slots:
 
 protected:
         void paintEvent(QPaintEvent *event) override;
+        void leaveEvent(QEvent *event) override;
+
+private slots:
+        void sortRoomsByLastMessage();
 
 private:
         void calculateUnreadMessageCount();
@@ -101,4 +108,7 @@ private:
 
         QSharedPointer<MatrixClient> client_;
         QSharedPointer<Cache> cache_;
+        QSharedPointer<UserSettings> userSettings_;
+
+        bool isSortPending_ = false;
 };
diff --git a/include/UserSettingsPage.h b/include/UserSettingsPage.h
index 99a149c66..adaa3956c 100644
--- a/include/UserSettingsPage.h
+++ b/include/UserSettingsPage.h
@@ -38,14 +38,26 @@ public:
         void load();
         void applyTheme();
         void setTheme(QString theme);
-        void setTray(bool state);
+        void setTray(bool state)
+        {
+                isTrayEnabled_ = state;
+                save();
+        };
+
+        void setRoomOrdering(bool state)
+        {
+                isOrderingEnabled_ = state;
+                save();
+        };
 
         QString theme() const { return !theme_.isEmpty() ? theme_ : "light"; }
         bool isTrayEnabled() const { return isTrayEnabled_; }
+        bool isOrderingEnabled() const { return isOrderingEnabled_; }
 
 private:
         QString theme_;
         bool isTrayEnabled_;
+        bool isOrderingEnabled_;
 };
 
 class HorizontalLine : public QFrame
@@ -84,6 +96,8 @@ private:
         QSharedPointer<UserSettings> settings_;
 
         Toggle *trayToggle_;
+        Toggle *roomOrderToggle_;
+
         QComboBox *themeCombo_;
 
         int sideMargin_ = 0;
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index 8abd651b6..071fef71d 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -37,15 +37,19 @@
 #include "TopRoomBar.h"
 #include "TypingDisplay.h"
 #include "UserInfoWidget.h"
+#include "UserSettingsPage.h"
 
 #include "timeline/TimelineViewManager.h"
 
 constexpr int MAX_INITIAL_SYNC_FAILURES = 5;
 constexpr int SYNC_RETRY_TIMEOUT        = 10000;
 
-ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
+ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
+                   QSharedPointer<UserSettings> userSettings,
+                   QWidget *parent)
   : QWidget(parent)
   , client_(client)
+  , userSettings_{userSettings}
 {
         setObjectName("chatPage");
 
@@ -74,7 +78,7 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
           sidebarActions_, &SideBarActions::createRoom, client_.data(), &MatrixClient::createRoom);
 
         user_info_widget_ = new UserInfoWidget(sideBar_);
-        room_list_        = new RoomList(client, sideBar_);
+        room_list_        = new RoomList(client, userSettings_, sideBar_);
 
         sideBarLayout_->addWidget(user_info_widget_);
         sideBarLayout_->addWidget(room_list_);
diff --git a/src/MainWindow.cc b/src/MainWindow.cc
index 04b0e8e3c..39f58dacc 100644
--- a/src/MainWindow.cc
+++ b/src/MainWindow.cc
@@ -58,7 +58,7 @@ MainWindow::MainWindow(QWidget *parent)
         welcome_page_     = new WelcomePage(this);
         login_page_       = new LoginPage(client_, this);
         register_page_    = new RegisterPage(client_, this);
-        chat_page_        = new ChatPage(client_, this);
+        chat_page_        = new ChatPage(client_, userSettings_, this);
         userSettingsPage_ = new UserSettingsPage(userSettings_, this);
 
         // Initialize sliding widget manager.
diff --git a/src/RoomList.cc b/src/RoomList.cc
index ea13d7006..a47c73a6d 100644
--- a/src/RoomList.cc
+++ b/src/RoomList.cc
@@ -18,6 +18,7 @@
 #include <QBuffer>
 #include <QDebug>
 #include <QObject>
+#include <QTimer>
 
 #include "Cache.h"
 #include "MainWindow.h"
@@ -27,10 +28,14 @@
 #include "RoomList.h"
 #include "RoomSettings.h"
 #include "RoomState.h"
+#include "UserSettingsPage.h"
 
-RoomList::RoomList(QSharedPointer<MatrixClient> client, QWidget *parent)
+RoomList::RoomList(QSharedPointer<MatrixClient> client,
+                   QSharedPointer<UserSettings> userSettings,
+                   QWidget *parent)
   : QWidget(parent)
   , client_(client)
+  , userSettings_{userSettings}
 {
         setStyleSheet("border: none;");
         topLayout_ = new QVBoxLayout(this);
@@ -291,6 +296,61 @@ RoomList::updateRoomDescription(const QString &roomid, const DescInfo &info)
         }
 
         rooms_.value(roomid)->setDescriptionMessage(info);
+
+        if (underMouse()) {
+                // When the user hover out of the roomlist a sort will be triggered.
+                isSortPending_ = true;
+                return;
+        }
+
+        isSortPending_ = false;
+
+        emit sortRoomsByLastMessage();
+}
+
+void
+RoomList::sortRoomsByLastMessage()
+{
+        if (!userSettings_->isOrderingEnabled())
+                return;
+
+        isSortPending_ = false;
+
+        std::multimap<uint64_t, RoomInfoListItem *, std::greater<uint64_t>> times;
+
+        for (int ii = 0; ii < contentsLayout_->count(); ++ii) {
+                auto room = qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(ii)->widget());
+
+                if (!room)
+                        continue;
+
+                // Not a room message.
+                if (room->lastMessageInfo().userid.isEmpty())
+                        times.emplace(0, room);
+                else
+                        times.emplace(room->lastMessageInfo().datetime.toSecsSinceEpoch(), room);
+        }
+
+        for (auto it = times.cbegin(); it != times.cend(); ++it) {
+                const auto roomWidget   = it->second;
+                const auto currentIndex = contentsLayout_->indexOf(roomWidget);
+                const auto newIndex     = std::distance(times.cbegin(), it);
+
+                if (currentIndex == newIndex)
+                        continue;
+
+                contentsLayout_->removeWidget(roomWidget);
+                contentsLayout_->insertWidget(newIndex, roomWidget);
+        }
+}
+
+void
+RoomList::leaveEvent(QEvent *event)
+{
+        if (isSortPending_)
+                QTimer::singleShot(700, this, &RoomList::sortRoomsByLastMessage);
+
+        QWidget::leaveEvent(event);
 }
 
 void
diff --git a/src/UserSettingsPage.cc b/src/UserSettingsPage.cc
index a5851c57b..82cf23a77 100644
--- a/src/UserSettingsPage.cc
+++ b/src/UserSettingsPage.cc
@@ -33,8 +33,9 @@ void
 UserSettings::load()
 {
         QSettings settings;
-        isTrayEnabled_ = settings.value("user/window/tray", true).toBool();
-        theme_         = settings.value("user/theme", "light").toString();
+        isTrayEnabled_     = settings.value("user/window/tray", true).toBool();
+        isOrderingEnabled_ = settings.value("user/room_ordering", true).toBool();
+        theme_             = settings.value("user/theme", "light").toString();
 
         applyTheme();
 }
@@ -47,12 +48,6 @@ UserSettings::setTheme(QString theme)
         applyTheme();
 }
 
-void
-UserSettings::setTray(bool state)
-{
-        isTrayEnabled_ = state;
-        save();
-}
 void
 UserSettings::applyTheme()
 {
@@ -86,6 +81,7 @@ UserSettings::save()
         settings.setValue("tray", isTrayEnabled_);
         settings.endGroup();
 
+        settings.setValue("room_ordering", isOrderingEnabled_);
         settings.setValue("theme", theme());
         settings.endGroup();
 }
@@ -132,6 +128,17 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
         trayOptionLayout_->addWidget(trayLabel);
         trayOptionLayout_->addWidget(trayToggle_, 0, Qt::AlignBottom | Qt::AlignRight);
 
+        auto orderRoomLayout = new QHBoxLayout;
+        orderRoomLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
+        auto orderLabel  = new QLabel(tr("Re-order rooms based on activity"), this);
+        roomOrderToggle_ = new Toggle(this);
+        roomOrderToggle_->setActiveColor(QColor("#38A3D8"));
+        roomOrderToggle_->setInactiveColor(QColor("gray"));
+        orderLabel->setStyleSheet("font-size: 15px;");
+
+        orderRoomLayout->addWidget(orderLabel);
+        orderRoomLayout->addWidget(roomOrderToggle_, 0, Qt::AlignBottom | Qt::AlignRight);
+
         auto themeOptionLayout_ = new QHBoxLayout;
         themeOptionLayout_->setContentsMargins(0, OptionMargin, 0, OptionMargin);
         auto themeLabel_ = new QLabel(tr("App theme"), this);
@@ -155,6 +162,8 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
         mainLayout_->addWidget(new HorizontalLine(this));
         mainLayout_->addLayout(trayOptionLayout_);
         mainLayout_->addWidget(new HorizontalLine(this));
+        mainLayout_->addLayout(orderRoomLayout);
+        mainLayout_->addWidget(new HorizontalLine(this));
         mainLayout_->addLayout(themeOptionLayout_);
         mainLayout_->addWidget(new HorizontalLine(this));
 
@@ -171,6 +180,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
                 emit trayOptionChanged(!isDisabled);
         });
 
+        connect(roomOrderToggle_, &Toggle::toggled, this, [=](bool isDisabled) {
+                settings_->setRoomOrdering(!isDisabled);
+        });
+
         connect(backBtn_, &QPushButton::clicked, this, [=]() {
                 settings_->save();
                 emit moveBack();
@@ -181,7 +194,8 @@ void
 UserSettingsPage::showEvent(QShowEvent *)
 {
         restoreThemeCombo();
-        trayToggle_->setState(!settings_->isTrayEnabled()); // Treats true as "off"
+        trayToggle_->setState(!settings_->isTrayEnabled());          // Treats true as "off"
+        roomOrderToggle_->setState(!settings_->isOrderingEnabled()); // Treats true as "off"
 }
 
 void
-- 
GitLab