From 9a5afd39189453b55f68b780bcb3339bb1fc05ca Mon Sep 17 00:00:00 2001
From: Ming Rui Zhang <mingrui.zhang@savoirfairelinux.com>
Date: Mon, 10 May 2021 15:46:37 -0400
Subject: [PATCH] accountcombobox: use QSortFilterProxyModel to filter out
 current account

Gitlab: #405

Change-Id: I364d7945739ef45319e0bb7834075ac52ec5c009
---
 src/accountadapter.cpp                        |  8 ++-
 src/accountadapter.h                          |  4 ++
 src/accountlistmodel.cpp                      | 47 +++-----------
 src/accountlistmodel.h                        | 61 ++++++++++++++++---
 src/mainview/MainView.qml                     |  6 --
 src/mainview/components/AccountComboBox.qml   | 34 +++++------
 .../components/AccountComboBoxPopup.qml       | 15 ++---
 .../components/AccountItemDelegate.qml        |  9 +--
 src/qmlregister.cpp                           |  2 +-
 9 files changed, 101 insertions(+), 85 deletions(-)

diff --git a/src/accountadapter.cpp b/src/accountadapter.cpp
index e29cc2550..8be5d784f 100644
--- a/src/accountadapter.cpp
+++ b/src/accountadapter.cpp
@@ -25,6 +25,7 @@
 
 #include "appsettingsmanager.h"
 #include "qtutils.h"
+#include "qmlregister.h"
 
 #include <QtConcurrent/QtConcurrent>
 
@@ -33,7 +34,12 @@ AccountAdapter::AccountAdapter(AppSettingsManager* settingsManager,
                                QObject* parent)
     : QmlAdapterBase(instance, parent)
     , settingsManager_(settingsManager)
-{}
+    , accSrcModel_(new AccountListModel(instance))
+    , accModel_(new CurrentAccountFilterModel(instance, accSrcModel_.get()))
+{
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, accModel_.get(), "CurrentAccountFilterModel");
+    QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, accSrcModel_.get(), "AccountListModel");
+}
 
 void
 AccountAdapter::safeInit()
diff --git a/src/accountadapter.h b/src/accountadapter.h
index 985781ab3..d749133b2 100644
--- a/src/accountadapter.h
+++ b/src/accountadapter.h
@@ -24,6 +24,7 @@
 #include <QSettings>
 #include <QString>
 
+#include "accountlistmodel.h"
 #include "lrcinstance.h"
 #include "utils.h"
 
@@ -134,5 +135,8 @@ private:
     QMetaObject::Connection registeredNameSavedConnection_;
 
     AppSettingsManager* settingsManager_;
+
+    QScopedPointer<AccountListModel> accSrcModel_;
+    QScopedPointer<CurrentAccountFilterModel> accModel_;
 };
 Q_DECLARE_METATYPE(AccountAdapter*)
diff --git a/src/accountlistmodel.cpp b/src/accountlistmodel.cpp
index 9c3bcb9df..4c253e71b 100644
--- a/src/accountlistmodel.cpp
+++ b/src/accountlistmodel.cpp
@@ -28,9 +28,11 @@
 #include "api/contact.h"
 #include "api/conversation.h"
 
-AccountListModel::AccountListModel(QObject* parent)
+AccountListModel::AccountListModel(LRCInstance* instance, QObject* parent)
     : AbstractListModelBase(parent)
-{}
+{
+    lrcInstance_ = instance;
+}
 
 AccountListModel::~AccountListModel() {}
 
@@ -92,47 +94,14 @@ AccountListModel::data(const QModelIndex& index, int role) const
 QHash<int, QByteArray>
 AccountListModel::roleNames() const
 {
+    using namespace AccountList;
     QHash<int, QByteArray> roles;
-    roles[Alias] = "Alias";
-    roles[Username] = "Username";
-    roles[Type] = "Type";
-    roles[Status] = "Status";
-    roles[ID] = "ID";
-    roles[PictureUid] = "PictureUid";
+#define X(role) roles[role] = #role;
+    ACC_ROLES
+#undef X
     return roles;
 }
 
