From 46057d75258be907e9f2ed579f8cc6b1ea5adfcd Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Mon, 26 Jan 2026 22:25:28 +0100 Subject: [PATCH 1/3] fix: place currency symbol correctly based on locale convention MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some currencies like PLN, EUR, CZK display their symbol after the amount (e.g., "0.35zł" not "zł0.35"), while others like USD, GBP display it before. This adds formattedWithSymbol() to ConvertedAmount which formats amounts with proper symbol placement based on the currency code. --- .../commands/NotifyPaymentReceivedHandler.kt | 4 +- .../main/java/to/bitkit/models/Currency.kt | 39 ++++++ .../bitkit/ui/components/BalanceHeaderView.kt | 28 +++- .../java/to/bitkit/ui/components/Money.kt | 3 +- .../ui/components/NumberPadTextField.kt | 25 +++- .../bitkit/ui/components/WalletBalanceView.kt | 9 +- .../activity/components/ActivityRow.kt | 13 +- .../wallets/receive/ReceiveConfirmScreen.kt | 6 +- .../wallets/send/SendCoinSelectionScreen.kt | 2 +- .../java/to/bitkit/models/CurrencyTest.kt | 120 ++++++++++++++++++ 10 files changed, 231 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandler.kt b/app/src/main/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandler.kt index fb17d1adf..57af17d2b 100644 --- a/app/src/main/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandler.kt +++ b/app/src/main/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandler.kt @@ -111,9 +111,9 @@ class NotifyPaymentReceivedHandler @Inject constructor( val amountText = converted?.let { val btcDisplay = it.bitcoinDisplay(settings.displayUnit) if (settings.primaryDisplay == PrimaryDisplay.BITCOIN) { - "${btcDisplay.symbol} ${btcDisplay.value} (${it.symbol}${it.formatted})" + "${btcDisplay.symbol} ${btcDisplay.value} (${it.formattedWithSymbol()})" } else { - "${it.symbol}${it.formatted} (${btcDisplay.symbol} ${btcDisplay.value})" + "${it.formattedWithSymbol()} (${btcDisplay.symbol} ${btcDisplay.value})" } } ?: "$BITCOIN_SYMBOL ${sats.formatToModernDisplay()}" diff --git a/app/src/main/java/to/bitkit/models/Currency.kt b/app/src/main/java/to/bitkit/models/Currency.kt index 9d5256caa..8d17dc91a 100644 --- a/app/src/main/java/to/bitkit/models/Currency.kt +++ b/app/src/main/java/to/bitkit/models/Currency.kt @@ -76,6 +76,8 @@ data class ConvertedAmount( val sats: Long, val locale: Locale = Locale.getDefault(), ) { + val isSymbolSuffix: Boolean get() = currency in SUFFIX_SYMBOL_CURRENCIES + data class BitcoinDisplayComponents( val symbol: String, val value: String, @@ -88,6 +90,8 @@ data class ConvertedAmount( value = formattedValue, ) } + + fun formattedWithSymbol(): String = value.formatCurrencyWithSymbol(currency, symbol) } fun Long.formatMoney( @@ -145,5 +149,40 @@ fun BigDecimal.formatCurrency(decimalPlaces: Int = FIAT_DECIMALS, locale: Locale return runCatching { formatter.format(this) }.getOrNull() } +fun BigDecimal.formatCurrencyWithSymbol( + currencyCode: String, + currencySymbol: String? = null, + decimalPlaces: Int = FIAT_DECIMALS, +): String { + val formatted = formatCurrency(decimalPlaces) ?: "0.00" + val symbol = currencySymbol + ?: runCatching { java.util.Currency.getInstance(currencyCode) }.getOrNull()?.symbol + ?: currencyCode + + return if (currencyCode in SUFFIX_SYMBOL_CURRENCIES) { + "$formatted$symbol" + } else { + "$symbol$formatted" + } +} + +fun isSuffixSymbolCurrency(currencyCode: String): Boolean = currencyCode in SUFFIX_SYMBOL_CURRENCIES + +private val SUFFIX_SYMBOL_CURRENCIES = setOf( + "BGN", // Bulgarian Lev (10,00 лв) + "CHF", // Swiss Franc (10.00 CHF) + "CZK", // Czech Koruna (10,00 Kč) + "DKK", // Danish Krone (10,00 kr) + "HRK", // Croatian Kuna (10,00 kn) + "HUF", // Hungarian Forint (10 000 Ft) + "ISK", // Icelandic Króna (10.000 kr) + "NOK", // Norwegian Krone (10,00 kr) + "PLN", // Polish Złoty (0,35 zł) + "RON", // Romanian Leu (10,00 lei) + "RUB", // Russian Ruble (10,00 ₽) + "SEK", // Swedish Krona (10,00 kr) + "TRY", // Turkish Lira (10,00 ₺) +) + /** Represent this sat value in Bitcoin BigDecimal. */ fun Long.asBtc(): BigDecimal = BigDecimal(this).divide(BigDecimal(SATS_IN_BTC), BTC_SCALE, RoundingMode.HALF_UP) diff --git a/app/src/main/java/to/bitkit/ui/components/BalanceHeaderView.kt b/app/src/main/java/to/bitkit/ui/components/BalanceHeaderView.kt index 565cbd41b..fe8505b24 100644 --- a/app/src/main/java/to/bitkit/ui/components/BalanceHeaderView.kt +++ b/app/src/main/java/to/bitkit/ui/components/BalanceHeaderView.kt @@ -89,10 +89,12 @@ fun BalanceHeaderView( modifier = modifier, smallRowSymbol = if (isBitcoinPrimary) fiat.symbol else btc.symbol, smallRowText = if (isBitcoinPrimary) fiat.formatted else btc.value, + smallRowIsSymbolSuffix = if (isBitcoinPrimary) fiat.isSymbolSuffix else false, smallRowModifier = Modifier.testTag("$testTag-secondary"), largeRowPrefix = prefix, largeRowText = if (isBitcoinPrimary) btc.value else fiat.formatted, largeRowSymbol = if (isBitcoinPrimary) btc.symbol else fiat.symbol, + largeRowIsSymbolSuffix = if (isBitcoinPrimary) false else fiat.isSymbolSuffix, largeRowModifier = Modifier.testTag("$testTag-primary"), showSymbol = if (isBitcoinPrimary) showBitcoinSymbol else true, hideBalance = shouldHideBalance, @@ -110,10 +112,12 @@ fun BalanceHeader( modifier: Modifier = Modifier, smallRowSymbol: String? = null, smallRowText: String, + smallRowIsSymbolSuffix: Boolean = false, smallRowModifier: Modifier = Modifier, largeRowPrefix: String? = null, largeRowText: String, largeRowSymbol: String, + largeRowIsSymbolSuffix: Boolean = false, largeRowModifier: Modifier = Modifier, showSymbol: Boolean, hideBalance: Boolean = false, @@ -137,6 +141,7 @@ fun BalanceHeader( SmallRow( symbol = smallRowSymbol, text = smallRowText, + isSymbolSuffix = smallRowIsSymbolSuffix, hideBalance = hideBalance, modifier = smallRowModifier, ) @@ -151,6 +156,7 @@ fun BalanceHeader( text = largeRowText, symbol = largeRowSymbol, showSymbol = showSymbol, + isSymbolSuffix = largeRowIsSymbolSuffix, hideBalance = hideBalance, modifier = largeRowModifier, ) @@ -187,6 +193,7 @@ fun LargeRow( symbol: String, showSymbol: Boolean, modifier: Modifier = Modifier, + isSymbolSuffix: Boolean = false, hideBalance: Boolean = false, ) { Row( @@ -202,7 +209,7 @@ fun LargeRow( .testTag("MoneySign") ) } - if (showSymbol) { + if (showSymbol && !isSymbolSuffix) { Display( text = symbol, color = Colors.White64, @@ -221,6 +228,15 @@ fun LargeRow( modifier = Modifier.testTag("MoneyText") ) } + if (showSymbol && isSymbolSuffix) { + Display( + text = symbol, + color = Colors.White64, + modifier = Modifier + .padding(start = 8.dp) + .testTag("MoneyFiatSymbol") + ) + } } } @@ -229,6 +245,7 @@ private fun SmallRow( symbol: String?, text: String, modifier: Modifier = Modifier, + isSymbolSuffix: Boolean = false, hideBalance: Boolean = false, ) { Row( @@ -236,7 +253,7 @@ private fun SmallRow( horizontalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier, ) { - if (symbol != null) { + if (symbol != null && !isSymbolSuffix) { Caption13Up( text = symbol, color = Colors.White64, @@ -254,6 +271,13 @@ private fun SmallRow( modifier = Modifier.testTag("MoneyText") ) } + if (symbol != null && isSymbolSuffix) { + Caption13Up( + text = symbol, + color = Colors.White64, + modifier = Modifier.testTag("MoneyFiatSymbol") + ) + } } } diff --git a/app/src/main/java/to/bitkit/ui/components/Money.kt b/app/src/main/java/to/bitkit/ui/components/Money.kt index cc863d7a1..920efa6c3 100644 --- a/app/src/main/java/to/bitkit/ui/components/Money.kt +++ b/app/src/main/java/to/bitkit/ui/components/Money.kt @@ -144,8 +144,9 @@ fun rememberMoneyText( } } else { buildString { - if (showSymbol) append("${converted.symbol} ") + if (showSymbol && !converted.isSymbolSuffix) append("${converted.symbol} ") append(converted.formatted) + if (showSymbol && converted.isSymbolSuffix) append("${converted.symbol}") } } } diff --git a/app/src/main/java/to/bitkit/ui/components/NumberPadTextField.kt b/app/src/main/java/to/bitkit/ui/components/NumberPadTextField.kt index add65817c..b158ff18f 100644 --- a/app/src/main/java/to/bitkit/ui/components/NumberPadTextField.kt +++ b/app/src/main/java/to/bitkit/ui/components/NumberPadTextField.kt @@ -20,6 +20,7 @@ import to.bitkit.models.BITCOIN_SYMBOL import to.bitkit.models.PrimaryDisplay import to.bitkit.models.USD_SYMBOL import to.bitkit.models.formatToModernDisplay +import to.bitkit.models.isSuffixSymbolCurrency import to.bitkit.repositories.CurrencyState import to.bitkit.ui.LocalCurrencies import to.bitkit.ui.shared.modifiers.clickableAlpha @@ -48,6 +49,8 @@ fun NumberPadTextField( showPlaceholder = true, satoshis = uiState.value.sats, currencySymbol = currencies.currencySymbol, + isSymbolSuffix = currencies.primaryDisplay == PrimaryDisplay.FIAT && + isSuffixSymbolCurrency(currencies.selectedCurrency), showSecondaryField = showSecondaryField, ) } @@ -60,11 +63,14 @@ private fun MoneyAmount( satoshis: Long, modifier: Modifier = Modifier, currencySymbol: String = BITCOIN_SYMBOL, + isSymbolSuffix: Boolean = false, showPlaceholder: Boolean = true, showSecondaryField: Boolean = true, valueStyle: SpanStyle = SpanStyle(color = Colors.White), placeholderStyle: SpanStyle = SpanStyle(color = Colors.White50), ) { + val symbol = if (unit == PrimaryDisplay.BITCOIN) BITCOIN_SYMBOL else currencySymbol + Column( modifier = modifier.semantics { contentDescription = value }, horizontalAlignment = Alignment.Start @@ -76,11 +82,13 @@ private fun MoneyAmount( Row( verticalAlignment = Alignment.CenterVertically, ) { - Display( - text = if (unit == PrimaryDisplay.BITCOIN) BITCOIN_SYMBOL else currencySymbol, - color = Colors.White64, - modifier = Modifier.padding(end = 6.dp) - ) + if (!isSymbolSuffix) { + Display( + text = symbol, + color = Colors.White64, + modifier = Modifier.padding(end = 6.dp) + ) + } Display( text = buildAnnotatedString { if (value != placeholder) { @@ -95,6 +103,13 @@ private fun MoneyAmount( } } ) + if (isSymbolSuffix) { + Display( + text = symbol, + color = Colors.White64, + modifier = Modifier.padding(start = 6.dp) + ) + } } } } diff --git a/app/src/main/java/to/bitkit/ui/components/WalletBalanceView.kt b/app/src/main/java/to/bitkit/ui/components/WalletBalanceView.kt index db8ca0a9b..f303de52d 100644 --- a/app/src/main/java/to/bitkit/ui/components/WalletBalanceView.kt +++ b/app/src/main/java/to/bitkit/ui/components/WalletBalanceView.kt @@ -146,8 +146,13 @@ private fun RowScope.Content( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp), ) { - BodyMSB(text = converted.symbol) - BodyMSB(text = if (isHidden) UiConstants.HIDE_BALANCE_SHORT else converted.formatted) + if (converted.isSymbolSuffix) { + BodyMSB(text = if (isHidden) UiConstants.HIDE_BALANCE_SHORT else converted.formatted) + BodyMSB(text = converted.symbol) + } else { + BodyMSB(text = converted.symbol) + BodyMSB(text = if (isHidden) UiConstants.HIDE_BALANCE_SHORT else converted.formatted) + } } } } diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/ActivityRow.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/ActivityRow.kt index f9961bdd0..bd2b9ea85 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/ActivityRow.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/activity/components/ActivityRow.kt @@ -245,6 +245,7 @@ private fun AmountView( titlePrefix = prefix, subtitle = converted.formatted, subtitleSymbol = converted.symbol, + isSymbolSuffix = converted.isSymbolSuffix, hideBalance = hideBalance, ) } else { @@ -253,6 +254,7 @@ private fun AmountView( titleSymbol = converted.symbol, titlePrefix = prefix, subtitle = btcValue, + isSymbolSuffix = converted.isSymbolSuffix, hideBalance = hideBalance, ) } @@ -267,6 +269,7 @@ private fun AmountViewContent( modifier: Modifier = Modifier, titleSymbol: String? = null, subtitleSymbol: String? = null, + isSymbolSuffix: Boolean = false, hideBalance: Boolean = false, ) { Column( @@ -280,7 +283,7 @@ private fun AmountViewContent( horizontalArrangement = Arrangement.spacedBy(1.dp), ) { BodyM(text = titlePrefix, color = Colors.White64) - if (titleSymbol != null) { + if (titleSymbol != null && !isSymbolSuffix) { BodyMSB(text = titleSymbol, color = Colors.White64) } Spacer(modifier = Modifier.width(2.dp)) @@ -291,6 +294,9 @@ private fun AmountViewContent( ) { isHidden -> BodyMSB(text = if (isHidden) UiConstants.HIDE_BALANCE_SHORT else title) } + if (titleSymbol != null && isSymbolSuffix) { + BodyMSB(text = titleSymbol, color = Colors.White64) + } } // Subtitle row with static symbol @@ -298,7 +304,7 @@ private fun AmountViewContent( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(3.dp), ) { - if (subtitleSymbol != null) { + if (subtitleSymbol != null && !isSymbolSuffix) { CaptionB(text = subtitleSymbol, color = Colors.White64) } AnimatedContent( @@ -311,6 +317,9 @@ private fun AmountViewContent( color = Colors.White64, ) } + if (subtitleSymbol != null && isSymbolSuffix) { + CaptionB(text = subtitleSymbol, color = Colors.White64) + } } } } diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt index d1a93def0..9722288f3 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/receive/ReceiveConfirmScreen.kt @@ -64,13 +64,13 @@ fun ReceiveConfirmScreen( val networkFeeFormatted = remember(entry.networkFeeSat) { currency.convert(entry.networkFeeSat) - ?.let { converted -> "${converted.symbol}${converted.formatted}" } + ?.let { converted -> converted.formattedWithSymbol() } ?: entry.networkFeeSat.toString() } val serviceFeeFormatted = remember(entry.serviceFeeSat) { currency.convert(entry.serviceFeeSat) - ?.let { converted -> "${converted.symbol}${converted.formatted}" } + ?.let { converted -> converted.formattedWithSymbol() } ?: entry.serviceFeeSat.toString() } @@ -84,7 +84,7 @@ fun ReceiveConfirmScreen( val btcComponents = converted.bitcoinDisplay(displayUnit) "${btcComponents.symbol} ${btcComponents.value}" } else { - "${converted.symbol} ${converted.formatted}" + converted.formattedWithSymbol() } } ?: sats.toString() } diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendCoinSelectionScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendCoinSelectionScreen.kt index 96b032de3..c27f20d2d 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendCoinSelectionScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendCoinSelectionScreen.kt @@ -213,7 +213,7 @@ private fun UtxoRow( currency.convert(sats = amount)?.let { converted -> val btcValue = converted.bitcoinDisplay(displayUnit).value BodyMSB(text = btcValue) - BodySSB(text = "${converted.symbol} ${converted.formatted}", color = Colors.White64) + BodySSB(text = converted.formattedWithSymbol(), color = Colors.White64) } } diff --git a/app/src/test/java/to/bitkit/models/CurrencyTest.kt b/app/src/test/java/to/bitkit/models/CurrencyTest.kt index 7b9ad3324..971da1f8b 100644 --- a/app/src/test/java/to/bitkit/models/CurrencyTest.kt +++ b/app/src/test/java/to/bitkit/models/CurrencyTest.kt @@ -2,6 +2,7 @@ package to.bitkit.models import org.junit.Assert.assertEquals import org.junit.Test +import java.math.BigDecimal import java.util.Locale class CurrencyTest { @@ -37,4 +38,123 @@ class CurrencyTest { assertEquals("0.00012345", formatted) } + + @Test + fun `formatCurrencyWithSymbol places USD symbol before amount`() { + val value = BigDecimal("10.50") + + val formatted = value.formatCurrencyWithSymbol("USD") + + assertEquals("$10.50", formatted) + } + + @Test + fun `formatCurrencyWithSymbol places GBP symbol before amount`() { + val value = BigDecimal("10.50") + + val formatted = value.formatCurrencyWithSymbol("GBP") + + assertEquals("£10.50", formatted) + } + + @Test + fun `formatCurrencyWithSymbol places PLN symbol after amount`() { + val value = BigDecimal("0.35") + + val formatted = value.formatCurrencyWithSymbol("PLN", "zł") + + assertEquals("0.35zł", formatted) + } + + @Test + fun `formatCurrencyWithSymbol places EUR symbol before amount`() { + val value = BigDecimal("10.00") + + val formatted = value.formatCurrencyWithSymbol("EUR", "€") + + assertEquals("€10.00", formatted) + } + + @Test + fun `formatCurrencyWithSymbol places CZK symbol after amount`() { + val value = BigDecimal("250.00") + + val formatted = value.formatCurrencyWithSymbol("CZK", "Kč") + + assertEquals("250.00Kč", formatted) + } + + @Test + fun `formatCurrencyWithSymbol places SEK symbol after amount`() { + val value = BigDecimal("100.00") + + val formatted = value.formatCurrencyWithSymbol("SEK", "kr") + + assertEquals("100.00kr", formatted) + } + + @Test + fun `formatCurrencyWithSymbol places CHF symbol after amount`() { + val value = BigDecimal("50.00") + + val formatted = value.formatCurrencyWithSymbol("CHF", "CHF") + + assertEquals("50.00CHF", formatted) + } + + @Test + fun `formatCurrencyWithSymbol falls back to currency code when no symbol provided`() { + val value = BigDecimal("100.00") + + val formatted = value.formatCurrencyWithSymbol("PLN") + + // Without explicit symbol, falls back to Java Currency symbol (PLN in non-Polish locale) + assertEquals("100.00PLN", formatted) + } + + @Test + fun `formatCurrencyWithSymbol handles unknown currency code`() { + val value = BigDecimal("100.00") + + val formatted = value.formatCurrencyWithSymbol("XYZ") + + assertEquals("XYZ100.00", formatted) + } + + @Test + fun `formatCurrencyWithSymbol formats large amounts with grouping`() { + val value = BigDecimal("1234567.89") + + val formatted = value.formatCurrencyWithSymbol("USD") + + assertEquals("$1,234,567.89", formatted) + } + + @Test + fun `ConvertedAmount formattedWithSymbol returns correct format for prefix currency`() { + val converted = ConvertedAmount( + value = BigDecimal("10.50"), + formatted = "10.50", + symbol = "$", + currency = "USD", + flag = "🇺🇸", + sats = 1000L, + ) + + assertEquals("$10.50", converted.formattedWithSymbol()) + } + + @Test + fun `ConvertedAmount formattedWithSymbol returns correct format for suffix currency`() { + val converted = ConvertedAmount( + value = BigDecimal("0.35"), + formatted = "0.35", + symbol = "zł", + currency = "PLN", + flag = "🇵🇱", + sats = 100L, + ) + + assertEquals("0.35zł", converted.formattedWithSymbol()) + } } From 67573813fa0869676d5c8d67a07654ca415129cf Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Tue, 10 Feb 2026 11:09:27 +0100 Subject: [PATCH 2/3] fix: remove private currency set comments --- .../main/java/to/bitkit/models/Currency.kt | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/to/bitkit/models/Currency.kt b/app/src/main/java/to/bitkit/models/Currency.kt index 8d17dc91a..fe5d83fa7 100644 --- a/app/src/main/java/to/bitkit/models/Currency.kt +++ b/app/src/main/java/to/bitkit/models/Currency.kt @@ -169,19 +169,19 @@ fun BigDecimal.formatCurrencyWithSymbol( fun isSuffixSymbolCurrency(currencyCode: String): Boolean = currencyCode in SUFFIX_SYMBOL_CURRENCIES private val SUFFIX_SYMBOL_CURRENCIES = setOf( - "BGN", // Bulgarian Lev (10,00 лв) - "CHF", // Swiss Franc (10.00 CHF) - "CZK", // Czech Koruna (10,00 Kč) - "DKK", // Danish Krone (10,00 kr) - "HRK", // Croatian Kuna (10,00 kn) - "HUF", // Hungarian Forint (10 000 Ft) - "ISK", // Icelandic Króna (10.000 kr) - "NOK", // Norwegian Krone (10,00 kr) - "PLN", // Polish Złoty (0,35 zł) - "RON", // Romanian Leu (10,00 lei) - "RUB", // Russian Ruble (10,00 ₽) - "SEK", // Swedish Krona (10,00 kr) - "TRY", // Turkish Lira (10,00 ₺) + "BGN", + "CHF", + "CZK", + "DKK", + "HRK", + "HUF", + "ISK", + "NOK", + "PLN", + "RON", + "RUB", + "SEK", + "TRY", ) /** Represent this sat value in Bitcoin BigDecimal. */ From ef9338fe0fef8c226f38c4d09a9e1c8680eb5d82 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Tue, 10 Feb 2026 13:15:24 +0100 Subject: [PATCH 3/3] fix: align weather fee format with ios --- .../to/bitkit/data/widgets/WeatherService.kt | 4 +- .../main/java/to/bitkit/models/Currency.kt | 12 +++-- .../java/to/bitkit/models/CurrencyTest.kt | 53 +++++++++++++++++++ 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/to/bitkit/data/widgets/WeatherService.kt b/app/src/main/java/to/bitkit/data/widgets/WeatherService.kt index 81869f75d..dda33e962 100644 --- a/app/src/main/java/to/bitkit/data/widgets/WeatherService.kt +++ b/app/src/main/java/to/bitkit/data/widgets/WeatherService.kt @@ -139,8 +139,8 @@ class WeatherService @Inject constructor( } private fun formatFeeForDisplay(sats: Long): String { - val usdValue = currencyRepo.convertSatsToFiat(sats, USD).getOrNull() - return usdValue?.formatted.orEmpty() + val selectedFiatValue = currencyRepo.convertSatsToFiat(sats).getOrNull() + return selectedFiatValue?.formattedWithSymbol(withSpace = true).orEmpty() } } diff --git a/app/src/main/java/to/bitkit/models/Currency.kt b/app/src/main/java/to/bitkit/models/Currency.kt index fe5d83fa7..b37612564 100644 --- a/app/src/main/java/to/bitkit/models/Currency.kt +++ b/app/src/main/java/to/bitkit/models/Currency.kt @@ -91,7 +91,11 @@ data class ConvertedAmount( ) } - fun formattedWithSymbol(): String = value.formatCurrencyWithSymbol(currency, symbol) + fun formattedWithSymbol(withSpace: Boolean = false): String = value.formatCurrencyWithSymbol( + currencyCode = currency, + currencySymbol = symbol, + withSpace = withSpace, + ) } fun Long.formatMoney( @@ -152,17 +156,19 @@ fun BigDecimal.formatCurrency(decimalPlaces: Int = FIAT_DECIMALS, locale: Locale fun BigDecimal.formatCurrencyWithSymbol( currencyCode: String, currencySymbol: String? = null, + withSpace: Boolean = false, decimalPlaces: Int = FIAT_DECIMALS, ): String { val formatted = formatCurrency(decimalPlaces) ?: "0.00" val symbol = currencySymbol ?: runCatching { java.util.Currency.getInstance(currencyCode) }.getOrNull()?.symbol ?: currencyCode + val separator = if (withSpace) " " else "" return if (currencyCode in SUFFIX_SYMBOL_CURRENCIES) { - "$formatted$symbol" + "$formatted$separator$symbol" } else { - "$symbol$formatted" + "$symbol$separator$formatted" } } diff --git a/app/src/test/java/to/bitkit/models/CurrencyTest.kt b/app/src/test/java/to/bitkit/models/CurrencyTest.kt index 971da1f8b..a79ced622 100644 --- a/app/src/test/java/to/bitkit/models/CurrencyTest.kt +++ b/app/src/test/java/to/bitkit/models/CurrencyTest.kt @@ -102,6 +102,31 @@ class CurrencyTest { assertEquals("50.00CHF", formatted) } + @Test + fun `formatCurrencyWithSymbol places USD symbol before amount with space`() { + val value = BigDecimal("10.50") + + val formatted = value.formatCurrencyWithSymbol( + currencyCode = "USD", + withSpace = true, + ) + + assertEquals("$ 10.50", formatted) + } + + @Test + fun `formatCurrencyWithSymbol places PLN symbol after amount with space`() { + val value = BigDecimal("0.35") + + val formatted = value.formatCurrencyWithSymbol( + currencyCode = "PLN", + currencySymbol = "zł", + withSpace = true, + ) + + assertEquals("0.35 zł", formatted) + } + @Test fun `formatCurrencyWithSymbol falls back to currency code when no symbol provided`() { val value = BigDecimal("100.00") @@ -157,4 +182,32 @@ class CurrencyTest { assertEquals("0.35zł", converted.formattedWithSymbol()) } + + @Test + fun `ConvertedAmount formattedWithSymbol returns correct format for prefix currency with space`() { + val converted = ConvertedAmount( + value = BigDecimal("10.50"), + formatted = "10.50", + symbol = "$", + currency = "USD", + flag = "🇺🇸", + sats = 1000L, + ) + + assertEquals("$ 10.50", converted.formattedWithSymbol(withSpace = true)) + } + + @Test + fun `ConvertedAmount formattedWithSymbol returns correct format for suffix currency with space`() { + val converted = ConvertedAmount( + value = BigDecimal("0.35"), + formatted = "0.35", + symbol = "zł", + currency = "PLN", + flag = "🇵🇱", + sats = 100L, + ) + + assertEquals("0.35 zł", converted.formattedWithSymbol(withSpace = true)) + } }