diff --git a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt
index b99c731bf2656a7e0bd3ac7d15f640b92a06ed35..edf4a93aff3a7640a7d324bbd11ff953f208a287 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/contact/ContactDropDown.kt
@@ -22,25 +22,26 @@ import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 @Composable
 fun ContactDropDown(
     expanded: Boolean,
-    isExpanded: (Boolean) -> Unit,
+    close: () -> Unit,
     onMakeIntroduction: () -> Unit,
+    onDeleteAllMessages: () -> Unit,
 ) {
     var connectionMode by remember { mutableStateOf(false) }
     var contactMode by remember { mutableStateOf(false) }
     DropdownMenu(
         expanded = expanded,
-        onDismissRequest = { isExpanded(false) },
+        onDismissRequest = close,
     ) {
-        DropdownMenuItem(onClick = { isExpanded(false); onMakeIntroduction() }) {
+        DropdownMenuItem(onClick = { close(); onMakeIntroduction() }) {
             Text(i18n("contacts.dropdown.introduction"), fontSize = 14.sp)
         }
         DropdownMenuItem(onClick = {}) {
             Text(i18n("contacts.dropdown.disappearing"), fontSize = 14.sp)
         }
-        DropdownMenuItem(onClick = {}) {
+        DropdownMenuItem(onClick = { close(); onDeleteAllMessages() }) {
             Text(i18n("contacts.dropdown.delete.all"), fontSize = 14.sp)
         }
-        DropdownMenuItem(onClick = { connectionMode = true; isExpanded(false) }) {
+        DropdownMenuItem(onClick = { connectionMode = true; close() }) {
             Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
                 Text(
                     i18n("contacts.dropdown.connections"),
@@ -54,7 +55,7 @@ fun ContactDropDown(
                 )
             }
         }
-        DropdownMenuItem(onClick = { contactMode = true; isExpanded(false) }) {
+        DropdownMenuItem(onClick = { contactMode = true; close() }) {
             Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
                 Text(
                     i18n("contacts.dropdown.contact"),
@@ -69,36 +70,32 @@ fun ContactDropDown(
             }
         }
     }
-    if (connectionMode) {
-        DropdownMenu(
-            expanded = connectionMode,
-            onDismissRequest = { connectionMode = false },
-        ) {
-            DropdownMenuItem(onClick = { false }) {
-                Text(i18n("contacts.dropdown.connections.title"), fontSize = 12.sp)
-            }
-            DropdownMenuItem(onClick = { false }) {
-                Text(i18n("contacts.dropdown.connections.bluetooth"), fontSize = 14.sp)
-            }
-            DropdownMenuItem(onClick = { false }) {
-                Text(i18n("contacts.dropdown.connections.removable"), fontSize = 14.sp)
-            }
+    DropdownMenu(
+        expanded = connectionMode,
+        onDismissRequest = { connectionMode = false },
+    ) {
+        DropdownMenuItem(onClick = { false }) {
+            Text(i18n("contacts.dropdown.connections.title"), fontSize = 12.sp)
+        }
+        DropdownMenuItem(onClick = { false }) {
+            Text(i18n("contacts.dropdown.connections.bluetooth"), fontSize = 14.sp)
+        }
+        DropdownMenuItem(onClick = { false }) {
+            Text(i18n("contacts.dropdown.connections.removable"), fontSize = 14.sp)
         }
     }
-    if (contactMode) {
-        DropdownMenu(
-            expanded = contactMode,
-            onDismissRequest = { contactMode = false },
-        ) {
-            DropdownMenuItem(onClick = { false }) {
-                Text(i18n("contacts.dropdown.contact.title"), fontSize = 12.sp)
-            }
-            DropdownMenuItem(onClick = { false }) {
-                Text(i18n("contacts.dropdown.contact.change"), fontSize = 14.sp)
-            }
-            DropdownMenuItem(onClick = { false }) {
-                Text(i18n("contacts.dropdown.contact.delete"), fontSize = 14.sp)
-            }
+    DropdownMenu(
+        expanded = contactMode,
+        onDismissRequest = { contactMode = false },
+    ) {
+        DropdownMenuItem(onClick = { false }) {
+            Text(i18n("contacts.dropdown.contact.title"), fontSize = 12.sp)
+        }
+        DropdownMenuItem(onClick = { false }) {
+            Text(i18n("contacts.dropdown.contact.change"), fontSize = 14.sp)
+        }
+        DropdownMenuItem(onClick = { false }) {
+            Text(i18n("contacts.dropdown.contact.delete"), fontSize = 14.sp)
         }
     }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationDialogs.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationDialogs.kt
new file mode 100644
index 0000000000000000000000000000000000000000..136f61939ca2bb73e91abdd7dfba5b81ba9a7d34
--- /dev/null
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationDialogs.kt
@@ -0,0 +1,140 @@
+package org.briarproject.briar.desktop.conversation
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.AlertDialog
+import androidx.compose.material.Button
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.Text
+import androidx.compose.material.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import org.briarproject.briar.api.conversation.DeletionResult
+import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
+import org.briarproject.briar.desktop.utils.PreviewUtils.preview
+
+fun main() = preview(
+    "introduction_pending" to false,
+    "invitation_pending" to false,
+    "introduction_not_all" to false,
+    "invitation_not_all" to false,
+) {
+    var confirmationDialog by remember { mutableStateOf(false) }
+    var failedDialog by remember { mutableStateOf(false) }
+    val deletionResult by derivedStateOf {
+        if (!failedDialog) null else
+            DeletionResult().apply {
+                if (getBooleanParameter("introduction_pending")) addIntroductionSessionInProgress()
+                if (getBooleanParameter("invitation_pending")) addInvitationSessionInProgress()
+                if (getBooleanParameter("introduction_not_all")) addIntroductionNotAllSelected()
+                if (getBooleanParameter("invitation_not_all")) addInvitationNotAllSelected()
+            }
+    }
+
+    Column {
+        Button(onClick = { confirmationDialog = true }) {
+            Text("Show confirmation dialog")
+        }
+
+        Button(onClick = { failedDialog = true }) {
+            Text("Show deletion failed dialog")
+        }
+    }
+
+    DeleteAllMessagesConfirmationDialog(
+        isVisible = confirmationDialog,
+        close = { confirmationDialog = false },
+    )
+
+    DeleteAllMessagesFailedDialog(deletionResult) { failedDialog = false }
+}
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+fun DeleteAllMessagesConfirmationDialog(
+    isVisible: Boolean,
+    close: () -> Unit,
+    onDelete: () -> Unit = {},
+    onCancel: () -> Unit = {},
+) {
+    if (!isVisible) return
+
+    AlertDialog(
+        onDismissRequest = close,
+        title = {
+            Text(
+                text = i18n("conversation.delete.all.dialog.title"),
+                modifier = Modifier.width(IntrinsicSize.Max)
+            )
+        },
+        text = {
+            Text(i18n("conversation.delete.all.dialog.message"))
+        },
+        dismissButton = {
+            TextButton(onClick = { close(); onDelete() }) {
+                Text(i18n("delete"))
+            }
+        },
+        confirmButton = {
+            TextButton(onClick = { close(); onCancel() }) {
+                Text(i18n("cancel"))
+            }
+        }
+    )
+}
+
+@OptIn(ExperimentalMaterialApi::class)
+@Composable
+fun DeleteAllMessagesFailedDialog(
+    deletionResult: DeletionResult?,
+    close: () -> Unit,
+) {
+    if (deletionResult == null) return
+
+    val message = buildList {
+        when {
+            // get failures the user cannot immediately resolve
+            deletionResult.hasIntroductionSessionInProgress() &&
+                deletionResult.hasInvitationSessionInProgress() ->
+                add(i18n("conversation.delete.failed.dialog.message.ongoing_both"))
+            deletionResult.hasIntroductionSessionInProgress() ->
+                add(i18n("conversation.delete.failed.dialog.message.ongoing_introductions"))
+            deletionResult.hasInvitationSessionInProgress() ->
+                add(i18n("conversation.delete.failed.dialog.message.ongoing_invitations"))
+        }
+        when {
+            // add problems the user can resolve
+            deletionResult.hasNotAllIntroductionSelected() &&
+                deletionResult.hasNotAllInvitationSelected() ->
+                add(i18n("conversation.delete.failed.dialog.message.not_all_selected_both"))
+            deletionResult.hasNotAllIntroductionSelected() ->
+                add(i18n("conversation.delete.failed.dialog.message.not_all_selected_introductions"))
+            deletionResult.hasNotAllInvitationSelected() ->
+                add(i18n("conversation.delete.failed.dialog.message.not_all_selected_invitations"))
+        }
+    }.joinToString("\n\n")
+
+    AlertDialog(
+        onDismissRequest = close,
+        title = {
+            Text(
+                text = i18n("conversation.delete.failed.dialog.title"),
+                modifier = Modifier.width(IntrinsicSize.Max)
+            )
+        },
+        text = {
+            Text(message)
+        },
+        confirmButton = {
+            TextButton(onClick = close) {
+                Text(i18n("ok"))
+            }
+        }
+    )
+}
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt
index a6566260d67adf60248d6230b267d161bcf486fc..87edcda219c24910d3b5d41402fe864437640d84 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationHeader.kt
@@ -34,6 +34,7 @@ import org.briarproject.briar.desktop.utils.InternationalizationUtils.i18n
 fun ConversationHeader(
     contactItem: ContactItem,
     onMakeIntroduction: () -> Unit,
+    onDeleteAllMessages: () -> Unit,
 ) {
     val (isExpanded, setExpanded) = remember { mutableStateOf(false) }
     val onlineColor =
@@ -64,7 +65,7 @@ fun ConversationHeader(
             modifier = Modifier.align(Alignment.CenterEnd).padding(end = 16.dp)
         ) {
             Icon(Icons.Filled.MoreVert, i18n("access.contact.menu"), modifier = Modifier.size(24.dp))
-            ContactDropDown(isExpanded, setExpanded, onMakeIntroduction)
+            ContactDropDown(isExpanded, { setExpanded(false) }, onMakeIntroduction, onDeleteAllMessages)
         }
         HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
     }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt
index e10fe586a45effdf5250ccfbc14b2ffd4ea8c16c..29618666a31b89e010c632849e1332d90c9c8fa3 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationScreen.kt
@@ -18,6 +18,7 @@ import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Scaffold
 import androidx.compose.runtime.Composable
@@ -37,6 +38,7 @@ import org.briarproject.briar.desktop.ui.Constants.CONTACTLIST_WIDTH
 import org.briarproject.briar.desktop.ui.Loader
 import org.briarproject.briar.desktop.viewmodel.viewModel
 
+@OptIn(ExperimentalMaterialApi::class)
 @Composable
 fun ConversationScreen(
     contactId: ContactId,
@@ -55,6 +57,7 @@ fun ConversationScreen(
 
     val (infoDrawer, setInfoDrawer) = remember { mutableStateOf(false) }
     val (contactDrawerState, setDrawerState) = remember { mutableStateOf(ContactInfoDrawerState.MakeIntro) }
+    val (deleteAllMessagesDialogVisible, setDeleteAllMessagesDialog) = remember { mutableStateOf(false) }
     val scrollState = rememberLazyListState()
 
     BoxWithConstraints(Modifier.fillMaxSize()) {
@@ -65,6 +68,9 @@ fun ConversationScreen(
                     contactItem,
                     onMakeIntroduction = {
                         setInfoDrawer(true)
+                    },
+                    onDeleteAllMessages = {
+                        setDeleteAllMessagesDialog(true)
                     }
                 )
             },
@@ -139,5 +145,16 @@ fun ConversationScreen(
                 )
             }
         }
+
+        DeleteAllMessagesConfirmationDialog(
+            isVisible = deleteAllMessagesDialogVisible,
+            close = { setDeleteAllMessagesDialog(false) },
+            onDelete = viewModel::deleteAllMessages
+        )
+
+        DeleteAllMessagesFailedDialog(
+            deletionResult = viewModel.deletionResult.value,
+            close = viewModel::confirmDeletionResult
+        )
     }
 }
diff --git a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
index bef737f175fa25ec663d0e77151b710072d8848d..f716ff85ca8d57db78ecdfeffd69ecbc4473a03a 100644
--- a/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
+++ b/src/main/kotlin/org/briarproject/briar/desktop/conversation/ConversationViewModel.kt
@@ -28,6 +28,7 @@ import org.briarproject.briar.api.attachment.AttachmentReader
 import org.briarproject.briar.api.autodelete.UnexpectedTimerException
 import org.briarproject.briar.api.autodelete.event.ConversationMessagesDeletedEvent
 import org.briarproject.briar.api.conversation.ConversationManager
+import org.briarproject.briar.api.conversation.DeletionResult
 import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent
 import org.briarproject.briar.api.identity.AuthorManager
 import org.briarproject.briar.api.introduction.IntroductionManager
@@ -75,11 +76,15 @@ constructor(
 
     private val _newMessage = mutableStateOf("")
 
+    private val _deletionResult = mutableStateOf<DeletionResult?>(null)
+
     val contactItem = _contactItem.asState()
     val messages = _messages.asList()
 
     val newMessage = _newMessage.asState()
 
+    val deletionResult = _deletionResult.asState()
+
     fun setContactId(id: ContactId) {
         if (_contactId.value == id)
             return
@@ -299,4 +304,22 @@ constructor(
             loadMessages(txn, contactItem.value!!)
         }
     }
+
+    fun deleteAllMessages() = runOnDbThread {
+        try {
+            val result = conversationManager.deleteAllMessages(_contactId.value!!)
+            reloadConversationAfterDeletingMessages(result)
+        } finally {
+            // todo:
+        }
+    }
+
+    private fun reloadConversationAfterDeletingMessages(result: DeletionResult) {
+        reloadMessages()
+        _deletionResult.value = if (!result.allDeleted()) result else null
+    }
+
+    fun confirmDeletionResult() {
+        _deletionResult.value = null
+    }
 }
diff --git a/src/main/resources/strings/BriarDesktop.properties b/src/main/resources/strings/BriarDesktop.properties
index 179f08dca8edc95debaa133a30f8dbb0fbabdcd1..32c74cea6aca208b62d6eacb3ed1e991ebb6166f 100644
--- a/src/main/resources/strings/BriarDesktop.properties
+++ b/src/main/resources/strings/BriarDesktop.properties
@@ -29,6 +29,15 @@ contacts.search.title=Contacts
 
 # Conversation
 conversation.message.new=New Message
+conversation.delete.all.dialog.title=Confirm Message Deletion
+conversation.delete.all.dialog.message=Are you sure that you want to delete all messages?
+conversation.delete.failed.dialog.title=Could not delete all messages
+conversation.delete.failed.dialog.message.ongoing_both=Messages related to ongoing invitations and introductions cannot be deleted until they conclude.
+conversation.delete.failed.dialog.message.ongoing_introductions=Messages related to ongoing introductions cannot be deleted until they conclude.
+conversation.delete.failed.dialog.message.ongoing_invitations=Messages related to ongoing invitations cannot be deleted until they conclude.
+conversation.delete.failed.dialog.message.not_all_selected_both=To delete an invitation or introduction, you need to select the request and the response.
+conversation.delete.failed.dialog.message.not_all_selected_introductions=To delete an introduction, you need to select the request and the response.
+conversation.delete.failed.dialog.message.not_all_selected_invitations=To delete an invitation, you need to select the request and the response.
 
 # Private Groups
 groups.card.created=Created by {0}
@@ -92,6 +101,9 @@ main.help.tor.port.socks=Tor Socks Port
 main.help.tor.port.control=Tor Control Port
 
 # Miscellaneous
+cancel=Cancel
+delete=Delete
+ok=OK
 password=Password
 accept=Accept
 decline=Decline