Updated on 2026-08-14

This commit is contained in:
Tangem 2024-04-02 21:07:38 +05:00
parent f9ace58267
commit 9ac2009c89
14 changed files with 250 additions and 149 deletions

View file

@ -1,10 +1,7 @@
package com.tangem.core.ui.components.appbar
import androidx.annotation.DrawableRes
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@ -66,8 +63,7 @@ fun AppBarWithBackButtonAndIconContent(
)
Column(
verticalArrangement = Arrangement.Center,
modifier = Modifier.weight(1f)
.animateContentSize(),
modifier = Modifier.weight(1f),
) {
AnimatedVisibility(
visible = !text.isNullOrBlank(),
@ -84,8 +80,8 @@ fun AppBarWithBackButtonAndIconContent(
}
AnimatedVisibility(
visible = !subtitle.isNullOrBlank(),
enter = fadeIn(),
exit = fadeOut(),
enter = fadeIn().plus(expandVertically()),
exit = fadeOut().plus(shrinkVertically()),
label = "Toolbar subtitle change",
) {
Text(
@ -93,6 +89,7 @@ fun AppBarWithBackButtonAndIconContent(
color = TangemTheme.colors.text.secondary,
maxLines = 1,
style = TangemTheme.typography.caption2,
modifier = Modifier.animateContentSize(),
)
}
}

View file

@ -2,12 +2,10 @@ package com.tangem.core.ui.components.fields
import androidx.annotation.FloatRange
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.TopCenter
@ -21,6 +19,7 @@ import androidx.compose.ui.text.ParagraphIntrinsics
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.createFontFamilyResolver
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDirection
import androidx.compose.ui.tooling.preview.Preview
@ -33,20 +32,21 @@ import java.text.DecimalFormat
/**
* Simple text field for amount input.
* Validates and trims input text using [DecimalFormat]. Formats visual output using [AmountVisualTransformation].
* Can display aligned placeholder and currency symbol [symbol].
* Validates and trims input text using [DecimalFormat]. Formats visual output using [visualTransformation].
* Can display aligned placeholder.
*
* @param value initial text
* @param decimals number of decimal places
* @param onValueChange callback
* @param textStyle text and placeholder styles
* @param modifier modifier
* @param symbol currency symbol
* @param color text color
* @param placeholderAlignment alignment of placeholder
* @param showPlaceholder show placeholder
* @param visualTransformation text visual transformation
* @param keyboardOptions keyboard options
*
* @param keyboardActions keyboard actions
* @param isEnabled is field editing enabled
* @param isAutoResize is text font auto resize
* @param reduceFactor font resize factor
* @see [SimpleTextField] for standard text field
*/
@Composable
@ -56,10 +56,8 @@ fun AmountTextField(
onValueChange: (String) -> Unit,
textStyle: TextStyle,
modifier: Modifier = Modifier,
symbol: String? = null,
color: Color = TangemTheme.colors.text.primary1,
placeholderAlignment: Alignment = TopStart,
showPlaceholder: Boolean = true,
visualTransformation: VisualTransformation = AmountVisualTransformation(decimals),
keyboardOptions: KeyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
),
@ -70,12 +68,6 @@ fun AmountTextField(
reduceFactor: Double = 0.9,
) {
val decimalFormat = rememberDecimalFormat()
val visualTransformation = remember { AmountVisualTransformation(decimals, symbol, decimalFormat) }
val placeholderTextAlign = if (placeholderAlignment == TopCenter) {
TextAlign.Center
} else {
TextAlign.Start
}
BoxWithConstraints(modifier = modifier) {
var fontSize = textStyle.fontSize
if (isAutoResize) {
@ -96,6 +88,7 @@ fun AmountTextField(
}
}
}
val textColor = if (value.isBlank()) TangemTheme.colors.text.disabled else color
SimpleTextField(
value = value,
onValueChange = { newText ->
@ -108,31 +101,13 @@ fun AmountTextField(
fontSize = fontSize,
textDirection = TextDirection.ContentOrLtr,
),
color = color,
color = textColor,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = true,
readOnly = !isEnabled,
visualTransformation = AmountVisualTransformation(decimals, symbol, decimalFormat),
visualTransformation = visualTransformation,
modifier = Modifier.background(TangemTheme.colors.background.action),
decorationBox = { innerTextField ->
Box {
if (value.isBlank() && showPlaceholder) {
var placeholder = decimalFormat.defaultFormat()
if (symbol != null) {
placeholder = placeholder.plus(" $symbol")
}
Text(
text = placeholder,
style = textStyle.copy(textDirection = TextDirection.ContentOrLtr),
color = TangemTheme.colors.text.disabled,
textAlign = placeholderTextAlign,
modifier = Modifier.align(placeholderAlignment),
)
}
innerTextField()
}
},
)
}
}
@ -153,9 +128,6 @@ private fun AmountTextFieldPreview(
AmountTextField(
value = text,
decimals = amount.decimals,
symbol = amount.symbol,
placeholderAlignment = amount.placeholderAlignment,
showPlaceholder = amount.showPlaceholder,
onValueChange = { text = it },
textStyle = TangemTheme.typography.h2.copy(
color = TangemTheme.colors.text.primary1,
@ -169,28 +141,24 @@ private fun AmountTextFieldPreview(
private class AmountTextFieldPreviewProvider : PreviewParameterProvider<AmountTextFieldPreviewData> {
override val values = sequenceOf(
AmountTextFieldPreviewData(
symbol = "USD",
value = "1000000,123123",
decimals = 3,
placeholderAlignment = TopStart,
showPlaceholder = true,
),
AmountTextFieldPreviewData(
symbol = null,
value = "1000000.123123",
decimals = 6,
placeholderAlignment = TopStart,
showPlaceholder = false,
),
AmountTextFieldPreviewData(
symbol = "$",
value = null,
decimals = 2,
showPlaceholder = true,
placeholderAlignment = TopCenter,
),
AmountTextFieldPreviewData(
symbol = null,
value = null,
decimals = 2,
showPlaceholder = true,
@ -200,7 +168,6 @@ private class AmountTextFieldPreviewProvider : PreviewParameterProvider<AmountTe
}
private data class AmountTextFieldPreviewData(
val symbol: String? = "$",
val value: String? = null,
val decimals: Int = 2,
val showPlaceholder: Boolean,

View file

@ -1,48 +1,82 @@
package com.tangem.core.ui.components.fields.visualtransformations
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.input.OffsetMapping
import androidx.compose.ui.text.input.TransformedText
import androidx.compose.ui.text.input.VisualTransformation
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.utils.defaultFormat
import com.tangem.core.ui.utils.formatWithThousands
import com.tangem.core.ui.utils.parseToBigDecimal
import java.text.DecimalFormat
class AmountVisualTransformation(
private val decimals: Int,
private val symbol: String? = null,
private val currencyCode: String? = null,
private val decimalFormat: DecimalFormat = DecimalFormat(),
) : VisualTransformation {
override fun filter(text: AnnotatedString): TransformedText {
val formattedText = decimalFormat.formatWithThousands(
var formattedAmount = decimalFormat.formatWithThousands(
text.text,
decimals,
)
formattedAmount = formattedAmount.ifEmpty { decimalFormat.defaultFormat() }
val decimalValue = text.text.parseToBigDecimal(decimals)
val formattedText = if (formattedAmount.isNotEmpty() && symbol != null) {
AnnotatedString(
if (currencyCode != null) {
BigDecimalFormatter.formatFiatAmount(
fiatAmount = decimalValue,
fiatCurrencyCode = currencyCode,
fiatCurrencySymbol = symbol,
)
} else {
BigDecimalFormatter.formatCryptoAmountUncapped(
cryptoAmount = decimalValue,
cryptoSymbol = symbol,
decimals = decimals,
)
},
)
} else {
AnnotatedString(decimalFormat.defaultFormat())
}
val groupingSymbol = decimalFormat.decimalFormatSymbols.groupingSeparator
return TransformedText(
text = buildAnnotatedString {
append(formattedText)
if (formattedText.isNotEmpty() && symbol != null) {
append(" $symbol")
}
},
offsetMapping = OffsetMappingImpl(text.text, formattedText, groupingSymbol),
text = formattedText,
offsetMapping = OffsetMappingImpl(text.text, formattedText, symbol, groupingSymbol),
)
}
private class OffsetMappingImpl(
private val text: String,
private val formattedText: String,
private val formattedText: AnnotatedString,
private val currencySymbol: String?,
private val gropingSymbol: Char,
) : OffsetMapping {
override fun originalToTransformed(offset: Int): Int {
var noneDigitCount = 0
var i = 0
val symbolOffset = currencySymbol?.let { formattedText.indexOf(it) } ?: -1
while (i < offset + noneDigitCount) {
if (formattedText.getOrNull(i++) == gropingSymbol) noneDigitCount++
val char = formattedText.getOrNull(i++)
if (char == gropingSymbol) noneDigitCount++
if (symbolOffset == 0 && char?.isWhitespace() == true) noneDigitCount++
}
return (offset + noneDigitCount).coerceIn(0, formattedText.length)
var transformedOffset = if (symbolOffset == 0 && currencySymbol != null) {
currencySymbol.length + offset + noneDigitCount
} else {
offset + noneDigitCount
}
transformedOffset = if (symbolOffset > 0) {
transformedOffset.coerceIn(0, minOf(symbolOffset, formattedText.length))
} else {
transformedOffset.coerceIn(0, formattedText.length)
}
return transformedOffset
}
override fun transformedToOriginal(offset: Int): Int {

View file

@ -17,10 +17,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import com.tangem.core.ui.components.fields.AmountTextField
import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.rememberDecimalFormat
/**
* Input row for entering amount. Manages correct amount format and validation
@ -76,7 +78,11 @@ fun InputRowEnterAmount(
AmountTextField(
value = text,
decimals = decimals,
symbol = symbol,
visualTransformation = AmountVisualTransformation(
decimals = decimals,
symbol = symbol,
decimalFormat = rememberDecimalFormat(),
),
onValueChange = onValueChange,
color = textColor,
textStyle = TangemTheme.typography.body2,

View file

@ -12,23 +12,28 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.tangem.core.ui.components.fields.AmountTextField
import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.rememberDecimalFormat
/**
* `Input Row Enter Info` for entering amount. Manages correct amount format and validation
* @param title title reference
* @param text primary text reference
* @param decimals amount text decimal count
* @param onValueChange text change callback
* @param modifier modifier
* @param symbol amount symbol
* @param info info text
* @param titleColor title color
* @param textColor text color
* @param isSingleLine text
* @param visualTransformation applied transformation to text
* @param infoColor info text color
* @param keyboardOptions keyboard options for field
* @param keyboardActions keyboard actions for field
* @param showDivider show divider
*
* @see [InputRowEnterInfo]
@ -71,7 +76,11 @@ fun InputRowEnterInfoAmount(
AmountTextField(
value = text,
decimals = decimals,
symbol = symbol,
visualTransformation = AmountVisualTransformation(
decimals = decimals,
symbol = symbol,
decimalFormat = rememberDecimalFormat(),
),
onValueChange = onValueChange,
color = textColor,
textStyle = TangemTheme.typography.body2,

View file

@ -38,6 +38,30 @@ object BigDecimalFormatter {
}
}
fun formatCryptoAmountUncapped(
cryptoAmount: BigDecimal?,
cryptoSymbol: String,
decimals: Int,
locale: Locale = Locale.getDefault(),
): String {
if (cryptoAmount == null) return EMPTY_BALANCE_SIGN
val formatter = NumberFormat.getNumberInstance(locale).apply {
maximumFractionDigits = decimals
minimumFractionDigits = minOf(2, decimals)
isGroupingUsed = true
roundingMode = RoundingMode.DOWN
}
return formatter.format(cryptoAmount).let {
if (cryptoSymbol.isEmpty()) {
it
} else {
it + "\u2009$cryptoSymbol"
}
}
}
fun formatCryptoAmount(
cryptoAmount: BigDecimal?,
cryptoCurrency: CryptoCurrency,

View file

@ -50,6 +50,7 @@ internal sealed class SendStates {
val segmentedButtonConfig: PersistentList<SendAmountSegmentedButtonsConfig>,
val amountTextField: SendTextField.AmountField,
val notifications: ImmutableList<SendNotification>,
val appCurrencyCode: String,
val isFeeLoading: Boolean,
) : SendStates()

View file

@ -40,6 +40,7 @@ internal class SendAmountStateConverter(
isPrimaryButtonEnabled = false,
notifications = persistentListOf(),
isFeeLoading = false,
appCurrencyCode = appCurrency.code,
segmentedButtonConfig = if (status.value.fiatRate.isNullOrZero()) {
persistentListOf()
} else {

View file

@ -50,7 +50,7 @@ internal class EthereumCustomFeeConverter(
),
SendTextField.CustomFee(
value = value.gasPrice.toString(),
decimals = ETHEREUM_GAS_DECIMALS,
decimals = GAS_DECIMALS,
symbol = ETHEREUM_GAS_UNIT,
title = resourceReference(R.string.send_gas_price),
footer = resourceReference(R.string.send_gas_price_footer),
@ -64,7 +64,7 @@ internal class EthereumCustomFeeConverter(
SendTextField.CustomFee(
value = value.gasLimit.toString(),
decimals = GAS_DECIMALS,
symbol = null,
symbol = "",
title = resourceReference(R.string.send_gas_limit),
footer = resourceReference(R.string.send_gas_limit_footer),
onValueChange = { clickIntents.onCustomFeeValueChange(GAS_LIMIT, it) },
@ -95,57 +95,10 @@ internal class EthereumCustomFeeConverter(
): ImmutableList<SendTextField.CustomFee> {
val mutableCustomValues = customValues.toMutableList()
return mutableCustomValues.apply {
val gasLimit = this[GAS_LIMIT].value.parseToBigDecimal(this[GAS_LIMIT].decimals)
when (index) {
FEE_AMOUNT -> {
val newFeeAmountDecimal = value.parseToBigDecimal(this[FEE_AMOUNT].decimals)
val newFeeAmount = newFeeAmountDecimal.movePointRight(this[FEE_AMOUNT].decimals)
val newGasPrice = newFeeAmount.divide(gasLimit, GAS_DECIMALS, RoundingMode.HALF_UP)
set(GAS_PRICE, this[GAS_PRICE].copy(value = newGasPrice.parseBigDecimal(GAS_DECIMALS)))
set(
index,
this[index].copy(
value = value,
label = getFeeFormatted(newFeeAmountDecimal),
),
)
}
GAS_PRICE -> {
val newGasPrice = value.parseToBigDecimal(this[GAS_PRICE].decimals)
.movePointLeft(this[GAS_PRICE].decimals)
val newFeeAmount = gasLimit * newGasPrice
set(
FEE_AMOUNT,
this[FEE_AMOUNT].copy(
value = newFeeAmount.parseBigDecimal(this[FEE_AMOUNT].decimals),
label = getFeeFormatted(newFeeAmount),
),
)
set(index, this[index].copy(value = value))
}
else -> {
val newGasLimit = value.parseToBigDecimal(this[GAS_LIMIT].decimals)
val gasPrice = this[GAS_PRICE].value.parseToBigDecimal(this[GAS_PRICE].decimals)
.movePointLeft(this[FEE_AMOUNT].decimals)
val newFeeAmount = newGasLimit * gasPrice
set(
FEE_AMOUNT,
this[FEE_AMOUNT].copy(
value = newFeeAmount.parseBigDecimal(this[FEE_AMOUNT].decimals),
label = getFeeFormatted(newFeeAmount),
),
)
set(
index,
this[index].copy(
value = value,
keyboardOptions = KeyboardOptions(
imeAction = if (!checkExceedBalance(newFeeAmount)) ImeAction.None else ImeAction.Done,
keyboardType = KeyboardType.Number,
),
),
)
}
FEE_AMOUNT -> setOnAmountChange(value, index)
GAS_PRICE -> setOnGasPriceChange(value, index)
else -> setOnGasLimitChange(value, index)
}
}.toImmutableList()
}
@ -170,9 +123,81 @@ internal class EthereumCustomFeeConverter(
return feeAmount == null || feeAmount.isZero() || feeAmount > currencyCryptoAmount
}
private fun MutableList<SendTextField.CustomFee>.setEmpty(index: Int) {
set(index, this[index].copy(value = ""))
}
private fun MutableList<SendTextField.CustomFee>.setOnAmountChange(value: String, index: Int) {
val gasLimit = this[GAS_LIMIT].value.parseToBigDecimal(this[GAS_LIMIT].decimals)
if (value.isBlank()) {
setEmpty(FEE_AMOUNT)
setEmpty(GAS_PRICE)
} else {
val newFeeAmountDecimal = value.parseToBigDecimal(this[FEE_AMOUNT].decimals)
val newFeeAmount = newFeeAmountDecimal.movePointRight(this[FEE_AMOUNT].decimals)
val newGasPrice = newFeeAmount.divide(gasLimit, GAS_DECIMALS, RoundingMode.HALF_UP)
set(GAS_PRICE, this[GAS_PRICE].copy(value = newGasPrice.parseBigDecimal(GAS_DECIMALS)))
set(
index,
this[index].copy(
value = value,
label = getFeeFormatted(newFeeAmountDecimal),
),
)
}
}
private fun MutableList<SendTextField.CustomFee>.setOnGasPriceChange(value: String, index: Int) {
val gasLimit = this[GAS_LIMIT].value.parseToBigDecimal(this[GAS_LIMIT].decimals)
if (value.isBlank()) {
setEmpty(FEE_AMOUNT)
setEmpty(GAS_PRICE)
} else {
val newGasPrice = value.parseToBigDecimal(this[GAS_PRICE].decimals)
.movePointLeft(this[GAS_PRICE].decimals)
val newFeeAmount = (gasLimit * newGasPrice).movePointLeft(this[FEE_AMOUNT].decimals)
set(
FEE_AMOUNT,
this[FEE_AMOUNT].copy(
value = newFeeAmount.parseBigDecimal(this[FEE_AMOUNT].decimals),
label = getFeeFormatted(newFeeAmount),
),
)
set(index, this[index].copy(value = value))
}
}
private fun MutableList<SendTextField.CustomFee>.setOnGasLimitChange(value: String, index: Int) {
if (value.isBlank()) {
setEmpty(FEE_AMOUNT)
setEmpty(GAS_LIMIT)
} else {
val newGasLimit = value.parseToBigDecimal(this[GAS_LIMIT].decimals)
val gasPrice = this[GAS_PRICE].value.parseToBigDecimal(this[GAS_PRICE].decimals)
.movePointLeft(this[FEE_AMOUNT].decimals)
val newFeeAmount = newGasLimit * gasPrice
set(
FEE_AMOUNT,
this[FEE_AMOUNT].copy(
value = newFeeAmount.parseBigDecimal(this[FEE_AMOUNT].decimals),
label = getFeeFormatted(newFeeAmount),
),
)
set(
index,
this[index].copy(
value = value,
keyboardOptions = KeyboardOptions(
imeAction = if (!checkExceedBalance(newFeeAmount)) ImeAction.None else ImeAction.Done,
keyboardType = KeyboardType.Number,
),
),
)
}
}
companion object {
private const val ETHEREUM_GAS_UNIT = "GWEI"
private const val ETHEREUM_GAS_DECIMALS = 18
private const val FEE_AMOUNT = 0
private const val GAS_PRICE = 1
private const val GAS_LIMIT = 2

View file

@ -75,12 +75,16 @@ internal class SendAmountFieldChangeConverter(
}
private fun SendUiState.emptyState(): SendUiState {
if (amountState == null) return this
val amountTextField = amountState.amountTextField
return copy(
amountState = amountState?.copy(
amountState = amountState.copy(
isPrimaryButtonEnabled = false,
amountTextField = amountState.amountTextField.copy(
amountTextField = amountTextField.copy(
value = "",
fiatValue = "",
cryptoAmount = amountTextField.cryptoAmount.copy(value = BigDecimal.ZERO),
fiatAmount = amountTextField.fiatAmount.copy(value = BigDecimal.ZERO),
isError = false,
),
),

View file

@ -24,6 +24,7 @@ internal object AmountStatePreviewData {
segmentedButtonConfig = persistentListOf(),
notifications = persistentListOf(),
isFeeLoading = false,
appCurrencyCode = "usd",
amountTextField = SendTextField.AmountField(
value = "123.123123123123123123",
onValueChange = {},

View file

@ -3,42 +3,49 @@ package com.tangem.features.send.impl.presentation.ui.amount
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredHeightIn
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment.Companion.BottomCenter
import androidx.compose.ui.Alignment.Companion.TopCenter
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDirection
import com.tangem.core.ui.components.fields.AmountTextField
import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.utils.defaultFormat
import com.tangem.core.ui.utils.rememberDecimalFormat
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
import kotlinx.coroutines.job
@Composable
internal fun AmountField(sendField: SendTextField.AmountField, isEnabled: Boolean) {
internal fun AmountField(sendField: SendTextField.AmountField, isEnabled: Boolean, appCurrencyCode: String) {
val decimalFormat = rememberDecimalFormat()
val (primaryValue, secondaryValue) = if (sendField.isFiatValue) {
sendField.fiatValue to sendField.value
val isFiatValue = sendField.isFiatValue
val currencyCode = if (isFiatValue) appCurrencyCode else null
val (primaryAmount, primaryValue) = if (isFiatValue) {
sendField.fiatAmount to sendField.fiatValue
} else {
sendField.value to sendField.fiatValue
sendField.cryptoAmount to sendField.value
}
val (primaryAmount, secondaryAmount) = if (sendField.isFiatValue) {
sendField.fiatAmount to sendField.cryptoAmount
} else {
sendField.cryptoAmount to sendField.fiatAmount
}
val requester = remember { FocusRequester() }
AmountTextField(
value = primaryValue,
decimals = primaryAmount.decimals,
symbol = primaryAmount.currencySymbol,
visualTransformation = AmountVisualTransformation(
decimals = primaryAmount.decimals,
symbol = primaryAmount.currencySymbol,
currencyCode = currencyCode,
decimalFormat = decimalFormat,
),
onValueChange = sendField.onValueChange,
keyboardOptions = sendField.keyboardOptions,
keyboardActions = sendField.keyboardActions,
@ -48,8 +55,8 @@ internal fun AmountField(sendField: SendTextField.AmountField, isEnabled: Boolea
),
isEnabled = isEnabled,
isAutoResize = true,
placeholderAlignment = TopCenter,
modifier = Modifier
.focusRequester(requester)
.padding(
top = TangemTheme.dimens.spacing24,
start = TangemTheme.dimens.spacing12,
@ -57,7 +64,18 @@ internal fun AmountField(sendField: SendTextField.AmountField, isEnabled: Boolea
)
.requiredHeightIn(min = TangemTheme.dimens.size32),
)
LaunchedEffect(key1 = Unit) {
this.coroutineContext.job.invokeOnCompletion {
requester.requestFocus()
}
}
AmountSecondary(sendField, appCurrencyCode)
}
@Composable
private fun AmountSecondary(sendField: SendTextField.AmountField, appCurrencyCode: String) {
val secondaryAmount = if (sendField.isFiatValue) sendField.cryptoAmount else sendField.fiatAmount
Box(
modifier = Modifier
.padding(
@ -66,10 +84,18 @@ internal fun AmountField(sendField: SendTextField.AmountField, isEnabled: Boolea
end = TangemTheme.dimens.spacing12,
),
) {
val text = if (sendField.isFiatUnavailable) {
BigDecimalFormatter.EMPTY_BALANCE_SIGN
val text = if (sendField.isFiatValue) {
BigDecimalFormatter.formatCryptoAmount(
cryptoAmount = secondaryAmount.value,
cryptoCurrency = secondaryAmount.currencySymbol,
decimals = secondaryAmount.decimals,
)
} else {
"${secondaryValue.ifEmpty { decimalFormat.defaultFormat() }} ${secondaryAmount.currencySymbol}"
BigDecimalFormatter.formatFiatAmount(
fiatAmount = secondaryAmount.value,
fiatCurrencySymbol = secondaryAmount.currencySymbol,
fiatCurrencyCode = appCurrencyCode,
)
}
Text(
text = text,

View file

@ -63,6 +63,7 @@ internal fun LazyListScope.amountField(
AmountField(
sendField = amountState.amountTextField,
isEnabled = !amountState.isFeeLoading,
appCurrencyCode = amountState.appCurrencyCode,
)
}
}

View file

@ -17,6 +17,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.tangem.core.ui.components.ResizableText
import com.tangem.core.ui.components.currency.tokenicon.TokenIcon
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.features.send.impl.presentation.state.SendStates
import com.tangem.features.send.impl.presentation.state.previewdata.AmountStatePreviewData
@ -29,8 +30,16 @@ internal fun AmountBlock(
) {
val amount = amountState.amountTextField
val cryptoAmount = getAmountWithSymbol(amount.value, amount.cryptoAmount.currencySymbol)
val fiatAmount = getAmountWithSymbol(amount.fiatValue, amount.fiatAmount.currencySymbol)
val cryptoAmount = BigDecimalFormatter.formatCryptoAmount(
cryptoAmount = amount.cryptoAmount.value,
cryptoCurrency = amount.cryptoAmount.currencySymbol,
decimals = amount.cryptoAmount.decimals,
)
val fiatAmount = BigDecimalFormatter.formatFiatAmount(
fiatAmount = amount.fiatAmount.value,
fiatCurrencySymbol = amount.fiatAmount.currencySymbol,
fiatCurrencyCode = amountState.appCurrencyCode,
)
val backgroundColor = if (isEditingDisabled) {
TangemTheme.colors.button.disabled
} else {
@ -77,10 +86,6 @@ internal fun AmountBlock(
}
}
private fun getAmountWithSymbol(amount: String, symbol: String): String {
return "$amount $symbol"
}
// region Preview
@Preview
@Composable