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:
@@ -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(
|
||||
|
||||
+2
-2
@@ -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) },
|
||||
) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user