From 300ac17db3372cd7e9c52cc77eafca0c63bcbbe2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Blin?=
 <sebastien.blin@savoirfairelinux.com>
Date: Mon, 4 Jul 2022 11:52:55 -0400
Subject: [PATCH] contactmodel: rework ban status

And do not ban contact when removing conversation.

Change-Id: Ia76c76b265dd2a9d7d3cc8e2f869bf74fd3cbe63
---
 src/app/constant/JamiStrings.qml              |  1 +
 src/app/conversationlistmodel.cpp             |  2 +-
 src/app/conversationlistmodelbase.cpp         |  5 +++
 src/app/currentconversation.cpp               |  1 +
 src/app/currentconversation.h                 |  1 +
 src/app/mainview/components/ChatView.qml      |  4 +-
 .../mainview/components/ChatViewHeader.qml    |  6 ++-
 .../components/ConversationListView.qml       |  2 +
 .../ConversationSmartListContextMenu.qml      | 16 ++++++--
 .../components/SmartListItemDelegate.qml      | 11 ++++++
 src/app/messagesadapter.cpp                   | 13 +++++++
 src/app/messagesadapter.h                     |  1 +
 src/libclient/contactmodel.cpp                | 15 +++++++-
 src/libclient/conversationmodel.cpp           | 38 +++++++++++--------
 14 files changed, 91 insertions(+), 25 deletions(-)

diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml
index 9377704a6..69e2e18b3 100644
--- a/src/app/constant/JamiStrings.qml
+++ b/src/app/constant/JamiStrings.qml
@@ -355,6 +355,7 @@ Item {
 
     // BannedContacts
     property string tipBannedContactsDisplay: qsTr("Display banned contacts")
+    property string banned: qsTr("Banned")
     property string tipBannedContactsHide: qsTr("Hide banned contacts")
 
     // DeleteAccountDialog
diff --git a/src/app/conversationlistmodel.cpp b/src/app/conversationlistmodel.cpp
index 389729380..88301cb45 100644
--- a/src/app/conversationlistmodel.cpp
+++ b/src/app/conversationlistmodel.cpp
@@ -120,7 +120,7 @@ ConversationListProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& s
     if (ignored_.contains(index.data(Role::UID).toString())) {
         match = true;
     } else if (index.data(Role::IsBanned).toBool()) {
-        if (!rx.isValid()) {
+        if (!rx.pattern().isEmpty() && rx.isValid()) {
             Q_FOREACH (const auto& filter, toFilter) {
                 auto matchResult = rx.match(filter);
                 if (matchResult.hasMatch() && matchResult.captured(0) == filter) {
diff --git a/src/app/conversationlistmodelbase.cpp b/src/app/conversationlistmodelbase.cpp
index 975d725f1..13d69e04f 100644
--- a/src/app/conversationlistmodelbase.cpp
+++ b/src/app/conversationlistmodelbase.cpp
@@ -128,6 +128,11 @@ ConversationListModelBase::dataForItem(item_t item, int role) const
         return QVariant(static_cast<int>(item.mode));
     case Role::UID:
         return QVariant(item.uid);
+    case Role::IsBanned:
+        if (!item.isCoreDialog()) {
+            return false;
+        }
+        break;
     case Role::Uris:
         return QVariant(model_->peersForConversation(item.uid).toList());
     case Role::Monikers: {
diff --git a/src/app/currentconversation.cpp b/src/app/currentconversation.cpp
index 534d6881c..5de28c6bb 100644
--- a/src/app/currentconversation.cpp
+++ b/src/app/currentconversation.cpp
@@ -84,6 +84,7 @@ CurrentConversation::updateData()
             if (isCoreDialog_)
                 try {
                     auto& contact = accInfo.contactModel->getContact(members.at(0));
+                    set_isBanned(contact.isBanned);
                     isContact = contact.profileInfo.type != profile::Type::TEMPORARY;
                 } catch (const std::exception& e) {
                     qInfo() << "Contact not found: " << e.what();
diff --git a/src/app/currentconversation.h b/src/app/currentconversation.h
index 58e393c80..6aa85f3d4 100644
--- a/src/app/currentconversation.h
+++ b/src/app/currentconversation.h
@@ -40,6 +40,7 @@ class CurrentConversation final : public QObject
     QML_PROPERTY(bool, readOnly)
     QML_PROPERTY(bool, needsSyncing)
     QML_PROPERTY(bool, isSip)
+    QML_PROPERTY(bool, isBanned)
     QML_PROPERTY(QString, callId)
     QML_PROPERTY(QString, color)
     QML_PROPERTY(call::Status, callState)
diff --git a/src/app/mainview/components/ChatView.qml b/src/app/mainview/components/ChatView.qml
index e3777b146..0dfc8f6d8 100644
--- a/src/app/mainview/components/ChatView.qml
+++ b/src/app/mainview/components/ChatView.qml
@@ -171,7 +171,9 @@ Rectangle {
                     id: chatViewFooter
 
                     visible: {
-                        if (CurrentConversation.needsSyncing || CurrentConversation.readOnly)
+                        if (CurrentConversation.isBlocked)
+                            return false
+                        else if (CurrentConversation.needsSyncing || CurrentConversation.readOnly)
                             return false
                         else if (CurrentConversation.isSwarm && CurrentConversation.isRequest)
                             return false
diff --git a/src/app/mainview/components/ChatViewHeader.qml b/src/app/mainview/components/ChatViewHeader.qml
index 3ad3baaca..8f80044d4 100644
--- a/src/app/mainview/components/ChatViewHeader.qml
+++ b/src/app/mainview/components/ChatViewHeader.qml
@@ -209,7 +209,7 @@ Rectangle {
             PushButton {
                 id: sendContactRequestButton
 
-                visible: CurrentConversation.isTemporary
+                visible: CurrentConversation.isTemporary || CurrentConversation.isBanned
 
                 source: JamiResources.add_people_24dp_svg
                 toolTipText: JamiStrings.addToConversations
@@ -217,7 +217,9 @@ Rectangle {
                 normalColor: JamiTheme.chatviewBgColor
                 imageColor: JamiTheme.chatviewButtonColor
 
-                onClicked: MessagesAdapter.sendConversationRequest()
+                onClicked: CurrentConversation.isBanned ?
+                                MessagesAdapter.unbanConversation(CurrentConversation.id)
+                                : MessagesAdapter.sendConversationRequest()
             }
 
             PushButton {
diff --git a/src/app/mainview/components/ConversationListView.qml b/src/app/mainview/components/ConversationListView.qml
index e1f523ab9..931205b27 100644
--- a/src/app/mainview/components/ConversationListView.qml
+++ b/src/app/mainview/components/ConversationListView.qml
@@ -128,6 +128,7 @@ JamiListView {
                 "title": model.dataForRow(row, ConversationList.Title),
                 "uri": model.dataForRow(row, ConversationList.URI),
                 "isSwarm": model.dataForRow(row, ConversationList.IsSwarm),
+                "isBanned": model.dataForRow(row, ConversationList.IsBanned),
                 "mode": model.dataForRow(row, ConversationList.Mode),
                 "readOnly": model.dataForRow(row, ConversationList.ReadOnly)
             }
@@ -135,6 +136,7 @@ JamiListView {
             responsibleAccountId = LRCInstance.currentAccountId
             responsibleConvUid = item.convId
             isSwarm = item.isSwarm
+            isBanned = item.isBanned
             mode = item.mode
             contactType = LRCInstance.currentAccountType
             readOnly = item.readOnly
diff --git a/src/app/mainview/components/ConversationSmartListContextMenu.qml b/src/app/mainview/components/ConversationSmartListContextMenu.qml
index 042871dff..e92bd4c6c 100644
--- a/src/app/mainview/components/ConversationSmartListContextMenu.qml
+++ b/src/app/mainview/components/ConversationSmartListContextMenu.qml
@@ -30,6 +30,7 @@ ContextMenuAutoLoader {
 
     property string responsibleAccountId: ""
     property string responsibleConvUid: ""
+    property bool isBanned: false
     property bool isSwarm: false
     property var mode: undefined
     property int contactType: Profile.Type.INVALID
@@ -70,7 +71,7 @@ ContextMenuAutoLoader {
         GeneralMenuItem {
             id: clearConversation
 
-            canTrigger: !isSwarm && !hasCall
+            canTrigger: !isSwarm && !hasCall && !root.isBanned
             itemName: JamiStrings.clearConversation
             iconSource: JamiResources.ic_clear_24dp_svg
             onClicked: MessagesAdapter.clearConversationHistory(
@@ -80,7 +81,7 @@ ContextMenuAutoLoader {
         GeneralMenuItem {
             id: removeContact
 
-            canTrigger: !hasCall
+            canTrigger: !hasCall && !root.isBanned
             itemName: {
                 if (mode !== Conversation.Mode.ONE_TO_ONE && mode !== Conversation.Mode.NON_SWARM)
                     return JamiStrings.removeConversation
@@ -126,12 +127,21 @@ ContextMenuAutoLoader {
         GeneralMenuItem {
             id: blockContact
 
-            canTrigger: !hasCall && contactType !== Profile.Type.SIP
+            canTrigger: !hasCall && contactType !== Profile.Type.SIP && !root.isBanned
             itemName: !(mode && mode !== Conversation.Mode.ONE_TO_ONE && mode !== Conversation.Mode.NON_SWARM) ? JamiStrings.blockContact : JamiStrings.blockSwarm
             iconSource: JamiResources.block_black_24dp_svg
             addMenuSeparatorAfter: contactType !== Profile.Type.SIP
             onClicked: MessagesAdapter.blockConversation(responsibleConvUid)
         },
+        GeneralMenuItem {
+            id: unblockContact
+
+            canTrigger: root.isBanned
+            itemName: JamiStrings.reinstateContact
+            iconSource: JamiResources.round_remove_circle_24dp_svg
+            addMenuSeparatorAfter: contactType !== Profile.Type.SIP
+            onClicked: MessagesAdapter.unbanConversation(responsibleConvUid)
+        },
         GeneralMenuItem {
             id: contactDetails
 
diff --git a/src/app/mainview/components/SmartListItemDelegate.qml b/src/app/mainview/components/SmartListItemDelegate.qml
index 580f15c22..c5f11d612 100644
--- a/src/app/mainview/components/SmartListItemDelegate.qml
+++ b/src/app/mainview/components/SmartListItemDelegate.qml
@@ -116,6 +116,7 @@ ItemDelegate {
             }
             RowLayout {
                 visible: ContactType !== Profile.Type.TEMPORARY
+                         && !IsBanned
                          && LastInteractionDate !== undefined
                          && interactive
                 Layout.fillWidth: true
@@ -151,6 +152,16 @@ ItemDelegate {
                     lineHeight: font.family === "Segoe UI Emoji" ? 1.25 : 1
                 }
             }
+            Text {
+                Layout.fillWidth: true
+                Layout.preferredHeight: 20
+                Layout.alignment: Qt.AlignVCenter
+                text: JamiStrings.banned
+                visible: IsBanned
+                font.pointSize: JamiTheme.smartlistItemFontSize
+                font.weight: Font.Bold
+                color: JamiTheme.textColor
+            }
         }
 
         // read-only conversation indicator
diff --git a/src/app/messagesadapter.cpp b/src/app/messagesadapter.cpp
index ddea15a62..3ba15c1c7 100644
--- a/src/app/messagesadapter.cpp
+++ b/src/app/messagesadapter.cpp
@@ -368,6 +368,19 @@ MessagesAdapter::unbanContact(int index)
     }
 }
 
+void
+MessagesAdapter::unbanConversation(const QString& convUid)
+{
+    auto& accInfo = lrcInstance_->getCurrentAccountInfo();
+    try {
+        const auto contactUri = accInfo.conversationModel->peersForConversation(convUid).at(0);
+        auto contactInfo = accInfo.contactModel->getContact(contactUri);
+        accInfo.contactModel->addContact(contactInfo);
+    } catch (const std::out_of_range& e) {
+        qDebug() << e.what();
+    }
+}
+
 void
 MessagesAdapter::clearConversationHistory(const QString& accountId, const QString& convUid)
 {
diff --git a/src/app/messagesadapter.h b/src/app/messagesadapter.h
index d51ecacf2..3d28af316 100644
--- a/src/app/messagesadapter.h
+++ b/src/app/messagesadapter.h
@@ -90,6 +90,7 @@ protected:
     Q_INVOKABLE void refuseInvitation(const QString& convUid = "");
     Q_INVOKABLE void blockConversation(const QString& convUid = "");
     Q_INVOKABLE void unbanContact(int index);
+    Q_INVOKABLE void unbanConversation(const QString& convUid);
     Q_INVOKABLE void sendMessage(const QString& message);
     Q_INVOKABLE void sendFile(const QString& message);
     Q_INVOKABLE void acceptFile(const QString& arg);
diff --git a/src/libclient/contactmodel.cpp b/src/libclient/contactmodel.cpp
index 63e4825db..425b97057 100644
--- a/src/libclient/contactmodel.cpp
+++ b/src/libclient/contactmodel.cpp
@@ -344,6 +344,15 @@ ContactModel::addToContacts(const QString& contactUri)
 void
 ContactModel::removeContact(const QString& contactUri, bool banned)
 {
+    try {
+        const auto& contact = getContact(contactUri);
+        if (contact.isBanned) {
+            qWarning() << "Contact already banned";
+            return;
+        }
+    } catch (...) {
+    }
+
     bool emitContactRemoved = false;
     {
         std::lock_guard<std::mutex> lk(pimpl_->contactsMtx_);
@@ -872,8 +881,9 @@ ContactModelPimpl::slotContactRemoved(const QString& accountId,
         std::lock_guard<std::mutex> lk(contactsMtx_);
 
         auto contact = contacts.find(contactUri);
-        if (contact == contacts.end())
+        if (contact == contacts.end()) {
             return;
+        }
 
         if (contact->profileInfo.type == profile::Type::PENDING) {
             Q_EMIT behaviorController.trustRequestTreated(linked.owner.id, contactUri);
@@ -911,8 +921,9 @@ ContactModelPimpl::slotContactRemoved(const QString& accountId,
     linked.owner.conversationModel->refreshFilter();
     if (banned) {
         Q_EMIT linked.bannedStatusChanged(contactUri, true);
+    } else {
+        Q_EMIT linked.contactRemoved(contactUri);
     }
-    Q_EMIT linked.contactRemoved(contactUri);
 }
 
 void
diff --git a/src/libclient/conversationmodel.cpp b/src/libclient/conversationmodel.cpp
index 6cf865464..6f0029066 100644
--- a/src/libclient/conversationmodel.cpp
+++ b/src/libclient/conversationmodel.cpp
@@ -814,20 +814,14 @@ ConversationModel::removeConversation(const QString& uid, bool banned)
                     "participant";
         return;
     }
-    if (conversation.isSwarm()) {
+    if (!conversation.isCoreDialog()) {
         if (conversation.isRequest)
             ConfigurationManager::instance().declineConversationRequest(owner.id, uid);
         else
             ConfigurationManager::instance().removeConversation(owner.id, uid);
-
-        // Still some other conversation, do nothing else
-        if (!banned)
-            return;
+    } else {
+        owner.contactModel->removeContact(peers.front(), banned);
     }
-
-    if (!conversation.isCoreDialog())
-        return;
-    owner.contactModel->removeContact(peers.front(), banned);
 }
 
 void
@@ -2575,21 +2569,33 @@ ConversationModelPimpl::slotConversationRemoved(const QString& accountId,
 
         auto& conversation = getConversationForUid(conversationId).get();
         auto& peers = peersForConversation(conversation);
+        if (peers.isEmpty()) {
+            removeConversation();
+            return;
+        }
+        auto contactUri = peers.first();
+        contact::Info contact;
+        try {
+            contact = linked.owner.contactModel->getContact(contactUri);
+        } catch (...) {
+        }
 
         if (conversation.mode == conversation::Mode::ONE_TO_ONE) {
-            if (peers.isEmpty()) {
-                removeConversation();
-                return;
-            }
-
-            auto contactUri = peers.first();
             removeConversation();
 
             // If it's a 1:1 conversation and we don't have any more conversation
             // we can remove the contact
             auto conv = storage::getConversationsWithPeer(db, contactUri);
             if (conv.empty())
-                linked.owner.contactModel->removeContact(contactUri, true);
+                linked.owner.contactModel->removeContact(contactUri, false);
+
+            if (contact.isBanned && conv.empty()) {
+                // Add 1:1 conv for banned
+                auto c = storage::beginConversationWithPeer(db, contactUri);
+                addConversationWith(c, contactUri, false);
+                Q_EMIT linked.conversationReady(c, contactUri);
+                Q_EMIT linked.newConversation(c);
+            }
         } else {
             removeConversation();
         }
-- 
GitLab