From fc8f057e3e6dfbaaa5a4f1ef1857e32ec257a908 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 May 2026 16:39:34 +0000 Subject: [PATCH 1/3] feat: add Public chip to onchain wallet card The onchain wallet's Taproot address is derived from the account's Nostr pubkey, so anyone with the npub can see its balance and transaction history on-chain. Surface that fact directly on the card with a tappable "(i) Public" chip that opens a dialog explaining the privacy implication and recommending private (non-Nostr) channels for funding and draining. --- .../screen/loggedIn/wallet/OnchainSection.kt | 63 +++++++++++++++++++ .../commons/icons/symbols/MaterialSymbols.kt | 1 + 2 files changed, 64 insertions(+) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt index e6eb069f5b..5d5a63e6ea 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt @@ -35,6 +35,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card @@ -43,6 +44,7 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -180,6 +182,8 @@ private fun HeaderRow( style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold, ) + Spacer(modifier = Modifier.width(8.dp)) + PublicChip() } Spacer(modifier = Modifier.height(4.dp)) Text( @@ -193,6 +197,65 @@ private fun HeaderRow( } } +@Composable +private fun PublicChip() { + var showDialog by remember { mutableStateOf(false) } + val onSurfaceVariant = MaterialTheme.colorScheme.onSurfaceVariant + + Row( + modifier = + Modifier + .clip(RoundedCornerShape(8.dp)) + .background(onSurfaceVariant.copy(alpha = 0.12f)) + .clickable { showDialog = true } + .padding(horizontal = 6.dp, vertical = 2.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + symbol = MaterialSymbols.Info, + contentDescription = null, + tint = onSurfaceVariant, + modifier = Modifier.size(12.dp), + ) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = "Public", + style = MaterialTheme.typography.labelSmall, + color = onSurfaceVariant, + fontWeight = FontWeight.Medium, + ) + } + + if (showDialog) { + AlertDialog( + onDismissRequest = { showDialog = false }, + icon = { + Icon( + symbol = MaterialSymbols.Info, + contentDescription = null, + modifier = Modifier.size(24.dp), + ) + }, + title = { Text("This wallet is public") }, + text = { + Text( + "Your Taproot address is derived from your Nostr public key, " + + "so anyone who knows your npub can see this wallet's balance " + + "and transaction history on the blockchain.\n\n" + + "To preserve your privacy, use private channels — not Nostr " + + "events — when funding or draining this wallet, and avoid " + + "linking deposits or withdrawals to identifiable activity.", + ) + }, + confirmButton = { + TextButton(onClick = { showDialog = false }) { + Text("Got it") + } + }, + ) + } +} + @Composable private fun BitcoinChip(orange: Color) { Box( diff --git a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/icons/symbols/MaterialSymbols.kt b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/icons/symbols/MaterialSymbols.kt index 1530c9af57..3be153731f 100644 --- a/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/icons/symbols/MaterialSymbols.kt +++ b/commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/icons/symbols/MaterialSymbols.kt @@ -129,6 +129,7 @@ object MaterialSymbols { val HourglassEmpty = MaterialSymbol("\uE88B") val HourglassTop = MaterialSymbol("\uEA5B") val Image = MaterialSymbol("\uE3F4") + val Info = MaterialSymbol("\uE88E") val Key = MaterialSymbol("\uE73C") val KeyboardArrowDown = MaterialSymbol("\uE313") val KeyboardArrowUp = MaterialSymbol("\uE316") From 339ad6dae77e7c2b2da4841b8a5053f74fa5351c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 May 2026 17:10:02 +0000 Subject: [PATCH 2/3] feat: clarify privacy guidance on onchain wallet popup Replace the vague "private channels" wording with concrete advice: fund and drain via non-private accounts (e.g. exchanges), never mix with cold wallets, and treat the balance as funds anyone with the nsec can spend. --- .../amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt index 5d5a63e6ea..81c9c6c675 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt @@ -242,9 +242,11 @@ private fun PublicChip() { "Your Taproot address is derived from your Nostr public key, " + "so anyone who knows your npub can see this wallet's balance " + "and transaction history on the blockchain.\n\n" + - "To preserve your privacy, use private channels — not Nostr " + - "events — when funding or draining this wallet, and avoid " + - "linking deposits or withdrawals to identifiable activity.", + "To preserve your privacy, fund and drain this wallet from " + + "and to non-private accounts, like exchanges. Never mix " + + "these funds with your cold wallets, and treat them as " + + "money you can lose, since anyone in control of your nsec " + + "can spend it.", ) }, confirmButton = { From 89b47da42a5c5bb57ae8e4979d7323b8caca247f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 20 May 2026 17:20:16 +0000 Subject: [PATCH 3/3] feat: extract onchain Public chip strings to resources Move the chip label, dialog title/body, and confirm button text out of the composable and into strings.xml under wallet_onchain_public_* so they can be translated via Crowdin. --- .../screen/loggedIn/wallet/OnchainSection.kt | 21 ++++++------------- amethyst/src/main/res/values/strings.xml | 4 ++++ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt index 81c9c6c675..92b39e41d0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/OnchainSection.kt @@ -62,6 +62,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols import com.vitorpamplona.amethyst.model.LocalCache @@ -69,6 +70,7 @@ import com.vitorpamplona.amethyst.ui.components.util.setText import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.routes.Route import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.bitcoinColor import com.vitorpamplona.quartz.nipBCOnchainZaps.taproot.TaprootAddress import kotlinx.coroutines.Dispatchers @@ -219,7 +221,7 @@ private fun PublicChip() { ) Spacer(modifier = Modifier.width(4.dp)) Text( - text = "Public", + text = stringRes(R.string.wallet_onchain_public_chip), style = MaterialTheme.typography.labelSmall, color = onSurfaceVariant, fontWeight = FontWeight.Medium, @@ -236,22 +238,11 @@ private fun PublicChip() { modifier = Modifier.size(24.dp), ) }, - title = { Text("This wallet is public") }, - text = { - Text( - "Your Taproot address is derived from your Nostr public key, " + - "so anyone who knows your npub can see this wallet's balance " + - "and transaction history on the blockchain.\n\n" + - "To preserve your privacy, fund and drain this wallet from " + - "and to non-private accounts, like exchanges. Never mix " + - "these funds with your cold wallets, and treat them as " + - "money you can lose, since anyone in control of your nsec " + - "can spend it.", - ) - }, + title = { Text(stringRes(R.string.wallet_onchain_public_dialog_title)) }, + text = { Text(stringRes(R.string.wallet_onchain_public_dialog_body)) }, confirmButton = { TextButton(onClick = { showDialog = false }) { - Text("Got it") + Text(stringRes(R.string.wallet_onchain_public_dialog_confirm)) } }, ) diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index fd7a5d35ba..1bc3b6a9db 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -1859,6 +1859,10 @@ No on-chain address available for this account. No chain backend is configured. Pending + Public + This wallet is public + Your Taproot address is derived from your Nostr public key, so anyone who knows your npub can see this wallet\'s balance and transaction history on the blockchain.\n\nTo preserve your privacy, fund and drain this wallet from and to non-private accounts, like exchanges. Never mix these funds with your cold wallets, and treat them as money you can lose, since anyone in control of your nsec can spend it. + Got it Security Filters Import Follows