diff --git a/src/api/interaction.h b/src/api/interaction.h
index 8156db714c2bccd67b9057a2f7dedbf7232b91d0..3b32df0174ae107f5eef16f47e1626466f1940a7 100644
--- a/src/api/interaction.h
+++ b/src/api/interaction.h
@@ -176,7 +176,7 @@ to_status(const QString& status)
         return Status::INVALID;
 }
 
-enum class ContactAction { ADD, JOIN, LEAVE, BANNED, INVALID };
+enum class ContactAction { ADD, JOIN, LEAVE, BANNED, UNBANNED, INVALID };
 Q_ENUM_NS(ContactAction)
 
 static inline const QString
@@ -191,6 +191,8 @@ to_string(const ContactAction& action)
         return "LEAVE";
     case ContactAction::BANNED:
         return "BANNED";
+    case ContactAction::UNBANNED:
+        return "UNBANNED";
     case ContactAction::INVALID:
         return {};
     }
@@ -208,6 +210,8 @@ to_action(const QString& action)
         return ContactAction::LEAVE;
     else if (action == "ban")
         return ContactAction::BANNED;
+    else if (action == "unban")
+        return ContactAction::UNBANNED;
     return ContactAction::INVALID;
 }
 
@@ -226,6 +230,8 @@ getContactInteractionString(const QString& authorUri, const ContactAction& actio
         return QObject::tr("%1 left").arg(authorUri);
     case ContactAction::BANNED:
         return QObject::tr("%1 was kicked").arg(authorUri);
+    case ContactAction::UNBANNED:
+        return QObject::tr("%1 was re-added").arg(authorUri);
     case ContactAction::INVALID:
         return {};
     }
diff --git a/src/conversationmodel.cpp b/src/conversationmodel.cpp
index c191ce59de5957de388e78418bbfc5e997f46096..e73d589d1d9d1a683e17621361444b8baeb0e938 100644
--- a/src/conversationmodel.cpp
+++ b/src/conversationmodel.cpp
@@ -2374,6 +2374,13 @@ ConversationModelPimpl::slotMessageReceived(const QString& accountId,
             linked.owner.dataTransferModel->registerTransferId(fileId, msgId);
         } else if (msg.type == interaction::Type::CALL) {
             msg.body = storage::getCallInteractionString(msg.authorUri, msg.duration);
+        } else if (msg.type == interaction::Type::CONTACT) {
+            auto bestName = msg.authorUri == linked.owner.profileInfo.uri
+                                ? linked.owner.accountModel->bestNameForAccount(linked.owner.id)
+                                : linked.owner.contactModel->bestNameForContact(msg.authorUri);
+            msg.body = interaction::getContactInteractionString(bestName,
+                                                                interaction::to_action(
+                                                                    message["action"]));
         } else if (msg.type == interaction::Type::TEXT
                    && msg.authorUri != linked.owner.profileInfo.uri) {
             conversation.unreadMessages++;
@@ -2584,20 +2591,12 @@ ConversationModelPimpl::slotConversationMemberEvent(const QString& accountId,
     if (accountId != linked.owner.id || indexOf(conversationId) < 0) {
         return;
     }
-    switch (event) {
-    case 0: // add
+    if (event == 0 /* add */) {
         // clear search result
         for (unsigned int i = 0; i < searchResults.size(); ++i) {
             if (searchResults.at(i).uid == memberUri)
                 searchResults.erase(searchResults.begin() + i);
         }
-        break;
-    case 1: // joins
-        break;
-    case 2: // leave
-        break;
-    case 3: // banned
-        break;
     }
     // update participants
     auto& conversation = getConversationForUid(conversationId).get();