feat: add confirmation dialog when swiping to delete a draft

The swipe-to-delete gesture on the drafts screen was too sensitive,
causing accidental deletions. Added a confirmation dialog that appears
after the swipe gesture completes, requiring the user to confirm before
the draft is actually deleted.

https://claude.ai/code/session_01JWJxejv2EKrPj2KGEfPPxU
This commit is contained in:
Claude
2026-03-13 02:49:07 +00:00
parent 198aa0971c
commit 48485ce933
3 changed files with 67 additions and 2 deletions
@@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -38,10 +39,15 @@ import androidx.compose.material3.SwipeToDismissBoxState
import androidx.compose.material3.SwipeToDismissBoxValue.EndToStart
import androidx.compose.material3.SwipeToDismissBoxValue.Settled
import androidx.compose.material3.SwipeToDismissBoxValue.StartToEnd
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberSwipeToDismissBoxState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -88,6 +94,64 @@ fun SwipeToDeleteContainer(
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SwipeToDeleteWithConfirmation(
modifier: Modifier = Modifier,
onStartToEnd: () -> Unit,
content: @Composable (RowScope.() -> Unit),
) {
var showConfirmDialog by remember { mutableStateOf(false) }
val dismissState =
rememberSwipeToDismissBoxState(
confirmValueChange = {
when (it) {
StartToEnd -> {
showConfirmDialog = true
}
EndToStart -> {}
Settled -> {}
}
return@rememberSwipeToDismissBoxState false
},
positionalThreshold = { it * .40f },
)
if (showConfirmDialog) {
AlertDialog(
onDismissRequest = { showConfirmDialog = false },
title = { Text(text = stringRes(id = R.string.request_deletion)) },
text = { Text(text = stringRes(id = R.string.delete_draft_confirmation)) },
confirmButton = {
TextButton(
onClick = {
showConfirmDialog = false
onStartToEnd()
},
) {
Text(text = stringRes(id = R.string.yes))
}
},
dismissButton = {
TextButton(
onClick = { showConfirmDialog = false },
) {
Text(text = stringRes(id = R.string.no))
}
},
)
}
SwipeToDismissBox(
state = dismissState,
modifier = modifier,
backgroundContent = { DismissBackground(dismissState) },
enableDismissFromEndToStart = false,
content = content,
)
}
@Composable
fun DismissBackground(dismissState: SwipeToDismissBoxState) {
val color by animateColorAsState(
@@ -48,7 +48,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedContentState
import com.vitorpamplona.amethyst.commons.ui.feeds.FeedState
import com.vitorpamplona.amethyst.ui.components.SwipeToDeleteContainer
import com.vitorpamplona.amethyst.ui.components.SwipeToDeleteWithConfirmation
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
import com.vitorpamplona.amethyst.ui.feeds.RenderFeedContentState
import com.vitorpamplona.amethyst.ui.feeds.ScrollStateKeys.DRAFTS
@@ -168,7 +168,7 @@ private fun DraftFeedLoaded(
}
itemsIndexed(items.list, key = { _, item -> item.idHex }) { _, item ->
Row(Modifier.fillMaxWidth().animateItem()) {
SwipeToDeleteContainer(
SwipeToDeleteWithConfirmation(
modifier = Modifier.fillMaxWidth().animateContentSize(),
onStartToEnd = { accountViewModel.delete(item) },
) {
+1
View File
@@ -1473,6 +1473,7 @@
<string name="add_a_blossom_server">Add a Blossom Server</string>
<string name="delete_all">Delete all</string>
<string name="delete_all_drafts_confirmation">Are you sure you want to delete all drafts?</string>
<string name="delete_draft_confirmation">Are you sure you want to delete this draft?</string>
<string name="and_more" translatable="false">" +%1$s"</string>
<string name="stack">Stack:</string>