Updated on 2026-08-14
This commit is contained in:
parent
b14caba3cf
commit
a2d1834687
17 changed files with 285 additions and 299 deletions
|
|
@ -22,6 +22,24 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.utils.*
|
||||
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].
|
||||
*
|
||||
* @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 keyboardOptions keyboard options
|
||||
*
|
||||
* @see [SimpleTextField] for standard text field
|
||||
*/
|
||||
@Composable
|
||||
fun AmountTextField(
|
||||
value: String,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
import com.tangem.domain.wallets.usecase.ValidateWalletMemoUseCase
|
||||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.domain.AvailableWallet
|
||||
import com.tangem.features.send.impl.presentation.state.amount.SendAmountCurrencyConverter
|
||||
import com.tangem.features.send.impl.presentation.state.amount.SendAmountStateConverter
|
||||
import com.tangem.features.send.impl.presentation.state.fee.SendFeeStateConverter
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldChangeConverter
|
||||
|
|
@ -39,8 +40,25 @@ internal class SendStateFactory(
|
|||
) {
|
||||
|
||||
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
|
||||
private val amountFieldConverter by lazy { SendAmountFieldConverter(clickIntents) }
|
||||
private val amountFieldChangeConverter by lazy { SendAmountFieldChangeConverter(currentStateProvider) }
|
||||
private val amountFieldConverter by lazy {
|
||||
SendAmountFieldConverter(
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
)
|
||||
}
|
||||
private val amountFieldChangeConverter by lazy {
|
||||
SendAmountFieldChangeConverter(
|
||||
currentStateProvider = currentStateProvider,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
)
|
||||
}
|
||||
private val amountCurrencyConverter by lazy {
|
||||
SendAmountCurrencyConverter(
|
||||
currentStateProvider = currentStateProvider,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
)
|
||||
}
|
||||
|
||||
private val amountStateConverter by lazy {
|
||||
SendAmountStateConverter(
|
||||
|
|
@ -57,11 +75,7 @@ internal class SendStateFactory(
|
|||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
)
|
||||
}
|
||||
private val feeStateConverter by lazy {
|
||||
SendFeeStateConverter(
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
)
|
||||
}
|
||||
private val feeStateConverter by lazy { SendFeeStateConverter() }
|
||||
|
||||
private val recipientListStateConverter by lazy {
|
||||
SendRecipientListConverter(
|
||||
|
|
@ -90,16 +104,7 @@ internal class SendStateFactory(
|
|||
//region amount state clicks
|
||||
fun getOnAmountValueChange(value: String) = amountFieldChangeConverter.convert(value)
|
||||
|
||||
fun getOnCurrencyChangedState(isFiat: Boolean): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val amountState = state.amountState ?: return state
|
||||
|
||||
return if (amountState.isFiatValue == isFiat) {
|
||||
state
|
||||
} else {
|
||||
return state.copy(amountState = amountState.copy(isFiatValue = isFiat))
|
||||
}
|
||||
}
|
||||
fun getOnCurrencyChangedState(isFiat: Boolean) = amountCurrencyConverter.convert(isFiat)
|
||||
//endregion
|
||||
|
||||
//region recipient
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ import androidx.paging.PagingData
|
|||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.core.ui.components.currency.tokenicon.TokenIconState
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent
|
||||
import com.tangem.features.send.impl.presentation.state.amount.SendAmountSegmentedButtonsConfig
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
|
||||
|
|
@ -46,15 +45,11 @@ internal sealed class SendStates {
|
|||
data class AmountState(
|
||||
override val type: SendUiStateType = SendUiStateType.Amount,
|
||||
override val isPrimaryButtonEnabled: Boolean,
|
||||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val appCurrency: AppCurrency,
|
||||
val walletName: String,
|
||||
val walletBalance: String,
|
||||
val walletBalance: TextReference,
|
||||
val tokenIconState: TokenIconState,
|
||||
val isFiatValue: Boolean,
|
||||
val segmentedButtonConfig: PersistentList<SendAmountSegmentedButtonsConfig>,
|
||||
val amountTextField: SendTextField.Amount,
|
||||
val amountValue: BigDecimal,
|
||||
val amountTextField: SendTextField.AmountField,
|
||||
) : SendStates()
|
||||
|
||||
/** Recipient state */
|
||||
|
|
@ -72,14 +67,13 @@ internal sealed class SendStates {
|
|||
data class FeeState(
|
||||
override val type: SendUiStateType = SendUiStateType.Fee,
|
||||
override val isPrimaryButtonEnabled: Boolean = false,
|
||||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val feeSelectorState: FeeSelectorState = FeeSelectorState.Loading,
|
||||
val isSubtract: Boolean = false,
|
||||
val isUserSubtracted: Boolean = false,
|
||||
val fee: Fee? = null,
|
||||
val receivedAmountValue: BigDecimal = BigDecimal.ZERO,
|
||||
val receivedAmount: String = "",
|
||||
val notifications: ImmutableList<SendFeeNotification> = persistentListOf(),
|
||||
val feeSelectorState: FeeSelectorState,
|
||||
val isSubtract: Boolean,
|
||||
val isUserSubtracted: Boolean,
|
||||
val fee: Fee?,
|
||||
val receivedAmountValue: BigDecimal,
|
||||
val receivedAmount: String,
|
||||
val notifications: ImmutableList<SendFeeNotification>,
|
||||
) : SendStates()
|
||||
|
||||
/** Send state */
|
||||
|
|
|
|||
|
|
@ -1,18 +1,20 @@
|
|||
package com.tangem.features.send.impl.presentation.state.amount
|
||||
|
||||
import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter.formatCryptoAmount
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter.formatFiatAmount
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.state.SendStates
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldConverter
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class SendAmountStateConverter(
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
|
|
@ -30,15 +32,11 @@ internal class SendAmountStateConverter(
|
|||
val crypto = formatCryptoAmount(status.value.amount, status.currency.symbol, status.currency.decimals)
|
||||
|
||||
return SendStates.AmountState(
|
||||
appCurrency = appCurrency,
|
||||
cryptoCurrencyStatus = status,
|
||||
walletName = userWallet.name,
|
||||
walletBalance = "$crypto ($fiat)",
|
||||
walletBalance = resourceReference(R.string.send_wallet_balance_format, wrappedList(crypto, fiat)),
|
||||
tokenIconState = iconStateConverter.convert(status),
|
||||
amountTextField = sendAmountFieldConverter.convert(Unit),
|
||||
isFiatValue = false,
|
||||
isPrimaryButtonEnabled = false,
|
||||
amountValue = BigDecimal.ZERO,
|
||||
segmentedButtonConfig = persistentListOf(
|
||||
SendAmountSegmentedButtonsConfig(
|
||||
title = stringReference(status.currency.symbol),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.fee
|
|||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.blockchain.extensions.toBigDecimalOrDefault
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -9,7 +10,7 @@ import java.math.BigDecimal
|
|||
* Calculate receiving amount when fee is subtracted from sending amount
|
||||
*/
|
||||
internal fun calculateReceiveAmount(state: SendUiState, feeAmount: Fee): BigDecimal {
|
||||
val amountValue = state.amountState?.amountValue ?: BigDecimal.ZERO
|
||||
val amountValue = state.amountState?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO
|
||||
val fee = feeAmount.amount.value ?: return BigDecimal.ZERO
|
||||
return amountValue.minus(fee)
|
||||
}
|
||||
|
|
@ -25,8 +26,7 @@ internal fun FeeSelectorState.Content.getFee(): Fee {
|
|||
FeeType.MARKET -> fees.normal
|
||||
FeeType.FAST -> fees.priority
|
||||
FeeType.CUSTOM -> {
|
||||
val feeAmount =
|
||||
customValues.firstOrNull()?.value?.let { BigDecimal(it.ifEmpty { "0" }) } ?: BigDecimal.ZERO
|
||||
val feeAmount = customValues.firstOrNull()?.value.toBigDecimalOrDefault()
|
||||
Fee.Common(
|
||||
fees.normal.amount.copy(
|
||||
value = feeAmount,
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ internal class FeeStateFactory(
|
|||
feeState = updatedState.copy(
|
||||
notifications = feeNotificationFactory(
|
||||
feeState = updatedState,
|
||||
amountValue = state.amountState?.amountValue,
|
||||
amountValue = state.amountState?.amountTextField?.cryptoAmount?.value,
|
||||
),
|
||||
isPrimaryButtonEnabled = updatedState.isPrimaryButtonEnabled(),
|
||||
),
|
||||
|
|
@ -92,7 +92,7 @@ internal class FeeStateFactory(
|
|||
feeState = updatedState?.copy(
|
||||
notifications = feeNotificationFactory(
|
||||
feeState = updatedState,
|
||||
amountValue = state.amountState?.amountValue,
|
||||
amountValue = state.amountState?.amountTextField?.cryptoAmount?.value,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
@ -119,7 +119,7 @@ internal class FeeStateFactory(
|
|||
feeState = updatedState.copy(
|
||||
notifications = feeNotificationFactory(
|
||||
feeState = updatedState,
|
||||
amountValue = state.amountState?.amountValue,
|
||||
amountValue = state.amountState?.amountTextField?.cryptoAmount?.value,
|
||||
),
|
||||
isPrimaryButtonEnabled = updatedState.isPrimaryButtonEnabled(),
|
||||
),
|
||||
|
|
@ -150,7 +150,7 @@ internal class FeeStateFactory(
|
|||
feeState = updatedState.copy(
|
||||
notifications = feeNotificationFactory(
|
||||
feeState = updatedState,
|
||||
amountValue = state.amountState?.amountValue,
|
||||
amountValue = state.amountState?.amountTextField?.cryptoAmount?.value,
|
||||
),
|
||||
isPrimaryButtonEnabled = updatedState.isPrimaryButtonEnabled(),
|
||||
),
|
||||
|
|
@ -174,7 +174,7 @@ internal class FeeStateFactory(
|
|||
feeState = updatedState.copy(
|
||||
notifications = feeNotificationFactory(
|
||||
feeState = updatedState,
|
||||
amountValue = state.amountState?.amountValue,
|
||||
amountValue = state.amountState?.amountTextField?.cryptoAmount?.value,
|
||||
),
|
||||
isPrimaryButtonEnabled = updatedState.isPrimaryButtonEnabled(),
|
||||
),
|
||||
|
|
@ -203,7 +203,7 @@ internal class FeeStateFactory(
|
|||
|
||||
private fun checkAutoSubtract(state: SendUiState, fee: Fee, balance: BigDecimal): Boolean {
|
||||
val feeState = state.feeState ?: return false
|
||||
val amountValue = state.amountState?.amountValue ?: BigDecimal.ZERO
|
||||
val amountValue = state.amountState?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO
|
||||
val feeAmount = fee.amount.value ?: BigDecimal.ZERO
|
||||
return if (feeState.isUserSubtracted) {
|
||||
feeState.isSubtract
|
||||
|
|
|
|||
|
|
@ -4,13 +4,16 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.toFormattedString
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
@ -31,16 +34,24 @@ internal class SendFeeCustomFieldConverter(
|
|||
|
||||
return persistentListOf(
|
||||
SendTextField.CustomFee(
|
||||
value = ethereumFee.amount.value.toString(),
|
||||
value = ethereumFee.amount.value?.toFormattedString(ethereumFee.amount.decimals).orEmpty(),
|
||||
decimals = ethereumFee.amount.decimals,
|
||||
symbol = ethereumFee.amount.currencySymbol,
|
||||
onValueChange = { clickIntents.onCustomFeeValueChange(0, it) },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Next,
|
||||
keyboardType = KeyboardType.Number,
|
||||
),
|
||||
label = TextReference.Str(maxFeeFiat),
|
||||
title = resourceReference(R.string.send_max_fee),
|
||||
footer = resourceReference(R.string.send_max_fee_footer),
|
||||
label = stringReference(maxFeeFiat),
|
||||
),
|
||||
SendTextField.CustomFee(
|
||||
value = ethereumFee.gasPrice.toString(),
|
||||
decimals = 0,
|
||||
symbol = ETHEREUM_UNIT,
|
||||
title = resourceReference(R.string.send_gas_price),
|
||||
footer = resourceReference(R.string.send_gas_price_footer),
|
||||
onValueChange = { clickIntents.onCustomFeeValueChange(1, it) },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Next,
|
||||
|
|
@ -49,6 +60,10 @@ internal class SendFeeCustomFieldConverter(
|
|||
),
|
||||
SendTextField.CustomFee(
|
||||
value = ethereumFee.gasLimit.toString(),
|
||||
decimals = 0,
|
||||
symbol = null,
|
||||
title = resourceReference(R.string.send_gas_limit),
|
||||
footer = resourceReference(R.string.send_gas_limit_footer),
|
||||
onValueChange = { clickIntents.onCustomFeeValueChange(2, it) },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done,
|
||||
|
|
@ -57,4 +72,8 @@ internal class SendFeeCustomFieldConverter(
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ETHEREUM_UNIT = "GWEI"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +1,21 @@
|
|||
package com.tangem.features.send.impl.presentation.state.fee
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendStates
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class SendFeeStateConverter(
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
) : Converter<Unit, SendStates.FeeState> {
|
||||
internal class SendFeeStateConverter : Converter<Unit, SendStates.FeeState> {
|
||||
|
||||
override fun convert(value: Unit): SendStates.FeeState {
|
||||
return SendStates.FeeState(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatusProvider(),
|
||||
feeSelectorState = FeeSelectorState.Loading,
|
||||
isSubtract = false,
|
||||
isUserSubtracted = false,
|
||||
fee = null,
|
||||
receivedAmountValue = BigDecimal.ZERO,
|
||||
receivedAmount = "",
|
||||
notifications = persistentListOf(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +1,49 @@
|
|||
package com.tangem.features.send.impl.presentation.state.fields
|
||||
|
||||
import com.tangem.blockchain.extensions.toBigDecimalOrDefault
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.features.send.impl.presentation.state.SendStates
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
import java.text.NumberFormat
|
||||
import java.math.RoundingMode
|
||||
|
||||
internal class SendAmountFieldChangeConverter(
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
) : Converter<String, SendUiState> {
|
||||
override fun convert(value: String): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val amountState = state.amountState ?: return state
|
||||
val amountTextField = amountState.amountTextField
|
||||
val feeState = state.feeState ?: return state
|
||||
|
||||
if (value.checkDecimalSeparatorDuplicate()) return state
|
||||
if (value.isEmpty()) return state.emptyState()
|
||||
|
||||
val fiatRate = amountState.cryptoCurrencyStatus.value.fiatRate
|
||||
val cryptoDecimals = amountTextField.cryptoAmount.decimals
|
||||
val fiatDecimals = amountTextField.fiatAmount.decimals
|
||||
|
||||
val trimmedValue = value.trim()
|
||||
val cryptoValue = if (amountState.isFiatValue) {
|
||||
if (value.isNotBlank()) {
|
||||
trimmedValue.toBigDecimal().divide(fiatRate)?.stripTrailingZeros()?.toPlainString().orEmpty()
|
||||
} else {
|
||||
DEFAULT_VALUE
|
||||
}
|
||||
} else {
|
||||
trimmedValue
|
||||
}
|
||||
val cryptoValue = trimmedValue.getCryptoValue(amountTextField.isFiatValue, cryptoDecimals)
|
||||
val fiatValue = trimmedValue.getFiatValue(amountTextField.isFiatValue, fiatDecimals)
|
||||
val decimalCryptoValue = cryptoValue.parseToBigDecimal(cryptoDecimals)
|
||||
val decimalFiatValue = fiatValue.parseToBigDecimal(fiatDecimals)
|
||||
|
||||
val fiatValue = if (!amountState.isFiatValue) {
|
||||
if (value.isNotBlank()) {
|
||||
trimmedValue.toBigDecimal().multiply(fiatRate)?.stripTrailingZeros()?.toPlainString().orEmpty()
|
||||
} else {
|
||||
DEFAULT_VALUE
|
||||
}
|
||||
} else {
|
||||
trimmedValue
|
||||
}
|
||||
|
||||
val checkValue = if (amountState.isFiatValue) fiatValue else cryptoValue
|
||||
val isExceedBalance = checkValue.checkExceedBalance(amountState)
|
||||
val isMaxAmount = checkValue.checkMaxAmount(amountState)
|
||||
val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue
|
||||
val isExceedBalance = checkValue.checkExceedBalance(amountTextField)
|
||||
val isMaxAmount = checkValue.checkMaxAmount(amountTextField)
|
||||
return state.copy(
|
||||
amountState = amountState.copy(
|
||||
isPrimaryButtonEnabled = !isExceedBalance,
|
||||
amountTextField = amountState.amountTextField.copy(
|
||||
amountTextField = amountTextField.copy(
|
||||
value = cryptoValue,
|
||||
fiatValue = fiatValue,
|
||||
isError = isExceedBalance,
|
||||
cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue),
|
||||
fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue),
|
||||
),
|
||||
amountValue = cryptoValue.toBigDecimalOrDefault(),
|
||||
),
|
||||
feeState = feeState.copy(
|
||||
isSubtract = isMaxAmount,
|
||||
|
|
@ -61,58 +51,63 @@ internal class SendAmountFieldChangeConverter(
|
|||
)
|
||||
}
|
||||
|
||||
private fun String.getCryptoValue(isFiatValue: Boolean, decimals: Int): String {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val fiatRate = cryptoCurrencyStatus.value.fiatRate
|
||||
return if (isFiatValue && fiatRate != null) {
|
||||
parseToBigDecimal(decimals).divide(fiatRate, decimals, RoundingMode.DOWN)
|
||||
.parseBigDecimal(decimals)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.getFiatValue(isFiatValue: Boolean, decimals: Int): String {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val fiatRate = cryptoCurrencyStatus.value.fiatRate
|
||||
return if (!isFiatValue && fiatRate != null) {
|
||||
parseToBigDecimal(decimals).multiply(fiatRate).parseBigDecimal(decimals)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
private fun SendUiState.emptyState(): SendUiState {
|
||||
return copy(
|
||||
amountState = amountState?.copy(
|
||||
isPrimaryButtonEnabled = false,
|
||||
amountTextField = amountState.amountTextField.copy(
|
||||
value = if (!amountState.isFiatValue) "" else DEFAULT_VALUE,
|
||||
fiatValue = if (amountState.isFiatValue) "" else DEFAULT_VALUE,
|
||||
value = "",
|
||||
fiatValue = "",
|
||||
isError = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun String.checkDecimalSeparatorDuplicate(): Boolean {
|
||||
val regex = TRIM_REGEX.toRegex()
|
||||
val decimalSeparatorCount = regex.findAll(this).count()
|
||||
|
||||
return decimalSeparatorCount > 1
|
||||
}
|
||||
|
||||
private fun String.checkExceedBalance(state: SendStates.AmountState): Boolean {
|
||||
val currencyCryptoAmount = state.cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
|
||||
val currencyFiatAmount = state.cryptoCurrencyStatus.value.fiatAmount ?: BigDecimal.ZERO
|
||||
return if (state.isFiatValue) {
|
||||
toBigDecimal() > currencyFiatAmount
|
||||
private fun String.checkExceedBalance(amountTextField: SendTextField.AmountField): Boolean {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val currencyCryptoAmount = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
|
||||
val currencyFiatAmount = cryptoCurrencyStatus.value.fiatAmount ?: BigDecimal.ZERO
|
||||
return if (amountTextField.isFiatValue) {
|
||||
parseToBigDecimal(amountTextField.fiatAmount.decimals) > currencyFiatAmount
|
||||
} else {
|
||||
toBigDecimal() > currencyCryptoAmount
|
||||
parseToBigDecimal(amountTextField.cryptoAmount.decimals) > currencyCryptoAmount
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.checkMaxAmount(state: SendStates.AmountState): Boolean {
|
||||
private fun String.checkMaxAmount(amountTextField: SendTextField.AmountField): Boolean {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
|
||||
// If current currency is Token
|
||||
if (state.cryptoCurrencyStatus.currency is CryptoCurrency.Token) return false
|
||||
if (cryptoCurrencyStatus.currency is CryptoCurrency.Token) return false
|
||||
|
||||
val currencyCryptoAmount = state.cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
|
||||
val currencyFiatAmount = state.cryptoCurrencyStatus.value.fiatAmount ?: BigDecimal.ZERO
|
||||
return if (state.isFiatValue) {
|
||||
toBigDecimal() == currencyFiatAmount
|
||||
val currencyCryptoAmount = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
|
||||
val currencyFiatAmount = cryptoCurrencyStatus.value.fiatAmount ?: BigDecimal.ZERO
|
||||
return if (amountTextField.isFiatValue) {
|
||||
parseToBigDecimal(amountTextField.fiatAmount.decimals) == currencyFiatAmount
|
||||
} else {
|
||||
toBigDecimal() == currencyCryptoAmount
|
||||
parseToBigDecimal(amountTextField.cryptoAmount.decimals) == currencyCryptoAmount
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.trim(): String {
|
||||
var trimmedValue = this
|
||||
if (length > 1 && firstOrNull() == '0' && get(1).isDigit()) trimmedValue = drop(1)
|
||||
|
||||
return trimmedValue.replace(TRIM_REGEX.toRegex(), ".")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_VALUE = NumberFormat.getInstance().format(0.00)
|
||||
private const val TRIM_REGEX = "[.,]"
|
||||
}
|
||||
}
|
||||
|
|
@ -4,31 +4,47 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.Amount
|
||||
import com.tangem.domain.tokens.model.AmountType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.convertToAmount
|
||||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.text.NumberFormat
|
||||
import java.math.BigDecimal
|
||||
|
||||
private const val FIAT_DECIMALS = 2
|
||||
|
||||
internal class SendAmountFieldConverter(
|
||||
private val clickIntents: SendClickIntents,
|
||||
) : Converter<Unit, SendTextField.Amount> {
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
) : Converter<Unit, SendTextField.AmountField> {
|
||||
|
||||
override fun convert(value: Unit): SendTextField.Amount {
|
||||
return SendTextField.Amount(
|
||||
override fun convert(value: Unit): SendTextField.AmountField {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
return SendTextField.AmountField(
|
||||
value = "",
|
||||
fiatValue = DEFAULT_VALUE,
|
||||
fiatValue = "",
|
||||
onValueChange = clickIntents::onAmountValueChange,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Next,
|
||||
keyboardType = KeyboardType.Number,
|
||||
),
|
||||
placeholder = TextReference.Str(DEFAULT_VALUE),
|
||||
isFiatValue = false,
|
||||
cryptoAmount = BigDecimal.ZERO.convertToAmount(cryptoCurrencyStatus.currency),
|
||||
fiatAmount = getAppCurrencyAmount(appCurrencyProvider()),
|
||||
isError = false,
|
||||
error = TextReference.Res(R.string.swapping_insufficient_funds),
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_VALUE = NumberFormat.getInstance().format(0.00)
|
||||
}
|
||||
private fun getAppCurrencyAmount(appCurrency: AppCurrency) = Amount(
|
||||
currencySymbol = appCurrency.symbol,
|
||||
value = BigDecimal.ZERO,
|
||||
decimals = FIAT_DECIMALS,
|
||||
type = AmountType.FiatType(appCurrency.code),
|
||||
)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.send.impl.presentation.state.fields
|
|||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.tokens.model.Amount
|
||||
|
||||
@Immutable
|
||||
internal sealed class SendTextField {
|
||||
|
|
@ -16,14 +17,13 @@ internal sealed class SendTextField {
|
|||
/** Keyboard options */
|
||||
abstract val keyboardOptions: KeyboardOptions
|
||||
|
||||
// /** Placeholder (hint) */
|
||||
// abstract val placeholder: TextReference
|
||||
|
||||
data class Amount(
|
||||
data class AmountField(
|
||||
override val value: String,
|
||||
override val onValueChange: (String) -> Unit,
|
||||
override val keyboardOptions: KeyboardOptions,
|
||||
val placeholder: TextReference,
|
||||
val cryptoAmount: Amount,
|
||||
val fiatAmount: Amount,
|
||||
val isFiatValue: Boolean,
|
||||
val fiatValue: String,
|
||||
val isError: Boolean,
|
||||
val error: TextReference,
|
||||
|
|
@ -53,6 +53,10 @@ internal sealed class SendTextField {
|
|||
override val value: String,
|
||||
override val onValueChange: (String) -> Unit,
|
||||
override val keyboardOptions: KeyboardOptions,
|
||||
val symbol: String?,
|
||||
val decimals: Int,
|
||||
val title: TextReference,
|
||||
val footer: TextReference,
|
||||
val label: TextReference? = null,
|
||||
) : SendTextField()
|
||||
}
|
||||
|
|
@ -3,48 +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.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Alignment.Companion.BottomCenter
|
||||
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||
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 com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation
|
||||
import com.tangem.core.ui.components.fields.AmountTextField
|
||||
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.defaultFormat
|
||||
import com.tangem.core.ui.utils.rememberDecimalFormat
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
|
||||
@Composable
|
||||
internal fun ColumnScope.AmountField(
|
||||
sendField: SendTextField.Amount,
|
||||
cryptoSymbol: String,
|
||||
fiatSymbol: String,
|
||||
isFiat: Boolean,
|
||||
) {
|
||||
val value = if (isFiat) sendField.fiatValue else sendField.value
|
||||
val secondaryValue = if (!isFiat) sendField.fiatValue else sendField.value
|
||||
val symbol = if (isFiat) fiatSymbol else cryptoSymbol
|
||||
val secondarySymbol = if (!isFiat) fiatSymbol else cryptoSymbol
|
||||
internal fun AmountField(sendField: SendTextField.AmountField, isFiat: Boolean) {
|
||||
val decimalFormat = rememberDecimalFormat()
|
||||
val (primaryValue, secondaryValue) = if (isFiat) {
|
||||
sendField.fiatValue to sendField.value
|
||||
} else {
|
||||
sendField.value to sendField.fiatValue
|
||||
}
|
||||
|
||||
AmountFieldInner(
|
||||
value = value,
|
||||
placeholder = sendField.placeholder,
|
||||
symbol = symbol,
|
||||
val (primaryAmount, secondaryAmount) = if (!isFiat) {
|
||||
sendField.cryptoAmount to sendField.fiatAmount
|
||||
} else {
|
||||
sendField.fiatAmount to sendField.cryptoAmount
|
||||
}
|
||||
|
||||
AmountTextField(
|
||||
value = primaryValue,
|
||||
decimals = primaryAmount.decimals,
|
||||
symbol = primaryAmount.currencySymbol,
|
||||
onValueChange = sendField.onValueChange,
|
||||
keyboardOptions = sendField.keyboardOptions,
|
||||
textStyle = TangemTheme.typography.h2.copy(
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
),
|
||||
placeholderAlignment = TopCenter,
|
||||
modifier = Modifier
|
||||
.align(CenterHorizontally)
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing24,
|
||||
start = TangemTheme.dimens.spacing12,
|
||||
|
|
@ -54,15 +55,15 @@ internal fun ColumnScope.AmountField(
|
|||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(CenterHorizontally)
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing8,
|
||||
start = TangemTheme.dimens.spacing12,
|
||||
end = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
) {
|
||||
val text = "${secondaryValue.ifEmpty { decimalFormat.defaultFormat() }} ${secondaryAmount.currencySymbol}"
|
||||
Text(
|
||||
text = "$secondaryValue $secondarySymbol",
|
||||
text = text,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
|
|
@ -80,47 +81,6 @@ internal fun ColumnScope.AmountField(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AmountFieldInner(
|
||||
value: String,
|
||||
placeholder: TextReference,
|
||||
symbol: String,
|
||||
onValueChange: (String) -> Unit,
|
||||
keyboardOptions: KeyboardOptions,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
BasicTextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
modifier = modifier
|
||||
.focusRequester(focusRequester)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
textStyle = TangemTheme.typography.h2.copy(
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
textAlign = TextAlign.Center,
|
||||
),
|
||||
keyboardOptions = keyboardOptions,
|
||||
singleLine = true,
|
||||
visualTransformation = AmountVisualTransformation(symbol),
|
||||
decorationBox = { innerTextField ->
|
||||
Box {
|
||||
if (value.isBlank()) {
|
||||
Text(
|
||||
text = "${placeholder.resolveReference()} $symbol",
|
||||
style = TangemTheme.typography.h2,
|
||||
color = TangemTheme.colors.text.disabled,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopCenter),
|
||||
)
|
||||
}
|
||||
innerTextField()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AmountFieldError(isError: Boolean, error: TextReference, modifier: Modifier = Modifier) {
|
||||
AnimatedVisibility(
|
||||
|
|
|
|||
|
|
@ -12,12 +12,14 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.tangem.core.ui.components.currency.tokenicon.TokenIcon
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.impl.presentation.state.SendStates
|
||||
|
||||
@Composable
|
||||
internal fun AmountFieldContainer(amountState: SendStates.AmountState, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
|
|
@ -33,29 +35,24 @@ internal fun AmountFieldContainer(amountState: SendStates.AmountState, modifier:
|
|||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing14)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
.padding(top = TangemTheme.dimens.spacing14),
|
||||
)
|
||||
Text(
|
||||
text = amountState.walletBalance,
|
||||
text = amountState.walletBalance.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing2)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
.padding(top = TangemTheme.dimens.spacing2),
|
||||
)
|
||||
TokenIcon(
|
||||
state = amountState.tokenIconState,
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing32)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
.padding(top = TangemTheme.dimens.spacing32),
|
||||
)
|
||||
AmountField(
|
||||
sendField = amountState.amountTextField,
|
||||
isFiat = amountState.isFiatValue,
|
||||
cryptoSymbol = amountState.cryptoCurrencyStatus.currency.symbol,
|
||||
fiatSymbol = amountState.appCurrency.symbol,
|
||||
isFiat = amountState.amountTextField.isFiatValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,86 +5,64 @@ import androidx.compose.foundation.layout.Arrangement
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation
|
||||
import com.tangem.core.ui.components.inputrow.InputRowEnter
|
||||
import com.tangem.core.ui.components.inputrow.InputRowEnterInfo
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.components.inputrow.InputRowEnterAmount
|
||||
import com.tangem.core.ui.components.inputrow.InputRowEnterInfoAmount
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeType
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.ui.common.FooterContainer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
private const val ETHEREUM_UNIT = "GWEI"
|
||||
|
||||
@Composable
|
||||
internal fun SendCustomFeeEthereum(
|
||||
customValues: ImmutableList<SendTextField.CustomFee>,
|
||||
selectedFee: FeeType,
|
||||
symbol: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (selectedFee == FeeType.CUSTOM && customValues.isNotEmpty()) {
|
||||
val fee = customValues[0]
|
||||
val gasPrice = customValues[1]
|
||||
val gasLimit = customValues[2]
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
modifier = modifier,
|
||||
) {
|
||||
FooterContainer(
|
||||
footer = stringResource(R.string.send_max_fee_footer),
|
||||
) {
|
||||
InputRowEnterInfo(
|
||||
text = fee.value,
|
||||
title = TextReference.Res(R.string.send_max_fee),
|
||||
info = fee.label,
|
||||
visualTransformation = AmountVisualTransformation(symbol),
|
||||
keyboardOptions = fee.keyboardOptions,
|
||||
onValueChange = fee.onValueChange,
|
||||
isSingleLine = true,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
),
|
||||
)
|
||||
}
|
||||
FooterContainer(
|
||||
footer = stringResource(R.string.send_gas_price_footer),
|
||||
) {
|
||||
InputRowEnter(
|
||||
text = gasPrice.value,
|
||||
title = TextReference.Res(R.string.send_gas_price),
|
||||
onValueChange = gasPrice.onValueChange,
|
||||
visualTransformation = AmountVisualTransformation(ETHEREUM_UNIT),
|
||||
keyboardOptions = fee.keyboardOptions,
|
||||
isSingleLine = true,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
),
|
||||
)
|
||||
}
|
||||
FooterContainer(
|
||||
footer = stringResource(R.string.send_gas_limit_footer),
|
||||
) {
|
||||
InputRowEnter(
|
||||
text = gasLimit.value,
|
||||
title = TextReference.Res(R.string.send_gas_limit),
|
||||
onValueChange = gasLimit.onValueChange,
|
||||
keyboardOptions = fee.keyboardOptions,
|
||||
isSingleLine = true,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
),
|
||||
)
|
||||
repeat(customValues.size) { index ->
|
||||
val value = customValues[index]
|
||||
FooterContainer(
|
||||
footer = value.footer.resolveReference(),
|
||||
) {
|
||||
if (value.label != null) {
|
||||
InputRowEnterInfoAmount(
|
||||
text = value.value,
|
||||
decimals = value.decimals,
|
||||
symbol = value.symbol,
|
||||
title = value.title,
|
||||
info = value.label,
|
||||
keyboardOptions = value.keyboardOptions,
|
||||
onValueChange = value.onValueChange,
|
||||
showDivider = false,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
InputRowEnterAmount(
|
||||
text = value.value,
|
||||
decimals = value.decimals,
|
||||
title = value.title,
|
||||
symbol = value.symbol,
|
||||
onValueChange = value.onValueChange,
|
||||
keyboardOptions = value.keyboardOptions,
|
||||
showDivider = false,
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,10 +43,7 @@ internal fun SendSpeedAndFeeContent(state: SendStates.FeeState?, clickIntents: S
|
|||
clickIntents = clickIntents,
|
||||
)
|
||||
}
|
||||
customFee(
|
||||
feeSendState = feeSendState,
|
||||
cryptoCurrencySymbol = state.cryptoCurrencyStatus.currency.symbol,
|
||||
)
|
||||
customFee(feeSendState)
|
||||
notifications(notifications)
|
||||
subtractButton(
|
||||
receivedAmount = state.receivedAmount,
|
||||
|
|
@ -84,11 +81,7 @@ internal fun LazyListScope.notifications(configs: ImmutableList<SendFeeNotificat
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
internal fun LazyListScope.customFee(
|
||||
feeSendState: FeeSelectorState,
|
||||
cryptoCurrencySymbol: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
internal fun LazyListScope.customFee(feeSendState: FeeSelectorState, modifier: Modifier = Modifier) {
|
||||
item(
|
||||
key = FEE_CUSTOM_KEY,
|
||||
) {
|
||||
|
|
@ -104,7 +97,6 @@ internal fun LazyListScope.customFee(
|
|||
SendCustomFeeEthereum(
|
||||
customValues = customValues,
|
||||
selectedFee = fee.selectedFee,
|
||||
symbol = cryptoCurrencySymbol,
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,16 +17,17 @@ import androidx.compose.ui.text.SpanStyle
|
|||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import com.tangem.blockchain.extensions.toBigDecimalOrDefault
|
||||
import com.tangem.core.ui.components.inputrow.InputRowDefault
|
||||
import com.tangem.core.ui.components.inputrow.InputRowImage
|
||||
import com.tangem.core.ui.components.inputrow.InputRowRecipientDefault
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.components.transactions.TransactionDoneTitle
|
||||
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.BigDecimalFormatter.formatCryptoAmount
|
||||
import com.tangem.domain.tokens.model.AmountType
|
||||
import com.tangem.features.send.impl.R
|
||||
import com.tangem.features.send.impl.presentation.state.SendNotification
|
||||
import com.tangem.features.send.impl.presentation.state.SendStates
|
||||
|
|
@ -61,7 +62,7 @@ internal fun SendContent(uiState: SendUiState) {
|
|||
AnimatedVisibility(visible = !isSuccess) {
|
||||
FromWallet(
|
||||
walletName = amountState.walletName,
|
||||
walletBalance = amountState.walletBalance,
|
||||
walletBalance = amountState.walletBalance.resolveReference(),
|
||||
)
|
||||
}
|
||||
AmountBlock(
|
||||
|
|
@ -122,13 +123,14 @@ private fun AmountBlock(amountState: SendStates.AmountState, isSuccess: Boolean,
|
|||
val amount = amountState.amountTextField
|
||||
|
||||
val cryptoAmount = formatCryptoAmount(
|
||||
cryptoCurrency = amountState.cryptoCurrencyStatus.currency,
|
||||
cryptoAmount = amount.value.toBigDecimalOrDefault(),
|
||||
cryptoAmount = amount.cryptoAmount.value,
|
||||
cryptoCurrency = amount.cryptoAmount.currencySymbol,
|
||||
decimals = amount.cryptoAmount.decimals,
|
||||
)
|
||||
val fiatAmount = BigDecimalFormatter.formatFiatAmount(
|
||||
fiatAmount = amount.fiatValue.toBigDecimalOrDefault(),
|
||||
fiatCurrencyCode = amountState.appCurrency.code,
|
||||
fiatCurrencySymbol = amountState.appCurrency.symbol,
|
||||
fiatAmount = amount.fiatAmount.value,
|
||||
fiatCurrencyCode = (amount.fiatAmount.type as AmountType.FiatType).code,
|
||||
fiatCurrencySymbol = amount.fiatAmount.currencySymbol,
|
||||
)
|
||||
InputRowImage(
|
||||
title = TextReference.Res(R.string.send_amount_label),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import com.tangem.blockchain.blockchains.xrp.XrpAddressService
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.redux.LegacyAction
|
||||
|
|
@ -351,12 +353,15 @@ internal class SendViewModel @Inject constructor(
|
|||
|
||||
override fun onMaxValueClick() {
|
||||
val amountState = uiState.amountState ?: return
|
||||
val amount = if (amountState.isFiatValue) {
|
||||
amountState.cryptoCurrencyStatus.value.fiatAmount
|
||||
val amountTextField = amountState.amountTextField
|
||||
val (amount, decimals) = if (amountTextField.isFiatValue) {
|
||||
cryptoCurrencyStatus.value.fiatAmount to amountTextField.fiatAmount.decimals
|
||||
} else {
|
||||
amountState.cryptoCurrencyStatus.value.amount
|
||||
cryptoCurrencyStatus.value.amount to amountTextField.cryptoAmount.decimals
|
||||
}
|
||||
if (amount != null && !amount.isZero()) {
|
||||
onAmountValueChange(amount.parseBigDecimal(decimals))
|
||||
}
|
||||
onAmountValueChange(amount?.toPlainString() ?: DEFAULT_VALUE)
|
||||
}
|
||||
// endregion
|
||||
|
||||
|
|
@ -451,7 +456,7 @@ internal class SendViewModel @Inject constructor(
|
|||
private suspend fun callFeeUseCase(): Either<GetFeeError, TransactionFee>? {
|
||||
val amountState = uiState.amountState ?: return null
|
||||
val recipientState = uiState.recipientState ?: return null
|
||||
val amount = amountState.amountTextField.value.toBigDecimal()
|
||||
val amount = amountState.amountTextField.cryptoAmount.value ?: return null
|
||||
|
||||
return getFeeUseCase.invoke(
|
||||
amount = amount,
|
||||
|
|
@ -495,7 +500,7 @@ internal class SendViewModel @Inject constructor(
|
|||
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return
|
||||
val memo = uiState.recipientState?.memoTextField?.value
|
||||
val fee = feeSelectorState.getFee()
|
||||
val amountValue = uiState.amountState?.amountValue ?: return
|
||||
val amountValue = uiState.amountState?.amountTextField?.cryptoAmount?.value ?: return
|
||||
|
||||
val amountToSend = if (feeState.isSubtract) feeState.receivedAmountValue else amountValue
|
||||
|
||||
|
|
@ -616,7 +621,6 @@ internal class SendViewModel @Inject constructor(
|
|||
|
||||
companion object {
|
||||
private const val XRP_X_ADDRESS = 'X'
|
||||
private const val DEFAULT_VALUE = "0.00"
|
||||
private const val CHECK_FEE_UPDATE_DELAY = 60_000L
|
||||
private const val BALANCE_UPDATE_DELAY = 10_000L
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue