feat(android): typed-confirm on AdminUsers delete (audit v3 §4.20)

Delete-user was a plain Delete/Cancel AlertDialog — too permissive
for an irreversible action. Now mirrors Flutter's TypedConfirmSheet:
an OutlinedTextField where the admin must type "DELETE" before the
Delete button enables (case-insensitive). The button stays disabled
and muted until the word matches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 00:57:30 -04:00
parent ca0fb70370
commit 7f6aa9482a
@@ -512,18 +512,41 @@ private fun DeleteConfirmDialog(
onDismiss: () -> Unit,
onConfirm: () -> Unit,
) {
var typed by remember { mutableStateOf("") }
val confirmed = typed.trim().equals(DELETE_CONFIRM_WORD, ignoreCase = true)
AlertDialog(
onDismissRequest = onDismiss,
title = { Text("Delete user?") },
text = {
Text("Delete @${user.username}? This removes the account and all their data.")
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text(
"Delete @${user.username}? This removes the account and all " +
"their data. Type $DELETE_CONFIRM_WORD to confirm.",
)
OutlinedTextField(
value = typed,
onValueChange = { typed = it },
label = { Text("Type $DELETE_CONFIRM_WORD") },
singleLine = true,
modifier = Modifier.fillMaxWidth(),
)
}
},
confirmButton = {
TextButton(onClick = onConfirm) {
Text("Delete", color = MaterialTheme.colorScheme.error)
TextButton(onClick = onConfirm, enabled = confirmed) {
Text(
"Delete",
color = if (confirmed) {
MaterialTheme.colorScheme.error
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
)
}
},
dismissButton = { TextButton(onClick = onDismiss) { Text("Cancel") } },
)
}
private const val DELETE_CONFIRM_WORD = "DELETE"