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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXRAunSJS2dBx7B79qTMew
This commit is contained in:
Claude
2026-06-17 22:32:11 +00:00
parent f970f92837
commit c340fd05cf
3 changed files with 91 additions and 76 deletions
@@ -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<CashuMintDirectoryEntry>,
accountViewModel: AccountViewModel,
nav: INav,
verifications: Map<String, MintPingState>,
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
}
}
}
@@ -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 = {},
)
}
+3 -3
View File
@@ -2197,7 +2197,7 @@
<string name="cashu_p2pk_autogen_warning_edit">Will invalidate any pending nutzaps locked to the current key.</string>
<string name="cashu_p2pk_keep_current">Keep current key</string>
<string name="cashu_p2pk_manual_label">P2PK private key (hex)</string>
<string name="wallet_edit_cashu_title">Edit Cashu wallet</string>
<string name="wallet_edit_cashu_title">Edit mints</string>
<string name="wallet_save_changes">Save changes</string>
<string name="cashu_mint_no_recs">No recommendations yet</string>
<string name="cashu_mint_recommended_by">Recommended by</string>
@@ -2227,8 +2227,8 @@
<string name="cashu_history_incoming">Received</string>
<string name="cashu_history_outgoing">Sent</string>
<string name="cashu_settings_title">Cashu Wallet Settings</string>
<string name="cashu_settings_edit_wallet">Edit wallet details</string>
<string name="cashu_settings_edit_wallet_subtitle">Mints, nutzap key</string>
<string name="cashu_settings_edit_wallet">My mints</string>
<string name="cashu_settings_edit_wallet_subtitle">Add or remove the mints your wallet uses.</string>
<string name="cashu_settings_my_recommendations">My mint recommendations</string>
<string name="cashu_settings_recommendations_subtitle">Publicly vouch for mints you trust, and retract recommendations.</string>
<string name="cashu_settings_no_recommendations">You haven\'t recommended any mints yet. Tap the thumbs-up next to a mint to publicly vouch for it.</string>