-
Notifications
You must be signed in to change notification settings - Fork 2
fix: align currency and calc widget with ios #884
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
0b79d78
64e4eed
902e3f0
1c9e0ee
c4d651e
e57d0e8
ab7f801
d365996
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -31,8 +31,11 @@ fun CalculatorInput( | |||||||||||||||||||
| currencySymbol: String, | ||||||||||||||||||||
| currencyName: String, | ||||||||||||||||||||
| modifier: Modifier = Modifier, | ||||||||||||||||||||
| keyboardType: KeyboardType = KeyboardType.Number, | ||||||||||||||||||||
| visualTransformation: VisualTransformation = VisualTransformation.None, | ||||||||||||||||||||
| ) { | ||||||||||||||||||||
| val displayCurrencySymbol = currencySymbol.toCalculatorDisplaySymbol() | ||||||||||||||||||||
|
|
||||||||||||||||||||
| TextInput( | ||||||||||||||||||||
| value = value, | ||||||||||||||||||||
| singleLine = true, | ||||||||||||||||||||
|
|
@@ -44,11 +47,11 @@ fun CalculatorInput( | |||||||||||||||||||
| .background(color = Colors.Gray6, shape = CircleShape) | ||||||||||||||||||||
| .size(32.dp) | ||||||||||||||||||||
| ) { | ||||||||||||||||||||
| BodyMSB(currencySymbol, color = Colors.Brand) | ||||||||||||||||||||
| BodyMSB(displayCurrencySymbol, color = Colors.Brand) | ||||||||||||||||||||
| } | ||||||||||||||||||||
| }, | ||||||||||||||||||||
| keyboardOptions = KeyboardOptions( | ||||||||||||||||||||
| keyboardType = KeyboardType.Number | ||||||||||||||||||||
| keyboardType = keyboardType | ||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing trailing comma in multi-line Per CLAUDE.md: "ALWAYS add trailing commas in multi-line declarations" Lines 52 to 56 in ab7f801
Suggested change
|
||||||||||||||||||||
| ), | ||||||||||||||||||||
| suffix = { CaptionB(currencyName.uppercase(), color = Colors.Gray1) }, | ||||||||||||||||||||
| colors = AppTextFieldDefaults.noIndicatorColors.copy( | ||||||||||||||||||||
|
|
@@ -60,6 +63,26 @@ fun CalculatorInput( | |||||||||||||||||||
| ) | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| internal fun sanitizeIntegerInput(raw: String): String = | ||||||||||||||||||||
| raw.filter { it.isDigit() } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| internal fun sanitizeDecimalInput(raw: String): String { | ||||||||||||||||||||
| val filtered = raw.filter { it.isDigit() || it == '.' } | ||||||||||||||||||||
| val dotIndex = filtered.indexOf('.') | ||||||||||||||||||||
| if (dotIndex == -1) return filtered | ||||||||||||||||||||
| return filtered.substring(0, dotIndex + 1) + | ||||||||||||||||||||
| filtered.substring(dotIndex + 1).replace(".", "") | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| internal fun String.toCalculatorDisplaySymbol(): String { | ||||||||||||||||||||
| val symbol = trim() | ||||||||||||||||||||
| return if (symbol.length >= 3) { | ||||||||||||||||||||
| symbol.take(1) | ||||||||||||||||||||
| } else { | ||||||||||||||||||||
| symbol | ||||||||||||||||||||
| } | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| @Preview(showBackground = true) | ||||||||||||||||||||
| @Composable | ||||||||||||||||||||
| private fun Preview() { | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,7 +6,6 @@ import androidx.compose.ui.text.input.TransformedText | |||||||||||||||||||||||||||
| import androidx.compose.ui.text.input.VisualTransformation | ||||||||||||||||||||||||||||
| import to.bitkit.models.BitcoinDisplayUnit | ||||||||||||||||||||||||||||
| import to.bitkit.models.SATS_GROUPING_SEPARATOR | ||||||||||||||||||||||||||||
| import to.bitkit.models.formatToModernDisplay | ||||||||||||||||||||||||||||
| import java.text.DecimalFormat | ||||||||||||||||||||||||||||
| import java.text.DecimalFormatSymbols | ||||||||||||||||||||||||||||
| import java.util.Locale | ||||||||||||||||||||||||||||
|
|
@@ -16,10 +15,10 @@ class BitcoinVisualTransformation( | |||||||||||||||||||||||||||
| ) : VisualTransformation { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| override fun filter(text: AnnotatedString): TransformedText { | ||||||||||||||||||||||||||||
| val originalText = text.text | ||||||||||||||||||||||||||||
| val originalText = sanitizeInput(text.text) | ||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Compose calls Lines 17 to 29 in ab7f801
Suggested fix: build the mapping between |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (originalText.isEmpty()) { | ||||||||||||||||||||||||||||
| return TransformedText(text, OffsetMapping.Identity) | ||||||||||||||||||||||||||||
| return TransformedText(AnnotatedString(""), OffsetMapping.Identity) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| val formattedText = when (displayUnit) { | ||||||||||||||||||||||||||||
|
|
@@ -35,21 +34,51 @@ class BitcoinVisualTransformation( | |||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| private fun sanitizeInput(text: String): String = when (displayUnit) { | ||||||||||||||||||||||||||||
| BitcoinDisplayUnit.MODERN -> text.filter { it.isDigit() } | ||||||||||||||||||||||||||||
| BitcoinDisplayUnit.CLASSIC -> sanitizeClassicInput(text) | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| private fun sanitizeClassicInput(text: String): String { | ||||||||||||||||||||||||||||
| val filtered = text.filter { it.isDigit() || it == '.' } | ||||||||||||||||||||||||||||
| val dotIndex = filtered.indexOf('.') | ||||||||||||||||||||||||||||
| if (dotIndex == -1) { | ||||||||||||||||||||||||||||
| return filtered | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| return filtered.substring(0, dotIndex + 1) + | ||||||||||||||||||||||||||||
| filtered.substring(dotIndex + 1).replace(".", "") | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| private fun formatModernDisplay(text: String): String { | ||||||||||||||||||||||||||||
| val longValue = text.replace("$SATS_GROUPING_SEPARATOR", "").toLongOrNull() ?: return text | ||||||||||||||||||||||||||||
| return longValue.formatToModernDisplay() | ||||||||||||||||||||||||||||
| val digits = text.replace("$SATS_GROUPING_SEPARATOR", "") | ||||||||||||||||||||||||||||
| if (digits.isEmpty()) { | ||||||||||||||||||||||||||||
| return "" | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| val normalizedDigits = digits.trimStart('0').ifEmpty { "0" } | ||||||||||||||||||||||||||||
| return normalizedDigits.reversed().chunked(3).joinToString(" ").reversed() | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| private fun formatClassicDisplay(text: String): String { | ||||||||||||||||||||||||||||
| val cleanText = text.replace(" ", "").replace(",", "") | ||||||||||||||||||||||||||||
| val doubleValue = cleanText.toDoubleOrNull() ?: return text | ||||||||||||||||||||||||||||
| if (cleanText.isEmpty() || cleanText == ".") { | ||||||||||||||||||||||||||||
| return cleanText | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| val endsWithDecimal = cleanText.endsWith(".") | ||||||||||||||||||||||||||||
| val textToFormat = if (endsWithDecimal) cleanText.dropLast(1) else cleanText | ||||||||||||||||||||||||||||
| if (textToFormat.isEmpty()) { | ||||||||||||||||||||||||||||
| return cleanText | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| val doubleValue = textToFormat.toDoubleOrNull() ?: return cleanText | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| val formatSymbols = DecimalFormatSymbols(Locale.getDefault()).apply { | ||||||||||||||||||||||||||||
| groupingSeparator = ' ' | ||||||||||||||||||||||||||||
| decimalSeparator = '.' | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| val formatter = DecimalFormat("#,##0.########", formatSymbols) | ||||||||||||||||||||||||||||
| return formatter.format(doubleValue) | ||||||||||||||||||||||||||||
| val formatted = formatter.format(doubleValue) | ||||||||||||||||||||||||||||
| return if (endsWithDecimal) "$formatted." else formatted | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| private fun createOffsetMapping(original: String, transformed: String): OffsetMapping { | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldHydrateFiatFromStoredBtcblocks recomputation after the initial hydration, makingcurrencyUiState.selectedCurrencyan ineffectiveLaunchedEffectkey.Trace:
fiatValueis set andcalculatorViewModel.updateCalculatorValues(fiatValue = ...)persists a non-emptyfiatValueto the store.selectedCurrencychanges,LaunchedEffectre-fires.shouldHydrateFiatFromStoredBtcis called withstoredFiatValue = calculatorValues.fiatValuewhich is now non-empty → returnsfalseimmediately.bitkit-android/app/src/main/java/to/bitkit/ui/screens/widgets/calculator/components/CalculatorCard.kt
Lines 63 to 84 in ab7f801
Suggested fix: add a separate
LaunchedEffectkeyed only oncurrencyUiState.selectedCurrencythat unconditionally recomputes the fiat from the stored BTC value (bypassing the hydration guard), or clearstoredFiatValuewhen the currency changes so the guard allows recomputation.