feat: add setting to hide the on-chain (Bitcoin) wallet

Some users don't want the on-chain wallet surfaced. Add a `showOnchainWallet`
UI preference (default true, so behavior is unchanged) that hides it across the
app when turned off:

- the "Bitcoin" card on the Wallet screen (OnchainSection)
- the on-chain chip on profiles (DisplayPaymentRailChips)
- the on-chain rail in the Send Payment screen

The flag lives in the existing UiSettings/UiSettingsFlow display-preferences
system (persisted to the shared-settings DataStore, alongside the other
show/hide profile toggles) and is exposed as a switch on the Profile UI
settings screen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UM57Rq5iUPL1SGhzhzTusa
This commit is contained in:
Claude
2026-07-05 19:24:48 +00:00
parent c27e977b7a
commit d69d99a8e1
8 changed files with 44 additions and 7 deletions
@@ -58,6 +58,11 @@ data class UiSettings(
val fontFamily: FontFamilyType = FontFamilyType.SYSTEM,
val fontSize: FontSizeType = FontSizeType.NORMAL,
val composeSignature: String = "",
// Whether the on-chain (Bitcoin/Taproot) wallet is shown across the app: the
// "Bitcoin" card on the wallet screen, the on-chain chip on profiles, and the
// on-chain rail in the Send Payment screen. Defaults to true (shown) so the
// behavior is unchanged for everyone who doesn't turn it off.
val showOnchainWallet: Boolean = true,
)
enum class ThemeType(
@@ -58,6 +58,7 @@ class UiSettingsFlow(
val fontFamily: MutableStateFlow<FontFamilyType> = MutableStateFlow(FontFamilyType.SYSTEM),
val fontSize: MutableStateFlow<FontSizeType> = MutableStateFlow(FontSizeType.NORMAL),
val composeSignature: MutableStateFlow<String> = MutableStateFlow(""),
val showOnchainWallet: MutableStateFlow<Boolean> = MutableStateFlow(true),
) {
val listOfFlows: List<Flow<Any?>> =
listOf<Flow<Any?>>(
@@ -90,6 +91,7 @@ class UiSettingsFlow(
fontFamily,
fontSize,
composeSignature,
showOnchainWallet,
)
// emits at every change in any of the propertyes.
@@ -126,6 +128,7 @@ class UiSettingsFlow(
flows[26] as FontFamilyType,
flows[27] as FontSizeType,
flows[28] as String,
flows[29] as Boolean,
)
}
@@ -160,6 +163,7 @@ class UiSettingsFlow(
fontFamily.value,
fontSize.value,
composeSignature.value,
showOnchainWallet.value,
)
fun update(torSettings: UiSettings): Boolean {
@@ -281,6 +285,10 @@ class UiSettingsFlow(
composeSignature.tryEmit(torSettings.composeSignature)
any = true
}
if (showOnchainWallet.value != torSettings.showOnchainWallet) {
showOnchainWallet.tryEmit(torSettings.showOnchainWallet)
any = true
}
return any
}
@@ -335,6 +343,7 @@ class UiSettingsFlow(
MutableStateFlow(uiSettings.fontFamily),
MutableStateFlow(uiSettings.fontSize),
MutableStateFlow(uiSettings.composeSignature),
MutableStateFlow(uiSettings.showOnchainWallet),
)
}
}
@@ -127,6 +127,7 @@ class UiSharedPreferences(
val UI_FONT_FAMILY = stringPreferencesKey("ui.font_family")
val UI_FONT_SIZE = stringPreferencesKey("ui.font_size")
val UI_COMPOSE_SIGNATURE = stringPreferencesKey("ui.compose_signature")
val UI_SHOW_ONCHAIN_WALLET = booleanPreferencesKey("ui.show_onchain_wallet")
suspend fun uiPreferences(context: Context): UiSettings? =
try {
@@ -168,6 +169,7 @@ class UiSharedPreferences(
fontFamily = preferences[UI_FONT_FAMILY]?.let { FontFamilyType.valueOf(it) } ?: FontFamilyType.SYSTEM,
fontSize = preferences[UI_FONT_SIZE]?.let { FontSizeType.valueOf(it) } ?: FontSizeType.NORMAL,
composeSignature = preferences[UI_COMPOSE_SIGNATURE] ?: "",
showOnchainWallet = preferences[UI_SHOW_ONCHAIN_WALLET] ?: true,
)
} catch (e: Exception) {
if (e is CancellationException) throw e
@@ -221,6 +223,7 @@ class UiSharedPreferences(
preferences[UI_FONT_FAMILY] = sharedSettings.fontFamily.name
preferences[UI_FONT_SIZE] = sharedSettings.fontSize.name
preferences[UI_COMPOSE_SIGNATURE] = sharedSettings.composeSignature
preferences[UI_SHOW_ONCHAIN_WALLET] = sharedSettings.showOnchainWallet
}
} catch (e: Exception) {
if (e is CancellationException) throw e
@@ -47,6 +47,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 androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.commons.hashtags.Cashu
import com.vitorpamplona.amethyst.commons.hashtags.CustomHashTagIcons
@@ -103,7 +104,9 @@ fun DisplayPaymentRailChips(
?.target
?.mintUrl
}
val onchainAvailable = LocalCache.onchainBackend != null
val showOnchainWallet by accountViewModel.settings.uiSettingsFlow.showOnchainWallet
.collectAsStateWithLifecycle()
val onchainAvailable = showOnchainWallet && LocalCache.onchainBackend != null
val address =
remember(baseUser.pubkeyHex) {
@@ -160,7 +160,11 @@ private fun SendPaymentLoaded(
// Cheap read, intentionally not remembered: the chain backend is wired up
// asynchronously at boot and a frozen `remember {}` would hide the
// on-chain rail for the whole composition if the screen won the race.
val onchainAvailable = LocalCache.onchainBackend != null
// Also honors the user's "show on-chain wallet" preference — off hides the
// rail everywhere, matching the wallet screen and profile chips.
val showOnchainWallet by accountViewModel.settings.uiSettingsFlow.showOnchainWallet
.collectAsStateWithLifecycle()
val onchainAvailable = showOnchainWallet && LocalCache.onchainBackend != null
// When navigated from a `bitcoin` payment target, the on-chain rail pays
// that announced address directly (plain send, no NIP-BC receipt) instead
// of the recipient's pubkey-derived Taproot address. Re-validated here in
@@ -92,6 +92,7 @@ fun ProfileUiSettingsContent(accountViewModel: AccountViewModel) {
val showAppRecommendations by ui.showProfileAppRecommendations.collectAsStateWithLifecycle()
val showZapReceived by ui.showProfileZapReceivedFeed.collectAsStateWithLifecycle()
val showFollowers by ui.showProfileFollowersFeed.collectAsStateWithLifecycle()
val showOnchainWallet by ui.showOnchainWallet.collectAsStateWithLifecycle()
Column(
modifier =
@@ -136,6 +137,13 @@ fun ProfileUiSettingsContent(accountViewModel: AccountViewModel) {
)
HorizontalDivider(modifier = Modifier.padding(horizontal = Size20dp))
ProfileUiSwitchRow(
title = stringRes(R.string.profile_ui_setting_onchain_wallet),
checked = showOnchainWallet,
onCheckedChange = { ui.showOnchainWallet.tryEmit(it) },
)
HorizontalDivider(modifier = Modifier.padding(horizontal = Size20dp))
Column(modifier = Modifier.padding(vertical = 12.dp, horizontal = Size20dp)) {
GalleryChoice(ui)
}
@@ -103,6 +103,8 @@ fun WalletScreen(
// the first composition pass. init() is idempotent — just sets refs.
cashuWalletViewModel.init(accountViewModel)
val showOnchainWallet by accountViewModel.settings.uiSettingsFlow.showOnchainWallet
.collectAsState()
val hasNwcWallet by walletViewModel.hasWalletSetup.collectAsState()
val cashuWalletEvent by cashuWalletViewModel.walletEvent.collectAsState()
val hasCashuWallet = cashuWalletEvent != null
@@ -147,11 +149,13 @@ fun WalletScreen(
},
) { padding ->
Column(modifier = Modifier.padding(padding)) {
OnchainSection(
accountViewModel = accountViewModel,
nav = nav,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
)
if (showOnchainWallet) {
OnchainSection(
accountViewModel = accountViewModel,
nav = nav,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
)
}
if (!hasWallet) {
NoWalletSetup(
modifier = Modifier,
+1
View File
@@ -2608,6 +2608,7 @@
<string name="profile_ui_setting_app_recommendations">App Recommendations</string>
<string name="profile_ui_setting_zap_received_feed">Zap Received Feed</string>
<string name="profile_ui_setting_followers_feed">Followers Feed</string>
<string name="profile_ui_setting_onchain_wallet">Bitcoin (on-chain) Wallet</string>
<string name="reactions_settings">Reaction Row</string>
<string name="reactions_settings_description">Configure which reaction buttons are shown, their order, and whether to display counters.</string>
<string name="reactions_settings_enabled">Enabled</string>