Add a Reset button to clear selected currencies, with confirmation
New "Reset" button in the currency picker's top bar (next to Done), shown only when more than the pinned default currency is selected. Tapping it asks for confirmation before removing every other selected currency, via PriceViewModel.onSelectedCurrenciesReset() — same recompute/persist pattern as onFiatCurrencyToggled. Compose uses an AlertDialog; iOS/macOS uses .alert with a destructive Reset action, matching the destructive-role toolbar button that triggers it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FXcrACM5sok3M1KQJ8kByy
This commit is contained in:
@@ -238,7 +238,9 @@ struct ContentView: View {
|
||||
pricedCurrencyCodes: state.pricedCurrencyCodes,
|
||||
sourceName: state.sourceName,
|
||||
localeCurrencyCode: state.localeCurrencyCode,
|
||||
onToggle: { viewModel.onFiatCurrencyToggled($0) }
|
||||
selectedCount: state.selectedCurrencyCodes.count,
|
||||
onToggle: { viewModel.onFiatCurrencyToggled($0) },
|
||||
onReset: { viewModel.onSelectedCurrenciesReset() }
|
||||
)
|
||||
}
|
||||
.environment(\.openURL, OpenURLAction { url in
|
||||
|
||||
@@ -39,6 +39,10 @@ final class ConverterViewModel: ObservableObject {
|
||||
bridge.onFiatCurrenciesReordered(newOrder: newOrder)
|
||||
}
|
||||
|
||||
func onSelectedCurrenciesReset() {
|
||||
bridge.onSelectedCurrenciesReset()
|
||||
}
|
||||
|
||||
func onSourceSelected(_ name: String) {
|
||||
bridge.onSourceSelected(name: name)
|
||||
}
|
||||
|
||||
@@ -8,10 +8,13 @@ struct CurrencyPickerSheet: View {
|
||||
let pricedCurrencyCodes: [String]
|
||||
let sourceName: String
|
||||
let localeCurrencyCode: String?
|
||||
let selectedCount: Int
|
||||
let onToggle: (String) -> Void
|
||||
let onReset: () -> Void
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var searchQuery = ""
|
||||
@State private var showResetConfirmation = false
|
||||
|
||||
private func matches(_ info: CurrencyInfo) -> Bool {
|
||||
SystemCurrenciesKt.matchesCurrencySearch(info: info, query: searchQuery)
|
||||
@@ -60,6 +63,16 @@ struct CurrencyPickerSheet: View {
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
#endif
|
||||
.toolbar {
|
||||
if selectedCount > 1 {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button(
|
||||
IosLocalizationKt.localizedString(resource: MR.strings.shared.reset_selected_currencies_button),
|
||||
role: .destructive
|
||||
) {
|
||||
showResetConfirmation = true
|
||||
}
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button(IosLocalizationKt.localizedString(resource: MR.strings.shared.done)) { dismiss() }
|
||||
}
|
||||
@@ -74,6 +87,23 @@ struct CurrencyPickerSheet: View {
|
||||
text: $searchQuery,
|
||||
prompt: IosLocalizationKt.localizedString(resource: MR.strings.shared.search_currencies_placeholder)
|
||||
)
|
||||
.alert(
|
||||
IosLocalizationKt.localizedString(resource: MR.strings.shared.reset_selected_currencies_confirmation_title),
|
||||
isPresented: $showResetConfirmation
|
||||
) {
|
||||
Button(
|
||||
IosLocalizationKt.localizedString(resource: MR.strings.shared.reset_selected_currencies_button),
|
||||
role: .destructive
|
||||
) {
|
||||
onReset()
|
||||
}
|
||||
Button(IosLocalizationKt.localizedString(resource: MR.strings.shared.cancel), role: .cancel) {}
|
||||
} message: {
|
||||
Text(IosLocalizationKt.localizedFormattedString(
|
||||
resource: MR.strings.shared.reset_selected_currencies_confirmation_message,
|
||||
args: [currentCurrency.code]
|
||||
))
|
||||
}
|
||||
#if os(macOS)
|
||||
// macOS sizes a .sheet() to its content's ideal size rather than the parent window's
|
||||
// size (unlike iOS, which presents modally full-size); without an explicit frame here,
|
||||
|
||||
@@ -63,6 +63,7 @@ class IosPriceViewModel {
|
||||
fun onFiatAmountChanged(code: String, value: String) = viewModel.onFiatAmountChanged(code, value)
|
||||
fun onFiatCurrencyToggled(code: String) = viewModel.onFiatCurrencyToggled(code)
|
||||
fun onFiatCurrenciesReordered(newOrder: List<String>) = viewModel.onFiatCurrenciesReordered(newOrder)
|
||||
fun onSelectedCurrenciesReset() = viewModel.onSelectedCurrenciesReset()
|
||||
fun onSourceSelected(name: String) = viewModel.onSourceSelected(name)
|
||||
fun onManualRateChanged(value: String) = viewModel.onManualRateChanged(value)
|
||||
fun onManualSatsPerCurrencyChanged(value: String) = viewModel.onManualSatsPerCurrencyChanged(value)
|
||||
|
||||
@@ -27,6 +27,7 @@ import androidx.compose.material.icons.filled.KeyboardDoubleArrowUp
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
@@ -120,6 +121,7 @@ fun PriceScreen(
|
||||
CurrencyPickerScreen(
|
||||
state = state,
|
||||
onToggle = viewModel::onFiatCurrencyToggled,
|
||||
onReset = viewModel::onSelectedCurrenciesReset,
|
||||
onDone = { showCurrencyPicker = false },
|
||||
)
|
||||
return
|
||||
@@ -501,8 +503,32 @@ private fun CurrencyRowMenu(
|
||||
private fun CurrencyPickerScreen(
|
||||
state: ConverterUiState,
|
||||
onToggle: (String) -> Unit,
|
||||
onReset: () -> Unit,
|
||||
onDone: () -> Unit,
|
||||
) {
|
||||
var showResetConfirmation by remember { mutableStateOf(false) }
|
||||
|
||||
if (showResetConfirmation) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showResetConfirmation = false },
|
||||
title = { Text(stringResource(MR.strings.reset_selected_currencies_confirmation_title)) },
|
||||
text = {
|
||||
Text(stringResource(MR.strings.reset_selected_currencies_confirmation_message, state.defaultCurrencyCode))
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
showResetConfirmation = false
|
||||
onReset()
|
||||
},
|
||||
) { Text(stringResource(MR.strings.reset_selected_currencies_button)) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showResetConfirmation = false }) { Text(stringResource(MR.strings.cancel)) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -515,7 +541,14 @@ private fun CurrencyPickerScreen(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(stringResource(MR.strings.currencies_section_title), style = MaterialTheme.typography.headlineSmall)
|
||||
TextButton(onClick = onDone) { Text(stringResource(MR.strings.done)) }
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
if (state.selectedFiatCurrencies.size > 1) {
|
||||
TextButton(onClick = { showResetConfirmation = true }) {
|
||||
Text(stringResource(MR.strings.reset_selected_currencies_button))
|
||||
}
|
||||
}
|
||||
TextButton(onClick = onDone) { Text(stringResource(MR.strings.done)) }
|
||||
}
|
||||
}
|
||||
|
||||
var searchQuery by remember { mutableStateOf("") }
|
||||
|
||||
@@ -207,6 +207,16 @@ class PriceViewModel(
|
||||
persistSelectionIfChanged(_uiState.value.selectedFiatCurrencies)
|
||||
}
|
||||
|
||||
/** Removes every selected currency except the pinned [defaultCurrencyCode]. */
|
||||
fun onSelectedCurrenciesReset() {
|
||||
_uiState.update { state ->
|
||||
if (state.selectedFiatCurrencies == listOf(defaultCurrencyCode)) return@update state
|
||||
val newState = state.copy(selectedFiatCurrencies = listOf(defaultCurrencyCode))
|
||||
rates?.let { recomputeFromKnownField(newState, it) } ?: newState
|
||||
}
|
||||
persistSelectionIfChanged(_uiState.value.selectedFiatCurrencies)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders the selected currencies to [newOrder]. Any code in [newOrder] that isn't
|
||||
* currently selected is ignored, and any currently-selected code missing from [newOrder]
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<string name="bitcoin_section_title">Bitcoin</string>
|
||||
<string name="btc_label">BTC</string>
|
||||
<string name="btc_to_currency">BTC to %1$s</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="clear_search_content_description">Clear search</string>
|
||||
<string name="currencies_section_title">Currencies</string>
|
||||
<string name="currencies_selected_count">%1$d selected</string>
|
||||
@@ -22,6 +23,9 @@
|
||||
<string name="priced_currencies_section_title">Priced Currencies</string>
|
||||
<string name="refresh_content_description">Refresh</string>
|
||||
<string name="remove_currency_content_description">Remove</string>
|
||||
<string name="reset_selected_currencies_button">Reset</string>
|
||||
<string name="reset_selected_currencies_confirmation_message">This keeps %1$s, but removes every other currency you\'ve added.</string>
|
||||
<string name="reset_selected_currencies_confirmation_title">Remove all selected currencies?</string>
|
||||
<string name="retry">Retry</string>
|
||||
<string name="sats_label">Sats</string>
|
||||
<string name="search_currencies_placeholder">Search currencies</string>
|
||||
|
||||
Reference in New Issue
Block a user