diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml
index 9377704a68a0e82ba6f8e26d262c290ae327c102..69e2e18b34c7337f3333e20ac55e0b4548fb6ff6 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 389729380d7bf6b3d3d21d455c7786219bde20d5..88301cb45cded7f30bde578760711d967d07acfc 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 975d725f162c62ba6b244b13332fc582ceeb45fc..13d69e04f1996a4d892b3dda7dc0f8781274123f 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 534d6881c17afedf792762bb762fd592abf11672..5de28c6bbb672bd51c1e93a91ffa697d90578a39 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 58e393c8065180566bdf9b541eaed00e73bcfa1c..6aa85f3d44d2c43a89c6a7ef090a4250f3ef8861 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 e3777b146fb0682982f892555ae97d4da853ce75..0dfc8f6d806024feeb9708bcad08c733f31df954 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 3ad3baacab595e37c20902f7694a439996bea285..8f80044d47f1dd860b00049b43c2d16785112496 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 e1f523ab9834464eeb8fbad4105cff59ff703908..931205b27c838f63e5dd66125ca1a52837899886 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 042871dff756571386bfcfc7342c4deabb3b0da0..e92bd4c6ce17d304cdca20c5a36a97abe4cbebeb 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 580f15c222b0dfd804d057c529fbcb85d78e3365..c5f11d6121bf5e2967a5d47cf42aeb31be3e0a1f 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 ddea15a6283345719a744349908197d0a3a1a8d6..3ba15c1c7e1f4d90c45850008ccaa18837ad33f4 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 d51ecacf2f4b9c7c0633dc1cf7381c30c8f06e68..3d28af31628d34c21e4cae28369170df3b44e9ff 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 63e4825db36d951177f5fd57f3e05f7cd4df4385..425b97057e8a6f5e7cab1c882f53424c7ad7e302 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 6cf8654646e00247f0947a096aeb7f4fc2f64d05..6f0029066570fd475bae62f200ba5aa28ac64440 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();
         }