From c340fd05cf3e0d91224d25ec8bbecb94ae6a20eb Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 22:32:11 +0000 Subject: [PATCH] feat(cashu): reframe edit-wallet as mint editor; move Verify to suggestions Adjusts the mint editor to the post-key-rotation reality and tidies its UI: - Settings hub: "Edit wallet details / Mints, nutzap key" row becomes "My mints / Add or remove the mints your wallet uses." The edit screen title changes from "Edit Cashu wallet" to "Edit mints". - The per-mint Verify button moves off the already-added mints list and into the Matching/Popular mints suggestion rows, sitting to the left of the + button, with the reachability result shown under each suggestion. Reuses the existing per-URL mintVerifications state. - Fixes the Mint URL placeholder wrapping onto two lines (and inflating the field height) by capping it to a single ellipsized line. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01SXRAunSJS2dBx7B79qTMew --- .../loggedIn/wallet/AddCashuWalletScreen.kt | 157 ++++++++++-------- .../wallet/CashuWalletSettingsScreen.kt | 4 +- amethyst/src/main/res/values/strings.xml | 6 +- 3 files changed, 91 insertions(+), 76 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/AddCashuWalletScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/AddCashuWalletScreen.kt index ae8b7f08f5..f0c5985071 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/AddCashuWalletScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/AddCashuWalletScreen.kt @@ -65,6 +65,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R @@ -162,67 +163,27 @@ fun AddCashuWalletScreen( ) Spacer(modifier = Modifier.height(8.dp)) - val verifications by viewModel.mintVerifications.collectAsState() mints.forEachIndexed { index, mint -> - val verifyState = verifications[mint.trim().trimEnd('/')] Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), ) { - Column(modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp)) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = mint, - modifier = Modifier.weight(1f), - style = MaterialTheme.typography.bodyMedium, + Row( + modifier = Modifier.padding(horizontal = 12.dp, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = mint, + modifier = Modifier.weight(1f), + style = MaterialTheme.typography.bodyMedium, + ) + IconButton(onClick = { mints.removeAt(index) }) { + Icon( + symbol = MaterialSymbols.Delete, + contentDescription = stringRes(R.string.cashu_remove_mint), + modifier = Modifier.size(18.dp), + tint = MaterialTheme.colorScheme.error, ) - OutlinedButton( - onClick = { viewModel.verifyMint(mint) }, - enabled = verifyState !is MintPingState.Pinging, - contentPadding = PaddingValues(horizontal = 12.dp, vertical = 0.dp), - modifier = Modifier.height(32.dp), - ) { - if (verifyState is MintPingState.Pinging) { - CircularProgressIndicator(modifier = Modifier.size(14.dp), strokeWidth = 2.dp) - } else { - Text( - stringRes(R.string.cashu_verify), - style = MaterialTheme.typography.labelMedium, - ) - } - } - IconButton(onClick = { mints.removeAt(index) }) { - Icon( - symbol = MaterialSymbols.Delete, - contentDescription = stringRes(R.string.cashu_remove_mint), - modifier = Modifier.size(18.dp), - tint = MaterialTheme.colorScheme.error, - ) - } - } - when (val vs = verifyState) { - is MintPingState.Ok -> { - Text( - text = - if (vs.name.isNullOrBlank()) { - stringRes(R.string.cashu_mint_reachable) - } else { - stringRes(R.string.cashu_mint_reachable_named, vs.name) - }, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.primary, - modifier = Modifier.padding(bottom = 4.dp), - ) - } - is MintPingState.Failed -> { - Text( - text = stringRes(R.string.cashu_mint_unreachable, vs.message), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.error, - modifier = Modifier.padding(bottom = 4.dp), - ) - } - else -> Unit } } } @@ -243,7 +204,13 @@ fun AddCashuWalletScreen( viewModel.resetMintPing() }, label = { Text(stringRes(R.string.cashu_mint_url)) }, - placeholder = { Text("https://mint.example.com") }, + placeholder = { + Text( + "https://mint.example.com", + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + }, singleLine = true, modifier = Modifier.weight(1f), ) @@ -318,10 +285,13 @@ fun AddCashuWalletScreen( color = MaterialTheme.colorScheme.onSurfaceVariant, ) Spacer(modifier = Modifier.height(4.dp)) + val verifications by viewModel.mintVerifications.collectAsState() MintSuggestionList( suggestions = suggestions, accountViewModel = accountViewModel, nav = nav, + verifications = verifications, + onVerify = { url -> viewModel.verifyMint(url) }, onAdd = { entry -> val trimmed = entry.url.trim().trimEnd('/') if (trimmed.isNotEmpty() && trimmed !in mints) { @@ -465,9 +435,9 @@ private fun P2pkRadio( * Mint directory autocomplete rendered inline under the mint input. * Each row uses [MintDirectoryRow] so the user sees follower avatars * and recommendation counts the same way they would in a dedicated - * picker. Each row carries a `+` button that adds the mint straight to - * the wallet's mint list — the per-mint Verify button in that list lets - * the user check reachability afterwards. + * picker. Each row carries a small Verify button (to check the mint is + * reachable) and, to its right, a `+` button that adds the mint straight + * to the wallet's mint list. The verify result renders under the row. * * Rows are visually separated by an outlined surface and divider so * a long list reads as discrete entries instead of merging into a @@ -478,6 +448,8 @@ private fun MintSuggestionList( suggestions: List, accountViewModel: AccountViewModel, nav: INav, + verifications: Map, + onVerify: (String) -> Unit, onAdd: (CashuMintDirectoryEntry) -> Unit, ) { OutlinedCard( @@ -490,18 +462,61 @@ private fun MintSuggestionList( if (index > 0) { HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant) } - MintDirectoryRow( - entry = entry, - accountViewModel = accountViewModel, - nav = nav, - ) { - IconButton(onClick = { onAdd(entry) }) { - Icon( - symbol = MaterialSymbols.Add, - contentDescription = stringRes(R.string.cashu_add_mint), - modifier = Modifier.size(20.dp), - tint = MaterialTheme.colorScheme.primary, - ) + val verifyState = verifications[entry.url.trim().trimEnd('/')] + Column { + MintDirectoryRow( + entry = entry, + accountViewModel = accountViewModel, + nav = nav, + ) { + OutlinedButton( + onClick = { onVerify(entry.url) }, + enabled = verifyState !is MintPingState.Pinging, + contentPadding = PaddingValues(horizontal = 12.dp, vertical = 0.dp), + modifier = Modifier.height(32.dp), + ) { + if (verifyState is MintPingState.Pinging) { + CircularProgressIndicator(modifier = Modifier.size(14.dp), strokeWidth = 2.dp) + } else { + Text( + stringRes(R.string.cashu_verify), + style = MaterialTheme.typography.labelMedium, + ) + } + } + Spacer(modifier = Modifier.width(4.dp)) + IconButton(onClick = { onAdd(entry) }) { + Icon( + symbol = MaterialSymbols.Add, + contentDescription = stringRes(R.string.cashu_add_mint), + modifier = Modifier.size(20.dp), + tint = MaterialTheme.colorScheme.primary, + ) + } + } + when (val vs = verifyState) { + is MintPingState.Ok -> { + Text( + text = + if (vs.name.isNullOrBlank()) { + stringRes(R.string.cashu_mint_reachable) + } else { + stringRes(R.string.cashu_mint_reachable_named, vs.name) + }, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.padding(start = 12.dp, end = 12.dp, bottom = 8.dp), + ) + } + is MintPingState.Failed -> { + Text( + text = stringRes(R.string.cashu_mint_unreachable, vs.message), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.error, + modifier = Modifier.padding(start = 12.dp, end = 12.dp, bottom = 8.dp), + ) + } + else -> Unit } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/CashuWalletSettingsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/CashuWalletSettingsScreen.kt index df16060300..3233b2849a 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/CashuWalletSettingsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/CashuWalletSettingsScreen.kt @@ -423,8 +423,8 @@ fun SettingsRowEditWalletPreview() { ThemeComparisonColumn { SettingsRow( icon = MaterialSymbols.Edit, - title = "Edit wallet details", - subtitle = "Mints, nutzap key", + title = "My mints", + subtitle = "Add or remove the mints your wallet uses.", onClick = {}, ) } diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 58ece6104e..baaea037b7 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -2197,7 +2197,7 @@ Will invalidate any pending nutzaps locked to the current key. Keep current key P2PK private key (hex) - Edit Cashu wallet + Edit mints Save changes No recommendations yet Recommended by @@ -2227,8 +2227,8 @@ Received Sent Cashu Wallet Settings - Edit wallet details - Mints, nutzap key + My mints + Add or remove the mints your wallet uses. My mint recommendations Publicly vouch for mints you trust, and retract recommendations. You haven\'t recommended any mints yet. Tap the thumbs-up next to a mint to publicly vouch for it.