diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift index 8edc792..d598274 100644 --- a/iosApp/iosApp/ContentView.swift +++ b/iosApp/iosApp/ContentView.swift @@ -36,7 +36,11 @@ struct ContentView: View { resource: MR.strings.shared.btc_to_currency, args: [state.defaultCurrencyCode] )), - footer: Text(state.statusLine) + footer: Group { + if !state.statusLine.isEmpty { + Text(state.statusLine) + } + } ) { Picker( IosLocalizationKt.localizedString(resource: MR.strings.shared.price_source), diff --git a/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/ConverterUiStateDisplay.kt b/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/ConverterUiStateDisplay.kt index 7c79f55..fcc0194 100644 --- a/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/ConverterUiStateDisplay.kt +++ b/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/ConverterUiStateDisplay.kt @@ -10,7 +10,7 @@ import kotlin.time.Instant /** Derived display strings/flags shared between the Compose UI and the iOS SwiftUI bridge. */ fun ConverterUiState.statusLine(): String { - if (isManualSource) return if (sourceName.isEmpty()) "" else "via $sourceName" + if (isManualSource) return "" val updated = lastUpdated?.let { "updated ${it.toDateTimeString()}" } ?: "loading rates…" return if (sourceName.isEmpty()) updated else "via $sourceName, $updated" } diff --git a/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/PriceScreen.kt b/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/PriceScreen.kt index 0432e46..2921710 100644 --- a/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/PriceScreen.kt +++ b/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/PriceScreen.kt @@ -138,10 +138,13 @@ fun PriceScreen( ) { Column(modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) { Text("SatsPrice", style = MaterialTheme.typography.headlineMedium) - Text( - text = state.statusLine(), - style = MaterialTheme.typography.bodySmall, - ) + val statusLine = state.statusLine() + if (statusLine.isNotEmpty()) { + Text( + text = statusLine, + style = MaterialTheme.typography.bodySmall, + ) + } } Card(modifier = Modifier.fillMaxWidth(), colors = SectionColors) { diff --git a/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/PriceViewModel.kt b/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/PriceViewModel.kt index a40c00b..9253e16 100644 --- a/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/PriceViewModel.kt +++ b/shared/src/commonMain/kotlin/xyz/tyiu/satsprice/ui/PriceViewModel.kt @@ -142,7 +142,12 @@ class PriceViewModel( fun refresh() { if (currentSource === manualSource && manualSource.rate == null) { - _uiState.update { it.copy(isLoading = false, errorMessage = null) } + // No rate typed in (yet): nothing to price fiat currencies with, so clear any stale + // rate this might otherwise still compute fiat amounts from. + rates = null + _uiState.update { + it.copy(isLoading = false, errorMessage = null, pricedCurrencyCodes = emptySet()) + } return } viewModelScope.launch { @@ -230,8 +235,6 @@ class PriceViewModel( _uiState.update { it.copy(manualRateInput = formatAmount(seedRate, decimalDigitsFor(defaultCurrencyCode))) } - } else { - _uiState.update { it.copy(pricedCurrencyCodes = emptySet()) } } refresh() } @@ -254,10 +257,11 @@ class PriceViewModel( fun onManualRateChanged(value: String) { val sanitized = sanitizeDecimalInput(value) _uiState.update { it.copy(manualRateInput = sanitized) } - sanitized.toBigDecimalOrNull()?.let { parsed -> - manualSource.rate = parsed - refresh() - } + // Also cleared (rather than left as the last valid rate) when sanitized fails to parse — + // e.g. the field is emptied, or is mid-edit on an incomplete number — so refresh() then + // wipes any fiat amounts computed from it, rather than leaving stale ones on screen. + manualSource.rate = sanitized.toBigDecimalOrNull() + refresh() } private fun updateAmount(field: EditedField, rawValue: String) {