-QModelIndex
-AccountListModel::index(int row, int column, const QModelIndex& parent) const
-{
-    Q_UNUSED(parent);
-    if (column != 0) {
-        return QModelIndex();
-    }
-
-    if (row >= 0 && row < rowCount()) {
-        return createIndex(row, column);
-    }
-    return QModelIndex();
-}
-
-QModelIndex
-AccountListModel::parent(const QModelIndex& child) const
-{
-    Q_UNUSED(child);
-    return QModelIndex();
-}
-
-Qt::ItemFlags
-AccountListModel::flags(const QModelIndex& index) const
-{
-    auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable;
-    if (!index.isValid()) {
-        return QAbstractItemModel::flags(index);
-    }
-    return flags;
-}
-
 void
 AccountListModel::reset()
 {
diff --git a/src/accountlistmodel.h b/src/accountlistmodel.h
index d8538fc38..eadd135e4 100644
--- a/src/accountlistmodel.h
+++ b/src/accountlistmodel.h
@@ -21,15 +21,62 @@
 
 #include "abstractlistmodelbase.h"
 
-class AccountListModel : public AbstractListModelBase
+#include <QSortFilterProxyModel>
+
+#define ACC_ROLES \
+    X(Alias) \
+    X(Username) \
+    X(Type) \
+    X(Status) \
+    X(ID) \
+    X(PictureUid)
+
+namespace AccountList {
+Q_NAMESPACE
+enum Role {
+    DummyRole = Qt::UserRole + 1,
+#define X(role) role,
+    ACC_ROLES
+#undef X
+};
+Q_ENUM_NS(Role)
+} // namespace AccountList
+
+/*
+ * The CurrentAccountFilterModel class
+ * is for the sole purpose of filtering out current account.
+ */
+class CurrentAccountFilterModel final : public QSortFilterProxyModel
 {
     Q_OBJECT
 
 public:
-    enum Role { Alias = Qt::UserRole + 1, Username, Type, Status, ID, PictureUid };
-    Q_ENUM(Role)
+    explicit CurrentAccountFilterModel(LRCInstance* lrcInstance,
+                                       QAbstractListModel* parent = nullptr)
+        : QSortFilterProxyModel(parent)
+        , lrcInstance_(lrcInstance)
+    {
+        setSourceModel(parent);
+    }
+
+    virtual bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override
+    {
+        // Accept all contacts in conversation list filtered with account type, except those in a call.
+        auto index = sourceModel()->index(sourceRow, 0, sourceParent);
+        auto accountID = sourceModel()->data(index, AccountList::ID);
+        return accountID != lrcInstance_->getCurrAccId();
+    }
 
-    explicit AccountListModel(QObject* parent = nullptr);
+protected:
+    LRCInstance* lrcInstance_ {nullptr};
+};
+
+class AccountListModel : public AbstractListModelBase
+{
+    Q_OBJECT
+
+public:
+    explicit AccountListModel(LRCInstance* instance, QObject* parent = nullptr);
     ~AccountListModel();
 
     /*
@@ -43,9 +90,6 @@ public:
      * Override role name as access point in qml.
      */
     QHash<int, QByteArray> roleNames() const override;
-    QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const;
-    QModelIndex parent(const QModelIndex& child) const;
-    Qt::ItemFlags flags(const QModelIndex& index) const;
 
     /*
      * This function is to reset the model when there's new account added.
@@ -57,6 +101,9 @@ public:
      */
     Q_INVOKABLE void updateAvatarUid(const QString& accountId);
 
+protected:
+    using Role = AccountList::Role;
+
 private:
     /*
      * Give a uuid for each account avatar and it will serve PictureUid role
diff --git a/src/mainview/MainView.qml b/src/mainview/MainView.qml
index 1c565dbb5..d4280cce3 100644
--- a/src/mainview/MainView.qml
+++ b/src/mainview/MainView.qml
@@ -350,12 +350,6 @@ Rectangle {
         }
     }
 
-    AccountListModel {
-        id: accountListModel
-
-        lrcInstance: LRCInstance
-    }
-
     SettingsMenu {
         id: settingsMenu
 
diff --git a/src/mainview/components/AccountComboBox.qml b/src/mainview/components/AccountComboBox.qml
index cbcd7f1df..7d5873820 100644
--- a/src/mainview/components/AccountComboBox.qml
+++ b/src/mainview/components/AccountComboBox.qml
@@ -49,8 +49,8 @@ Label {
     }
 
     function resetAccountListModel(accountId) {
-        accountListModel.updateAvatarUid(accountId)
-        accountListModel.reset()
+        AccountListModel.updateAvatarUid(accountId)
+        AccountListModel.reset()
     }
 
     function togglePopup() {
@@ -105,18 +105,18 @@ Label {
     }
 
     Connections {
-        target: accountListModel
+        target: AccountListModel
 
         function onModelReset() {
             avatar.updateImage(AccountAdapter.currentAccountId,
-                               accountListModel.data(accountListModel.index(0, 0),
-                                                     AccountListModel.PictureUid))
-            avatar.presenceStatus = accountListModel.data(accountListModel.index(0, 0),
-                                                          AccountListModel.Status)
-            userAliasText.text = accountListModel.data(accountListModel.index(0,0),
-                                                       AccountListModel.Alias)
-            usernameText.text = accountListModel.data(accountListModel.index(0,0),
-                                                      AccountListModel.Username)
+                               AccountListModel.data(AccountListModel.index(0, 0),
+                                                     AccountList.PictureUid))
+            avatar.presenceStatus = AccountListModel.data(AccountListModel.index(0, 0),
+                                                          AccountList.Status)
+            userAliasText.text = AccountListModel.data(AccountListModel.index(0,0),
+                                                       AccountList.Alias)
+            usernameText.text = AccountListModel.data(AccountListModel.index(0,0),
+                                                      AccountList.Username)
         }
     }
 
@@ -135,8 +135,8 @@ Label {
 
             imageId: AccountAdapter.currentAccountId
 
-            presenceStatus: accountListModel.data(accountListModel.index(0, 0),
-                                                  AccountListModel.Status)
+            presenceStatus: AccountListModel.data(AccountListModel.index(0, 0),
+                                                  AccountList.Status)
         }
 
         ColumnLayout {
@@ -150,8 +150,8 @@ Label {
                 Layout.fillWidth: true
                 Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
 
-                text: accountListModel.data(accountListModel.index(0,0),
-                                            AccountListModel.Alias)
+                text: AccountListModel.data(AccountListModel.index(0,0),
+                                            AccountList.Alias)
                 font.pointSize: JamiTheme.textFontSize
                 color: JamiTheme.textColor
                 elide: Text.ElideRight
@@ -165,8 +165,8 @@ Label {
 
                 visible: text.length
 
-                text:  accountListModel.data(accountListModel.index(0,0),
-                                             AccountListModel.Username)
+                text:  AccountListModel.data(AccountListModel.index(0,0),
+                                             AccountList.Username)
                 font.pointSize: JamiTheme.textFontSize
                 color: JamiTheme.faddedLastInteractionFontColor
                 elide: Text.ElideRight
diff --git a/src/mainview/components/AccountComboBoxPopup.qml b/src/mainview/components/AccountComboBoxPopup.qml
index 3b0128d44..8a9cf094b 100644
--- a/src/mainview/components/AccountComboBoxPopup.qml
+++ b/src/mainview/components/AccountComboBoxPopup.qml
@@ -34,9 +34,10 @@ Popup {
     implicitWidth: parent.width
     // limit the number of accounts shown at once
     implicitHeight: {
-        return Math.min(JamiTheme.accountListItemHeight *
-                        Math.min(5, accountListModel.rowCount() + 1),
-                        mainViewSidePanelRect.height)
+        return visible ? Math.min(
+                             JamiTheme.accountListItemHeight * Math.min(
+                                 5, CurrentAccountFilterModel.rowCount() + 1),
+                             mainViewSidePanelRect.height) : 0
     }
     padding: 0
     modal: true
@@ -54,15 +55,15 @@ Popup {
 
             clip: true
 
-            // TODO: this should use proxy model or custom filter out the
-            // current account
-            model: accountListModel
+            model: CurrentAccountFilterModel
             delegate: AccountItemDelegate {
                 height: JamiTheme.accountListItemHeight
                 width: root.width
                 onClicked: {
                     root.close()
-                    AccountAdapter.changeAccount(index)
+                    var sourceRow = CurrentAccountFilterModel.mapToSource(
+                                CurrentAccountFilterModel.index(index, 0)).row
+                    AccountAdapter.changeAccount(sourceRow)
                 }
             }
 
diff --git a/src/mainview/components/AccountItemDelegate.qml b/src/mainview/components/AccountItemDelegate.qml
index fc4a21237..13dcca576 100644
--- a/src/mainview/components/AccountItemDelegate.qml
+++ b/src/mainview/components/AccountItemDelegate.qml
@@ -56,14 +56,9 @@ ItemDelegate {
 
             presenceStatus: Status
 
-            Component.onCompleted: {
-                return updateImage(
-                            accountListModel.data(
-                                accountListModel.index(index, 0), AccountListModel.ID),
-                            accountListModel.data(
-                                accountListModel.index(index, 0), AccountListModel.PictureUid))
-            }
+            Component.onCompleted: updateImage(ID, PictureUid)
         }
+
         ColumnLayout {
             Layout.fillWidth: true
             Layout.fillHeight: true
diff --git a/src/qmlregister.cpp b/src/qmlregister.cpp
index 862ae16ba..d8e8e0f2c 100644
--- a/src/qmlregister.cpp
+++ b/src/qmlregister.cpp
@@ -93,7 +93,6 @@ registerTypes()
     QML_REGISTERNAMESPACE(NS_ENUMS, dummy::staticMetaObject, "");
 
     // QAbstractListModels
-    QML_REGISTERTYPE(NS_MODELS, AccountListModel);
     QML_REGISTERTYPE(NS_MODELS, DeviceItemListModel);
     QML_REGISTERTYPE(NS_MODELS, BannedListModel);
     QML_REGISTERTYPE(NS_MODELS, ModeratorListModel);
@@ -108,6 +107,7 @@ registerTypes()
     QML_REGISTERTYPE(NS_MODELS, SmartListModel);
 
     // Roles & type enums for models
+    QML_REGISTERNAMESPACE(NS_MODELS, AccountList::staticMetaObject, "AccountList");
     QML_REGISTERNAMESPACE(NS_MODELS, ConversationList::staticMetaObject, "ConversationList");
     QML_REGISTERNAMESPACE(NS_MODELS, ContactList::staticMetaObject, "ContactList");
 
-- 
GitLab