From 7740d6f4536007144e1837867628b714fd4b5d82 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Jan 2026 15:45:42 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/TransactionDomainModule.kt | 6 ++-- .../MockedGaslessTransactionRepository.kt | 6 ++-- .../usecase/gasless/TokenFeeCalculator.kt | 4 ++- .../send/v2/api/entity/FeeSelectorUM.kt | 14 +++++++- .../send/v2/api/params/FeeSelectorParams.kt | 10 ++++-- .../DefaultFeeSelectorBlockComponent.kt | 1 + .../model/FeeExtendedSelectorModel.kt | 35 ++++++++---------- .../model/SelectedTokenItemConverter.kt | 21 +++++++---- .../extended/ui/FeeExtendedSelectorContent.kt | 9 +++-- .../speed/ui/FeeSpeedSelectorContent.kt | 12 +++++-- .../token/model/FeeTokenSelectorModel.kt | 4 ++- .../model/FeeSelectorAlertFactory.kt | 1 + .../v2/feeselector/model/FeeSelectorLogic.kt | 23 +++++++++++- .../FeeSelectorLoadedTransformer.kt | 5 +-- .../FeeSelectorTokenSelectedTransformer.kt | 26 ++++++++++++++ .../send/v2/send/DefaultSendComponent.kt | 1 + .../v2/send/confirm/SendConfirmComponent.kt | 3 ++ .../features/send/v2/send/model/SendModel.kt | 36 ++++++++++++++++--- 18 files changed, 167 insertions(+), 50 deletions(-) create mode 100644 features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorTokenSelectedTransformer.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt index ea4bd0c0da..5eb7159463 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt @@ -291,7 +291,6 @@ internal object TransactionDomainModule { @Singleton fun provideGetFeeForGaslessUseCase( walletManagersFacade: WalletManagersFacade, - demoConfig: DemoConfig, gaslessTransactionRepository: GaslessTransactionRepository, currenciesRepository: CurrenciesRepository, getFeeUseCase: GetFeeUseCase, @@ -299,7 +298,7 @@ internal object TransactionDomainModule { ): GetFeeForGaslessUseCase { return GetFeeForGaslessUseCase( walletManagersFacade = walletManagersFacade, - demoConfig = demoConfig, + demoConfig = DemoConfig, gaslessTransactionRepository = gaslessTransactionRepository, currenciesRepository = currenciesRepository, getMultiCryptoCurrencyStatusUseCase = getMultiCryptoCurrencyStatusUseCase, @@ -311,7 +310,6 @@ internal object TransactionDomainModule { @Singleton fun provideGetFeeForTokenUseCase( walletManagersFacade: WalletManagersFacade, - demoConfig: DemoConfig, gaslessTransactionRepository: GaslessTransactionRepository, currenciesRepository: CurrenciesRepository, getMultiCryptoCurrencyStatusUseCase: GetMultiCryptoCurrencyStatusUseCase, @@ -319,7 +317,7 @@ internal object TransactionDomainModule { return GetFeeForTokenUseCase( gaslessTransactionRepository = gaslessTransactionRepository, walletManagersFacade = walletManagersFacade, - demoConfig = demoConfig, + demoConfig = DemoConfig, currenciesRepository = currenciesRepository, getMultiCryptoCurrencyStatusUseCase = getMultiCryptoCurrencyStatusUseCase, ) diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/MockedGaslessTransactionRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/MockedGaslessTransactionRepository.kt index 72e1e7c662..9ad092d702 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/MockedGaslessTransactionRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/MockedGaslessTransactionRepository.kt @@ -25,9 +25,9 @@ class MockedGaslessTransactionRepository( val usdcPolygon = responseCryptoCurrenciesFactory.createToken( blockchain = Blockchain.Polygon, sdkToken = Token( - contractAddress = "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", - name = "USD Coin", - symbol = "USDC", + contractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7", + name = "Tether", + symbol = "USDT", decimals = 6, ), network = network, diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculator.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculator.kt index e1c07c4617..612cd7cb24 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculator.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/TokenFeeCalculator.kt @@ -114,7 +114,9 @@ internal class TokenFeeCalculator( .toBigInteger() val feeInTokenCurrency = coinPriceInTokenInBigDecimal.multiply( - feeInNativeCurrency.toBigDecimal(), + feeInNativeCurrency + .toBigDecimal() + .movePointLeft(nativeCurrencyStatus.currency.decimals), ) val tokenBalance = tokenForPayFeeStatus.value.amount ?: BigDecimal.ZERO diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt index a62fec37f1..ef578be698 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt @@ -1,6 +1,8 @@ package com.tangem.features.send.v2.api.entity import androidx.compose.runtime.Immutable +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.core.analytics.models.AnalyticsParam @@ -44,7 +46,7 @@ sealed class FeeSelectorUM { is Custom, -> AnalyticsParam.FeeType.Custom is Fast -> AnalyticsParam.FeeType.Max - is Market -> AnalyticsParam.FeeType.Normal + is Market, is FeeItem.Loading -> AnalyticsParam.FeeType.Normal is Slow -> AnalyticsParam.FeeType.Min } } @@ -84,6 +86,16 @@ sealed class FeeItem { return this::class == other::class } + fun isLoading(): Boolean { + return this is Loading + } + + object Loading : FeeItem() { + override val title: TextReference = resourceReference(R.string.common_fee_selector_option_market) + override val iconRes: Int = R.drawable.ic_bird_24 + override val fee: Fee = Fee.Common(Amount(BigDecimal.ZERO, Blockchain.Unknown)) + } + data class Suggested( override val title: TextReference, override val fee: Fee, diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/params/FeeSelectorParams.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/params/FeeSelectorParams.kt index 8e46ee3710..8a6a6edf3e 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/params/FeeSelectorParams.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/params/FeeSelectorParams.kt @@ -15,7 +15,7 @@ import com.tangem.features.send.v2.api.entity.FeeSelectorUM sealed class FeeSelectorParams { abstract val state: FeeSelectorUM abstract val userWalletId: UserWalletId - abstract val onLoadFeeExtended: (suspend () -> Either)? + abstract val onLoadFeeExtended: (suspend (CryptoCurrencyStatus?) -> Either)? abstract val onLoadFee: suspend () -> Either abstract val cryptoCurrencyStatus: CryptoCurrencyStatus abstract val feeCryptoCurrencyStatus: CryptoCurrencyStatus @@ -27,7 +27,9 @@ sealed class FeeSelectorParams { data class FeeSelectorBlockParams( override val state: FeeSelectorUM, override val userWalletId: UserWalletId, - override val onLoadFeeExtended: (suspend () -> Either)? = null, + override val onLoadFeeExtended: ( + suspend (CryptoCurrencyStatus?) -> Either + )? = null, override val onLoadFee: suspend () -> Either, override val cryptoCurrencyStatus: CryptoCurrencyStatus, override val feeCryptoCurrencyStatus: CryptoCurrencyStatus, @@ -40,7 +42,9 @@ sealed class FeeSelectorParams { data class FeeSelectorDetailsParams( override val state: FeeSelectorUM, override val userWalletId: UserWalletId, - override val onLoadFeeExtended: (suspend () -> Either)? = null, + override val onLoadFeeExtended: ( + suspend (CryptoCurrencyStatus?) -> Either + )? = null, override val onLoadFee: suspend () -> Either, override val cryptoCurrencyStatus: CryptoCurrencyStatus, override val feeCryptoCurrencyStatus: CryptoCurrencyStatus, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorBlockComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorBlockComponent.kt index bf86789265..80a1151aa7 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorBlockComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/DefaultFeeSelectorBlockComponent.kt @@ -47,6 +47,7 @@ internal class DefaultFeeSelectorBlockComponent @AssistedInject constructor( params = FeeSelectorParams.FeeSelectorDetailsParams( state = model.uiState.value, onLoadFee = params.onLoadFee, + onLoadFeeExtended = params.onLoadFeeExtended, feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus, cryptoCurrencyStatus = params.cryptoCurrencyStatus, callback = model, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/model/FeeExtendedSelectorModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/model/FeeExtendedSelectorModel.kt index 75d481da3c..0a68ace88e 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/model/FeeExtendedSelectorModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/model/FeeExtendedSelectorModel.kt @@ -29,10 +29,6 @@ class FeeExtendedSelectorModel @Inject constructor( private val params = paramsContainer.require() private var appCurrency: AppCurrency = AppCurrency.Default - private val tokenConverter = SelectedTokenItemConverter( - appCurrencyProvider = Provider { appCurrency }, - onTokenClick = { router.push(FeeSelectorRoute.ChooseToken) }, - ) init { initAppCurrency() @@ -44,19 +40,13 @@ class FeeExtendedSelectorModel @Inject constructor( init { params.state .filterIsInstance() - .onEach(::onParentStateUpdated) + .onEach { state -> uiState.value = convertState(state) } .launchIn(modelScope) } private fun getInitialState(): FeeExtendedSelectorUM { val parentContentState = params.state.value as FeeSelectorUM.Content - - return FeeExtendedSelectorUM( - parent = parentContentState, - token = tokenConverter.convert(parentContentState.feeExtraInfo.feeCryptoCurrencyStatus), - fee = parentContentState.selectedFeeItem, - onFeeClick = { router.push(FeeSelectorRoute.ChooseSpeed) }, - ) + return convertState(parentContentState) } private fun initAppCurrency() { @@ -65,13 +55,18 @@ class FeeExtendedSelectorModel @Inject constructor( } } - private fun onParentStateUpdated(state: FeeSelectorUM.Content) { - uiState.update { current -> - current.copy( - parent = state, - token = tokenConverter.convert(state.feeExtraInfo.feeCryptoCurrencyStatus), - fee = state.selectedFeeItem, - ) - } + private fun convertState(state: FeeSelectorUM.Content): FeeExtendedSelectorUM { + val tokenConverter = SelectedTokenItemConverter( + appCurrencyProvider = Provider { appCurrency }, + onTokenClick = { router.push(FeeSelectorRoute.ChooseToken) }, + isTokenSelectionAvailable = state.feeExtraInfo.availableFeeCurrencies.orEmpty().size > 1, + ) + + return FeeExtendedSelectorUM( + parent = state, + token = tokenConverter.convert(state.feeExtraInfo.feeCryptoCurrencyStatus), + fee = state.selectedFeeItem, + onFeeClick = { router.push(FeeSelectorRoute.ChooseSpeed) }, + ) } } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/model/SelectedTokenItemConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/model/SelectedTokenItemConverter.kt index af61ddfe05..070c1895a1 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/model/SelectedTokenItemConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/model/SelectedTokenItemConverter.kt @@ -16,6 +16,7 @@ import com.tangem.utils.converter.Converter internal class SelectedTokenItemConverter( private val appCurrencyProvider: Provider, private val onTokenClick: () -> Unit, + private val isTokenSelectionAvailable: Boolean, ) : Converter { override fun convert(value: CryptoCurrencyStatus): TokenItemState { @@ -24,9 +25,14 @@ internal class SelectedTokenItemConverter( } val appCurrency = appCurrencyProvider() + val tokenItemConverter = TokenItemStateConverter( appCurrency = appCurrencyProvider(), - onItemClick = { _, _ -> onTokenClick() }, + onItemClick = if (isTokenSelectionAvailable) { + { _, _ -> onTokenClick() } + } else { + null + }, ) val state = tokenItemConverter.convert(value) @@ -38,10 +44,14 @@ internal class SelectedTokenItemConverter( } state.copy( - fiatAmountState = TokenItemState.FiatAmountState.Icon( - iconRes = R.drawable.ic_select_18_24, - tint = IconTint.Informative, - ), + fiatAmountState = if (isTokenSelectionAvailable) { + TokenItemState.FiatAmountState.Icon( + iconRes = R.drawable.ic_select_18_24, + tint = IconTint.Informative, + ) + } else { + TokenItemState.FiatAmountState.Empty + }, subtitleState = TokenItemState.SubtitleState.TextContent( value = resourceReference( R.string.common_balance, @@ -50,7 +60,6 @@ internal class SelectedTokenItemConverter( isAvailable = false, ), subtitle2State = null, - ) } else -> state diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/ui/FeeExtendedSelectorContent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/ui/FeeExtendedSelectorContent.kt index a94b018eae..eef8cc085a 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/ui/FeeExtendedSelectorContent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/extended/ui/FeeExtendedSelectorContent.kt @@ -22,6 +22,7 @@ import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.icons.IconTint import com.tangem.core.ui.components.token.TokenItem import com.tangem.core.ui.components.token.state.TokenItemState +import com.tangem.core.ui.extensions.conditional import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fee @@ -61,12 +62,15 @@ fun FeeExtendedSelectorContent(state: FeeExtendedSelectorUM, modifier: Modifier val feeFiatRateUM = state.parent.feeFiatRateUM val fee = state.fee.fee + val isChooseSpeedAvailable = state.parent.feeItems.size > 1 RegularFeeItemContent( modifier = Modifier .fillMaxWidth() .clip(RoundedCornerShape(TangemTheme.dimens.radius14)) - .clickable(onClick = state.onFeeClick) + .conditional(isChooseSpeedAvailable) { + clickable(onClick = state.onFeeClick) + } .background( TangemTheme.colors.background.action, RoundedCornerShape(TangemTheme.dimens.radius14), @@ -93,7 +97,8 @@ fun FeeExtendedSelectorContent(state: FeeExtendedSelectorUM, modifier: Modifier null }, ellipsizeOffset = fee.amount.currencySymbol.length, - showSelectorIcon = true, + showSelectorIcon = isChooseSpeedAvailable, + isLoading = state.fee.isLoading(), ) } } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/speed/ui/FeeSpeedSelectorContent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/speed/ui/FeeSpeedSelectorContent.kt index 4f5034555d..8a6f57fb07 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/speed/ui/FeeSpeedSelectorContent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/speed/ui/FeeSpeedSelectorContent.kt @@ -38,6 +38,7 @@ import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.ui.amountScreen.utils.getFiatReference import com.tangem.core.ui.components.SpacerH8 import com.tangem.core.ui.components.SpacerWMax +import com.tangem.core.ui.components.TextShimmer import com.tangem.core.ui.components.atoms.text.EllipsisText import com.tangem.core.ui.components.atoms.text.TextEllipsis import com.tangem.core.ui.components.inputrow.InputRowEnter @@ -325,6 +326,7 @@ internal fun RegularFeeItemContent( postDot: TextReference? = null, ellipsizeOffset: Int? = null, showSelectorIcon: Boolean = false, + isLoading: Boolean = false, ) { Column(modifier = modifier) { Row( @@ -349,6 +351,7 @@ internal fun RegularFeeItemContent( preDot = preDot, postDot = postDot, ellipsizeOffset = ellipsizeOffset, + isLoading = isLoading, ) if (showSelectorIcon) { @@ -371,6 +374,7 @@ private fun FeeDescription( preDot: TextReference? = null, postDot: TextReference? = null, ellipsizeOffset: Int? = null, + isLoading: Boolean = false, ) { Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(2.dp)) { Text( @@ -379,8 +383,12 @@ private fun FeeDescription( style = TangemTheme.typography.subtitle2, modifier = Modifier.testTag(SendSelectNetworkFeeBottomSheetTestTags.REGULAR_ITEM_TITLE), ) - if (preDot != null) { - FeeValueContent(preDot = preDot, postDot = postDot, ellipsizeOffset = ellipsizeOffset) + when { + isLoading -> TextShimmer( + modifier = Modifier.width(122.dp), + style = TangemTheme.typography.caption1, + ) + preDot != null -> FeeValueContent(preDot = preDot, postDot = postDot, ellipsizeOffset = ellipsizeOffset) } } } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/model/FeeTokenSelectorModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/model/FeeTokenSelectorModel.kt index fd5ac4c138..4afcd80323 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/model/FeeTokenSelectorModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/model/FeeTokenSelectorModel.kt @@ -22,6 +22,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch @@ -47,7 +48,8 @@ internal class FeeTokenSelectorModel @Inject constructor( initAppCurrency() params.state - .onEach { uiState.value = stateFromParent(it as FeeSelectorUM.Content) } + .filterIsInstance() + .onEach { state -> uiState.value = stateFromParent(state) } .launchIn(modelScope) } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorAlertFactory.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorAlertFactory.kt index 6abebb0fb4..96a8476eef 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorAlertFactory.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorAlertFactory.kt @@ -92,6 +92,7 @@ internal class FeeSelectorAlertFactory @Inject constructor( is FeeItem.Slow -> newTransactionFee.minimum is FeeItem.Market -> newTransactionFee.normal is FeeItem.Fast -> newTransactionFee.priority + FeeItem.Loading -> newTransactionFee.normal is FeeItem.Custom -> return } } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorLogic.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorLogic.kt index 81983520fd..0599751766 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorLogic.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorLogic.kt @@ -8,6 +8,7 @@ import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.transaction.error.GetFeeError @@ -31,6 +32,7 @@ import com.tangem.utils.transformer.update import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn @@ -56,6 +58,7 @@ internal class FeeSelectorLogic @AssistedInject constructor( ) : FeeSelectorIntents { private var appCurrency: AppCurrency = AppCurrency.Default + private var selectedToken: CryptoCurrencyStatus? = null val uiState = MutableStateFlow(params.state) init { @@ -112,9 +115,19 @@ internal class FeeSelectorLogic @AssistedInject constructor( } override fun onTokenSelected(status: CryptoCurrencyStatus) { + if (status.currency !is CryptoCurrency.Token || + status.currency.id == params.feeCryptoCurrencyStatus.currency.id) { + selectedToken = null + loadFee() + return + } + uiState.update(FeeSelectorTokenSelectedTransformer(status)) + uiState.update { state -> if (state is FeeSelectorUM.Content) { state.copy( + selectedFeeItem = FeeItem.Loading, + feeItems = persistentListOf(FeeItem.Loading), feeExtraInfo = state.feeExtraInfo.copy( feeCryptoCurrencyStatus = status, ), @@ -123,6 +136,14 @@ internal class FeeSelectorLogic @AssistedInject constructor( state } } + + if (status.currency is CryptoCurrency.Token) { + selectedToken = status + } else { + selectedToken = null + } + + loadFee() } override fun onCustomFeeValueChange(index: Int, value: String) { @@ -219,7 +240,7 @@ internal class FeeSelectorLogic @AssistedInject constructor( private suspend fun callLoadFee(): Either { val extended = params.onLoadFeeExtended return if (extended != null && sendFeatureToggles.isGaslessTransactionsEnabled) { - extended().fold( + extended(selectedToken).fold( ifLeft = { error -> if (error is GetFeeError.GaslessError) { params.onLoadFee().map { LoadedFeeResult.Basic(it) } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt index 75fb5135d8..0daa9f3a3c 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorLoadedTransformer.kt @@ -43,7 +43,8 @@ internal class FeeSelectorLoadedTransformer( ) val selectedFee = when (prevState) { - is FeeSelectorUM.Content -> feeItems.first { it.isSameClass(prevState.selectedFeeItem) } + is FeeSelectorUM.Content -> + feeItems.firstOrNull { it.isSameClass(prevState.selectedFeeItem) } ?: FeeItem.Loading is FeeSelectorUM.Error, FeeSelectorUM.Loading, -> feeItems.find { it is FeeItem.Suggested } ?: feeItems.first { it is FeeItem.Market } @@ -52,7 +53,7 @@ internal class FeeSelectorLoadedTransformer( val nonce = ((prevState as? FeeSelectorUM.Content)?.feeNonce as? FeeNonce.Nonce)?.nonce return FeeSelectorUM.Content( - isPrimaryButtonEnabled = true, + isPrimaryButtonEnabled = selectedFee !is FeeItem.Loading, fees = fees.transactionFee, feeItems = feeItems, selectedFeeItem = selectedFee, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorTokenSelectedTransformer.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorTokenSelectedTransformer.kt new file mode 100644 index 0000000000..bb7d3d5df7 --- /dev/null +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/transformers/FeeSelectorTokenSelectedTransformer.kt @@ -0,0 +1,26 @@ +package com.tangem.features.send.v2.feeselector.model.transformers + +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.features.send.v2.api.entity.FeeItem +import com.tangem.features.send.v2.api.entity.FeeSelectorUM +import com.tangem.utils.transformer.Transformer +import kotlinx.collections.immutable.persistentListOf + +class FeeSelectorTokenSelectedTransformer( + private val selectedToken: CryptoCurrencyStatus, +) : Transformer { + + override fun transform(prevState: FeeSelectorUM): FeeSelectorUM { + return if (prevState is FeeSelectorUM.Content) { + prevState.copy( + selectedFeeItem = FeeItem.Loading, + feeItems = persistentListOf(FeeItem.Loading), + feeExtraInfo = prevState.feeExtraInfo.copy( + feeCryptoCurrencyStatus = selectedToken, + ), + ) + } else { + prevState + } + } +} \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt index d6e89cd52c..2230676a18 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt @@ -216,6 +216,7 @@ internal class DefaultSendComponent @AssistedInject constructor( callback = model, predefinedValues = model.predefinedValues, onLoadFee = model::loadFee, + onLoadFeeExtended = model::loadFeeExtended, onSendTransaction = { innerRouter.replaceAll(CommonSendRoute.ConfirmSuccess) }, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt index fef494deef..afab0e41d7 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt @@ -15,6 +15,7 @@ import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.transaction.error.GetFeeError +import com.tangem.domain.transaction.models.TransactionFeeExtended import com.tangem.features.send.v2.api.FeeSelectorBlockComponent import com.tangem.features.send.v2.api.SendNotificationsComponent import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData @@ -86,6 +87,7 @@ internal class SendConfirmComponent( params = FeeSelectorParams.FeeSelectorBlockParams( state = model.uiState.value.feeSelectorUM, onLoadFee = params.onLoadFee, + onLoadFeeExtended = params.onLoadFeeExtended, feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus, cryptoCurrencyStatus = params.cryptoCurrencyStatus, feeStateConfiguration = model.feeStateConfiguration, @@ -164,6 +166,7 @@ internal class SendConfirmComponent( val isBalanceHidingFlow: StateFlow, val predefinedValues: PredefinedValues, val onLoadFee: suspend () -> Either, + val onLoadFeeExtended: suspend (CryptoCurrencyStatus?) -> Either, val onSendTransaction: () -> Unit, ) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt index 5035e1ea7c..d6b190dd5d 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable import arrow.core.Either import arrow.core.getOrElse import arrow.core.left +import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.navigationButtons.NavigationUM @@ -37,8 +38,11 @@ import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.transaction.error.GetFeeError +import com.tangem.domain.transaction.models.TransactionFeeExtended import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase +import com.tangem.domain.transaction.usecase.gasless.GetFeeForGaslessUseCase +import com.tangem.domain.transaction.usecase.gasless.GetFeeForTokenUseCase import com.tangem.domain.utils.convertToSdkAmount import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.usecase.GetUserWalletUseCase @@ -95,6 +99,8 @@ internal class SendModel @Inject constructor( private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val createTransferTransactionUseCase: CreateTransferTransactionUseCase, private val getFeeUseCase: GetFeeUseCase, + private val getFeeForGaslessUseCase: GetFeeForGaslessUseCase, + private val getFeeForTokenUseCase: GetFeeForTokenUseCase, private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase, private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase, private val sendAmountUpdateTrigger: SendAmountUpdateTrigger, @@ -245,10 +251,10 @@ internal class SendModel @Inject constructor( showAlertError() } - suspend fun loadFee(): Either { + suspend fun prepareTransferTransaction(): Either { val predefinedValues = predefinedValues val cryptoCurrencyStatus = cryptoCurrencyStatusFlow.value - val transferTransaction = if (predefinedValues is PredefinedValues.Content.Deeplink) { + return if (predefinedValues is PredefinedValues.Content.Deeplink) { val predefinedAmount = predefinedValues.amount.parseBigDecimalOrNull() createTransferTransactionUseCase( amount = predefinedAmount?.convertToSdkAmount(cryptoCurrencyStatus) ?: error("Invalid amount"), @@ -271,9 +277,12 @@ internal class SendModel @Inject constructor( userWalletId = userWallet.walletId, network = cryptoCurrency.network, ) - }.getOrElse { - return GetFeeError.DataError(it).left() } + } + + suspend fun loadFee(): Either { + val transferTransaction = prepareTransferTransaction() + .getOrElse { return GetFeeError.DataError(it).left() } return getFeeUseCase( transactionData = transferTransaction, @@ -282,6 +291,25 @@ internal class SendModel @Inject constructor( ) } + suspend fun loadFeeExtended(maybeToken: CryptoCurrencyStatus?): Either { + val transferTransaction = prepareTransferTransaction() + .getOrElse { return GetFeeError.DataError(it).left() } + + return if (maybeToken == null) { + getFeeForGaslessUseCase( + transactionData = transferTransaction, + userWallet = userWallet, + network = params.currency.network, + ) + } else { + getFeeForTokenUseCase( + transactionData = transferTransaction, + userWallet = userWallet, + token = maybeToken.currency, + ) + } + } + fun showAlertError() { sendConfirmAlertFactory.getGenericErrorState( onFailedTxEmailClick = ::onFailedTxEmailClick,