Updated on 2026-08-14

This commit is contained in:
Tangem 2024-05-29 17:16:59 +03:00
parent 8889ad4c31
commit d2518865d1
39 changed files with 414 additions and 57 deletions

View file

@ -1,10 +1,13 @@
package com.tangem.features.send.impl.presentation.state
import com.tangem.blockchain.common.Blockchain
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.features.send.impl.R
import java.math.BigDecimal
internal sealed class SendNotification(val config: NotificationConfig) {
@ -186,4 +189,46 @@ internal sealed class SendNotification(val config: NotificationConfig) {
),
)
}
sealed interface Koinos {
data class InsufficientRecoverableMana(
val mana: BigDecimal,
val maxMana: BigDecimal,
) : Error(
title = resourceReference(R.string.koinos_insufficient_mana_to_send_koin_title),
subtitle = resourceReference(
R.string.koinos_insufficient_mana_to_send_koin_description,
formatArgs = wrappedList(
BigDecimalFormatter.formatCryptoAmountShorted(mana, "", Blockchain.Koinos.decimals()),
BigDecimalFormatter.formatCryptoAmountShorted(maxMana, "", Blockchain.Koinos.decimals()),
),
),
)
data object InsufficientBalance : Error(
title = resourceReference(R.string.koinos_insufficient_balance_to_send_koin_title),
subtitle = resourceReference(R.string.koinos_insufficient_balance_to_send_koin_description),
)
data class ManaExceedsBalance(
val availableKoinForTransfer: BigDecimal,
val onReduceClick: () -> Unit,
) : Error(
title = resourceReference(R.string.koinos_mana_exceeds_koin_balance_title),
subtitle = resourceReference(
R.string.koinos_mana_exceeds_koin_balance_description,
formatArgs = wrappedList(
BigDecimalFormatter.formatCryptoAmount(
availableKoinForTransfer,
Blockchain.Koinos.currency,
Blockchain.Koinos.decimals(),
),
),
),
buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
text = resourceReference(R.string.send_notification_reduce_to, wrappedList(availableKoinForTransfer)),
onClick = onReduceClick,
),
)
}
}

View file

@ -60,6 +60,7 @@ internal class SendStateFactory(
SendFeeStateConverter(
appCurrencyProvider = appCurrencyProvider,
feeCryptoCurrencyStatusProvider = feeCryptoCurrencyStatusProvider,
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
)
}
private val confirmStateConverter by lazy(LazyThreadSafetyMode.NONE) {

View file

@ -125,6 +125,7 @@ internal sealed class SendStates {
val feeSelectorState: FeeSelectorState,
val fee: Fee?,
val rate: BigDecimal?,
val isFeeConvertibleToFiat: Boolean,
val appCurrency: AppCurrency,
val isFeeApproximate: Boolean,
val isCustomSelected: Boolean,

View file

@ -101,7 +101,11 @@ internal class SendNotificationFactory(
addTooLowNotification(feeState)
// blockchain specific
addCardanoNotifications(sendingAmount, feeState.fee, state)
addValidateTransactionNotifications(
sendingAmount = sendingAmount,
fee = feeState.fee,
state = state,
)
}.toImmutableList()
}
@ -420,13 +424,12 @@ internal class SendNotificationFactory(
return Blockchain.fromNetworkId(this.currency.network.backendId) == Blockchain.Arbitrum
}
private suspend fun MutableList<SendNotification>.addCardanoNotifications(
private suspend fun MutableList<SendNotification>.addValidateTransactionNotifications(
sendingAmount: BigDecimal,
fee: Fee?,
state: SendUiState,
) {
val sendingCurrency = cryptoCurrencyStatusProvider().currency
if (!BlockchainUtils.isCardano(sendingCurrency.network.id.value)) return
validateTransactionUseCase(
amount = sendingAmount.convertToAmount(sendingCurrency),
@ -437,10 +440,14 @@ internal class SendNotificationFactory(
network = sendingCurrency.network,
).fold(
ifLeft = {
addCardanoTransactionValidationError(
error = it as? BlockchainSdkError.Cardano ?: return@fold,
sendingCurrency = sendingCurrency,
)
when (it) {
is BlockchainSdkError.Cardano -> addCardanoTransactionValidationError(
error = it,
sendingCurrency = sendingCurrency,
)
is BlockchainSdkError.Koinos -> addKoinosTransactionValidationError(error = it)
else -> return
}
},
ifRight = {
(fee as? Fee.CardanoToken)?.let {
@ -488,6 +495,36 @@ internal class SendNotificationFactory(
}
}
private fun MutableList<SendNotification>.addKoinosTransactionValidationError(error: BlockchainSdkError.Koinos) {
when (error) {
is BlockchainSdkError.Koinos.InsufficientBalance -> {
add(SendNotification.Koinos.InsufficientBalance)
}
is BlockchainSdkError.Koinos.InsufficientMana -> {
add(
SendNotification.Koinos.InsufficientRecoverableMana(
mana = error.manaBalance ?: BigDecimal.ZERO,
maxMana = error.maxMana ?: BigDecimal.ZERO,
),
)
}
is BlockchainSdkError.Koinos.ManaFeeExceedsBalance -> {
add(
SendNotification.Koinos.ManaExceedsBalance(
availableKoinForTransfer = error.availableKoinForTransfer,
onReduceClick = {
clickIntents.onAmountReduceClick(
reduceAmountTo = error.availableKoinForTransfer,
clazz = SendNotification.Koinos.InsufficientRecoverableMana::class.java,
)
},
),
)
}
else -> {}
}
}
private suspend fun MutableList<SendNotification>.addDustWarningNotification(
feeValue: BigDecimal,
sendingAmount: BigDecimal,

View file

@ -10,6 +10,7 @@ import kotlinx.collections.immutable.persistentListOf
internal class SendFeeStateConverter(
private val appCurrencyProvider: Provider<AppCurrency>,
private val feeCryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
) : Converter<Unit, SendStates.FeeState> {
override fun convert(value: Unit): SendStates.FeeState {
@ -21,6 +22,7 @@ internal class SendFeeStateConverter(
appCurrency = appCurrencyProvider(),
isFeeApproximate = false,
isCustomSelected = false,
isFeeConvertibleToFiat = cryptoCurrencyStatusProvider().currency.network.hasFiatFeeRate,
)
}
}

View file

@ -82,6 +82,7 @@ internal object FeeStatePreviewData {
isFeeApproximate = false,
notifications = persistentListOf(),
isCustomSelected = false,
isFeeConvertibleToFiat = true,
)
val feeChoosableState = feeState.copy(

View file

@ -29,16 +29,15 @@ import com.tangem.core.ui.components.buttons.common.TangemButton
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
import com.tangem.core.ui.components.keyboardAsState
import com.tangem.core.ui.extensions.resolveAnnotatedReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.shareText
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.domain.tokens.model.Amount
import com.tangem.features.send.impl.presentation.state.SendUiCurrentScreen
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.features.send.impl.presentation.state.SendUiStateType
import com.tangem.features.send.impl.presentation.utils.getFiatString
import com.tangem.features.send.impl.presentation.utils.getCryptoReference
@Composable
internal fun SendNavigationButtons(
@ -175,7 +174,11 @@ private fun SendingText(
val sendingFiat = if (uiState.isSubtracted) {
fiatAmount?.value
} else {
feeFiat?.let { fiatAmount?.value?.plus(it) }
if (feeState?.isFeeConvertibleToFiat == true) {
feeFiat?.let { fiatAmount?.value?.plus(it) }
} else {
fiatAmount?.value
}
}
if (feeFiat != null && sendingFiat != null) {
@ -184,14 +187,23 @@ private fun SendingText(
fiatCurrencySymbol = feeState.appCurrency.symbol,
fiatCurrencyCode = feeState.appCurrency.code,
)
val feeValue = getFiatString(
value = feeState.fee?.amount?.value,
rate = feeState.rate,
appCurrency = feeState.appCurrency,
)
val feeValue = if (feeState.isFeeConvertibleToFiat) {
getFiatString(
value = feeState.fee?.amount?.value,
rate = feeState.rate,
appCurrency = feeState.appCurrency,
)
} else {
getCryptoReference(feeState.fee?.amount, feeState.isFeeApproximate)?.resolveReference().orEmpty()
}
val textResource = remember(uiState) {
resourceReference(
id = R.string.send_summary_transaction_description,
id = if (feeState.isFeeConvertibleToFiat) {
R.string.send_summary_transaction_description
} else {
R.string.send_summary_transaction_description_no_fiat_fee
},
formatArgs = wrappedList(sendingValue, feeValue),
)
}

View file

@ -54,7 +54,11 @@ internal fun SendSpeedSelectorItem(
onSelect = onSelect,
modifier = modifier,
preDot = getCryptoReference(amount, state.isFeeApproximate),
postDot = getFiatReference(amount?.value, state.rate, state.appCurrency),
postDot = if (state.isFeeConvertibleToFiat) {
getFiatReference(amount?.value, state.rate, state.appCurrency)
} else {
null
},
ellipsizeOffset = amount?.currencySymbol?.length,
isSelected = content?.selectedFee == feeType,
showDivider = showDivider,

View file

@ -62,7 +62,11 @@ internal fun FeeBlock(feeState: SendStates.FeeState, isClickDisabled: Boolean, o
titleRes = title,
iconRes = icon,
preDot = getCryptoReference(feeAmount, feeState.isFeeApproximate),
postDot = getFiatReference(feeAmount?.value, feeState.rate, feeState.appCurrency),
postDot = if (feeState.isFeeConvertibleToFiat) {
getFiatReference(feeAmount?.value, feeState.rate, feeState.appCurrency)
} else {
null
},
ellipsizeOffset = feeAmount?.currencySymbol?.length,
isSelected = true,
showDivider = false,