Always show all currencies, flagging ones the active source doesn't price

Previously a currency the active price source had no rate for was hidden
entirely, so switching sources (e.g. to CoinGecko, which quotes far fewer
fiat currencies than Coinbase) could silently drop it from the picker and
even from the user's own selection.

Every system currency is now always offered. ConverterUiState tracks which
codes the latest fetch actually priced, and both UIs (Compose and the
native SwiftUI screen, since iOS/macOS don't render Compose UI at all) show
a "Not priced by <source>" indicator for the rest, with the amount field
emptied and disabled rather than showing a stale or meaningless value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QpjMKWGoiT5aJBzhxvXkwp
This commit is contained in:
2026-09-08 17:19:48 +03:00
co-authored by Claude Sonnet 5
parent 84df992a82
commit b0f3744851
8 changed files with 170 additions and 32 deletions
+19 -2
View File
@@ -129,6 +129,8 @@ struct ContentView: View {
keyboardType: .decimalPad,
sanitize: sanitizeDecimalInput,
onChange: { viewModel.onFiatAmountChanged(code: row.code, value: $0) },
isPriced: state.pricedCurrencyCodes.contains(row.code),
sourceName: state.sourceName,
onMoveUp: index > 0 ? {
var codes = state.fiatRows.map(\.code)
codes.move(fromOffsets: [index], toOffset: index - 1)
@@ -164,6 +166,8 @@ struct ContentView: View {
currentCurrency: state.currentCurrency,
selectedOtherCurrencies: state.selectedOtherCurrencies,
unselectedCurrencies: state.unselectedCurrencies,
pricedCurrencyCodes: state.pricedCurrencyCodes,
sourceName: state.sourceName,
localeCurrencyCode: state.localeCurrencyCode,
onToggle: { viewModel.onFiatCurrencyToggled($0) }
)
@@ -202,6 +206,8 @@ struct ContentView: View {
keyboardType: NumericFieldKeyboard,
sanitize: @escaping (String) -> String,
onChange: @escaping (String) -> Void,
isPriced: Bool = true,
sourceName: String = "",
onMoveUp: (() -> Void)? = nil,
onMoveDown: (() -> Void)? = nil
) -> some View {
@@ -226,16 +232,27 @@ struct ContentView: View {
.buttonStyle(.borderless)
}
#endif
Text(label)
VStack(alignment: .leading) {
Text(label)
if !isPriced {
Text(IosLocalizationKt.localizedFormattedString(
resource: MR.strings.shared.currency_not_priced,
args: [sourceName]
))
.font(.caption2)
.foregroundColor(.red)
}
}
Spacer()
NumericField(
placeholder: "",
value: value,
value: isPriced ? value : "",
keyboardType: keyboardType,
sanitize: sanitize,
onChange: onChange,
alignment: .trailing
)
.disabled(!isPriced)
}
}
}
+15 -2
View File
@@ -5,6 +5,8 @@ struct CurrencyPickerSheet: View {
let currentCurrency: CurrencyInfo
let selectedOtherCurrencies: [CurrencyInfo]
let unselectedCurrencies: [CurrencyInfo]
let pricedCurrencyCodes: [String]
let sourceName: String
let localeCurrencyCode: String?
let onToggle: (String) -> Void
@@ -51,9 +53,20 @@ struct CurrencyPickerSheet: View {
@ViewBuilder
private func currencyRow(for info: CurrencyInfo, isSelected: Bool, onTap: (() -> Void)?) -> some View {
let isPriced = pricedCurrencyCodes.contains(info.code)
let content = HStack {
Text(currencyLabel(for: info))
.foregroundColor(.primary)
VStack(alignment: .leading) {
Text(currencyLabel(for: info))
.foregroundColor(.primary)
if !isPriced {
Text(IosLocalizationKt.localizedFormattedString(
resource: MR.strings.shared.currency_not_priced,
args: [sourceName]
))
.font(.caption)
.foregroundColor(.red)
}
}
Spacer()
if isSelected {
Image(systemName: "checkmark")