Updated on 2026-08-14

This commit is contained in:
Tangem 2024-07-10 18:49:35 +05:00
parent bc414f1e4a
commit e33edbdf2d
8 changed files with 203 additions and 8 deletions

View file

@ -8,6 +8,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.StateRouter
import com.tangem.features.send.impl.presentation.state.fee.custom.BitcoinCustomFeeConverter
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter
import com.tangem.features.send.impl.presentation.state.fee.custom.KaspaCustomFeeConverter
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
@ -37,6 +38,15 @@ internal class FeeConverter(
)
}
private val kaspaCustomFeeConverter by lazy(LazyThreadSafetyMode.NONE) {
KaspaCustomFeeConverter(
clickIntents = clickIntents,
stateRouterProvider = stateRouterProvider,
appCurrencyProvider = appCurrencyProvider,
feeCryptoCurrencyStatusProvider = feeCryptoCurrencyStatusProvider,
)
}
override fun convert(value: FeeSelectorState.Content): Fee {
return when (val fees = value.fees) {
is TransactionFee.Choosable -> {
@ -47,7 +57,12 @@ internal class FeeConverter(
FeeType.Custom -> convertCustom(value, fees)
}
}
is TransactionFee.Single -> fees.normal
is TransactionFee.Single ->
when (value.selectedFee) {
FeeType.Market -> fees.normal
FeeType.Custom -> convertCustom(value, fees)
else -> fees.normal
}
}
}
@ -60,6 +75,7 @@ internal class FeeConverter(
when (normalFee) {
is Fee.Ethereum -> ethereumCustomFeeConverter.convertBack(normalFee = normalFee, value = customValues)
is Fee.Bitcoin -> bitcoinCustomFeeConverter.convertBack(normalFee = normalFee, value = customValues)
is Fee.Kaspa -> kaspaCustomFeeConverter.convertBack(normalFee = normalFee, value = customValues)
else -> {
val customFee = customValues.firstOrNull()
Fee.Common(

View file

@ -6,6 +6,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.StateRouter
import com.tangem.features.send.impl.presentation.state.fee.custom.BitcoinCustomFeeConverter
import com.tangem.features.send.impl.presentation.state.fee.custom.EthereumCustomFeeConverter
import com.tangem.features.send.impl.presentation.state.fee.custom.KaspaCustomFeeConverter
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
import com.tangem.utils.Provider
@ -38,10 +39,20 @@ internal class SendFeeCustomFieldConverter(
)
}
private val kaspaCustomFeeConverter by lazy(LazyThreadSafetyMode.NONE) {
KaspaCustomFeeConverter(
clickIntents = clickIntents,
stateRouterProvider = stateRouterProvider,
appCurrencyProvider = appCurrencyProvider,
feeCryptoCurrencyStatusProvider = feeCryptoCurrencyStatusProvider,
)
}
override fun convert(value: Fee): ImmutableList<SendTextField.CustomFee> {
return when (value) {
is Fee.Ethereum -> ethereumCustomFeeConverter.convert(value)
is Fee.Bitcoin -> bitcoinCustomFeeConverter.convert(value)
is Fee.Kaspa -> kaspaCustomFeeConverter.convert(value)
else -> persistentListOf()
}
}
@ -59,6 +70,12 @@ internal class SendFeeCustomFieldConverter(
value = value,
txSize = fee.txSize,
)
is Fee.Kaspa -> kaspaCustomFeeConverter.onValueChange(
customValues = feeSelectorState.customValues,
index = index,
value = value,
utxoCount = fee.utxoCount,
)
else -> feeSelectorState.customValues
},
)

View file

@ -43,7 +43,7 @@ internal class EthereumCustomFeeConverter(
keyboardType = KeyboardType.Number,
),
title = resourceReference(R.string.send_max_fee),
footer = resourceReference(R.string.send_evm_custom_fee_footer),
footer = resourceReference(R.string.send_custom_amount_fee_footer),
label = getFiatReference(
rate = feeCurrency?.fiatRate,
value = feeValue,

View file

@ -0,0 +1,153 @@
package com.tangem.features.send.impl.presentation.state.fee.custom
import androidx.compose.foundation.text.KeyboardActions
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.common.ui.amountScreen.utils.getFiatReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.state.StateRouter
import com.tangem.features.send.impl.presentation.state.fee.checkExceedBalance
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 kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import java.math.RoundingMode
internal class KaspaCustomFeeConverter(
private val clickIntents: SendClickIntents,
private val stateRouterProvider: Provider<StateRouter>,
private val appCurrencyProvider: Provider<AppCurrency>,
private val feeCryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
) : CustomFeeConverter<Fee.Kaspa> {
override fun convert(value: Fee.Kaspa): ImmutableList<SendTextField.CustomFee> {
val feeValue = value.amount.value
val feeCurrency = feeCryptoCurrencyStatusProvider()?.value
val network = feeCryptoCurrencyStatusProvider()?.currency?.network?.id?.value
return if (network != null) {
persistentListOf(
SendTextField.CustomFee(
value = feeValue?.parseBigDecimal(value.amount.decimals).orEmpty(),
decimals = value.amount.decimals,
symbol = value.amount.currencySymbol,
onValueChange = { clickIntents.onCustomFeeValueChange(FEE_AMOUNT_INDEX, it) },
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.Next,
keyboardType = KeyboardType.Number,
),
title = resourceReference(R.string.send_max_fee),
footer = resourceReference(R.string.send_custom_amount_fee_footer),
label = getFiatReference(
rate = feeCurrency?.fiatRate,
value = feeValue,
appCurrency = appCurrencyProvider(),
),
keyboardActions = KeyboardActions(),
),
SendTextField.CustomFee(
value = value.valuePerUtxo.parseBigDecimal(value.amount.decimals),
decimals = value.amount.decimals,
symbol = "",
title = resourceReference(R.string.send_custom_kaspa_per_utxo_title),
footer = resourceReference(R.string.send_custom_kaspa_per_utxo_footer),
onValueChange = { clickIntents.onCustomFeeValueChange(FEE_VALUE_PER_UTXO_INDEX, it) },
keyboardOptions = KeyboardOptions(
imeAction = if (checkExceedBalance(
feeBalance = feeCurrency?.amount,
feeAmount = feeValue,
)
) {
ImeAction.None
} else {
ImeAction.Done
},
keyboardType = KeyboardType.Number,
),
keyboardActions = KeyboardActions(
onDone = { clickIntents.onNextClick(stateRouterProvider().isEditState) },
),
),
)
} else {
persistentListOf()
}
}
override fun convertBack(normalFee: Fee.Kaspa, value: ImmutableList<SendTextField.CustomFee>): Fee.Kaspa {
val feeAmount = value[FEE_AMOUNT_INDEX].value.parseToBigDecimal(value[FEE_AMOUNT_INDEX].decimals)
val valuePerUtxo =
value[FEE_VALUE_PER_UTXO_INDEX].value.parseToBigDecimal(value[FEE_VALUE_PER_UTXO_INDEX].decimals)
return normalFee.copy(
amount = normalFee.amount.copy(value = feeAmount),
valuePerUtxo = valuePerUtxo,
)
}
fun onValueChange(
customValues: ImmutableList<SendTextField.CustomFee>,
index: Int,
value: String,
utxoCount: Int,
): ImmutableList<SendTextField.CustomFee> {
val mutableCustomValues = customValues.toMutableList()
return mutableCustomValues.apply {
when (index) {
FEE_AMOUNT_INDEX -> {
val valueDecimal = value.parseToBigDecimal(this[FEE_AMOUNT_INDEX].decimals)
val newValuePerUtxo = valueDecimal.divide(
/* divisor = */ utxoCount.toBigDecimal(),
/* scale = */ this[FEE_VALUE_PER_UTXO_INDEX].decimals,
/* roundingMode = */ RoundingMode.HALF_UP,
)
set(
FEE_VALUE_PER_UTXO_INDEX,
this[FEE_VALUE_PER_UTXO_INDEX].copy(
value = newValuePerUtxo.parseBigDecimal(this[FEE_VALUE_PER_UTXO_INDEX].decimals),
),
)
set(
index,
this[index].copy(
value = value,
label = getFiatReference(
rate = feeCryptoCurrencyStatusProvider()?.value?.fiatRate,
value = valueDecimal,
appCurrency = appCurrencyProvider(),
),
),
)
}
FEE_VALUE_PER_UTXO_INDEX -> {
val valuePerUtxo = value.parseToBigDecimal(this[FEE_VALUE_PER_UTXO_INDEX].decimals)
val newFeeAmount = valuePerUtxo.multiply(utxoCount.toBigDecimal())
set(
FEE_AMOUNT_INDEX,
this[FEE_AMOUNT_INDEX].copy(
value = newFeeAmount.parseBigDecimal(this[FEE_AMOUNT_INDEX].decimals),
label = getFiatReference(
rate = feeCryptoCurrencyStatusProvider()?.value?.fiatRate,
value = newFeeAmount,
appCurrency = appCurrencyProvider(),
),
),
)
set(index, this[index].copy(value = value))
}
}
}.toImmutableList()
}
private companion object {
private const val FEE_AMOUNT_INDEX = 0
private const val FEE_VALUE_PER_UTXO_INDEX = 1
}
}

View file

@ -36,9 +36,8 @@ internal fun SendSpeedSelectorItem(
val content = feeSelectorState as? FeeSelectorState.Content
val amount = content?.getAmount(feeType)
val (showDivider, isVisible) = content.getDividerAndVisibility(feeType)
val hasCustomValues = !content?.customValues.isNullOrEmpty()
AnimatedVisibility(
visible = isVisible || hasCustomValues,
visible = isVisible,
label = "Fee Selector Visibility Animation",
enter = expandVertically().plus(fadeIn()),
exit = shrinkVertically().plus(fadeOut()),
@ -135,7 +134,7 @@ private fun FeeSelectorState.Content?.getDividerAndVisibility(feeType: FeeType):
val isNotSingle = this?.fees !is TransactionFee.Single
return when (feeType) {
FeeType.Slow -> true to isNotSingle
FeeType.Market -> isNotSingle to true
FeeType.Market -> (isNotSingle || hasCustomValues) to true
FeeType.Fast -> hasCustomValues to isNotSingle
FeeType.Custom -> false to hasCustomValues
}