diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt index 8d904b5ce7..8f9c251ea1 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/row/token/TangemTokenRow.kt @@ -101,8 +101,7 @@ fun TangemTokenRow( .testTag(TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK), ) }, - modifier = modifier - .tokenClickable(tokenRowUM = tokenRowUM), + modifier = modifier.tokenClickable(tokenRowUM = tokenRowUM), ) } diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/GetAvailableFeeTokensUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/GetAvailableFeeTokensUseCase.kt index 3883bbacf4..0d2c825756 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/GetAvailableFeeTokensUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/gasless/GetAvailableFeeTokensUseCase.kt @@ -14,7 +14,6 @@ import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.transaction.GaslessTransactionRepository import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.raiseIllegalStateError -import java.math.BigDecimal class GetAvailableFeeTokensUseCase( private val gaslessTransactionRepository: GaslessTransactionRepository, @@ -88,9 +87,7 @@ class GetAvailableFeeTokensUseCase( .filter { it.currency.network.id == network.id } .filter { currencyStatus -> val token = currencyStatus.currency - token is CryptoCurrency.Token && - currencyStatus.value.amount?.let { amount -> amount > BigDecimal.ZERO } == true && - supportedGaslessTokens.contains(token.contractAddress.lowercase()) + token is CryptoCurrency.Token && supportedGaslessTokens.contains(token.contractAddress.lowercase()) } .toList() } 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 8a6f57fb07..89a8ed3660 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 @@ -75,6 +75,7 @@ internal fun FeeSpeedSelectorContent( start = 16.dp, end = 16.dp, ), + horizontalAlignment = Alignment.CenterHorizontally, ) { Title(onLearnMoreClick = intents::onLearnMoreClick) SpacerH8() diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/entity/FeeTokenItemState.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/entity/FeeTokenItemState.kt new file mode 100644 index 0000000000..ffc1e2e2d0 --- /dev/null +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/entity/FeeTokenItemState.kt @@ -0,0 +1,8 @@ +package com.tangem.features.send.v2.feeselector.component.token.entity + +import com.tangem.core.ui.components.token.state.TokenItemState + +internal data class FeeTokenItemState( + val state: TokenItemState, + val isAvailable: Boolean = true, +) \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/entity/FeeTokenSelectorUM.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/entity/FeeTokenSelectorUM.kt index 5de55c5a70..e1fb00d6af 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/entity/FeeTokenSelectorUM.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/entity/FeeTokenSelectorUM.kt @@ -1,11 +1,10 @@ package com.tangem.features.send.v2.feeselector.component.token.entity -import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.features.send.v2.api.entity.FeeSelectorUM import kotlinx.collections.immutable.ImmutableList -data class FeeTokenSelectorUM( +internal data class FeeTokenSelectorUM( val parent: FeeSelectorUM.Content, - val selectedToken: TokenItemState, - val tokens: ImmutableList, + val selectedToken: FeeTokenItemState, + val tokens: ImmutableList, ) \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/model/FeeTokenForListConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/model/FeeTokenForListConverter.kt index aeec5346f3..bcf449a9d1 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/model/FeeTokenForListConverter.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/model/FeeTokenForListConverter.kt @@ -8,29 +8,36 @@ import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.features.send.v2.feeselector.component.token.entity.FeeTokenItemState import com.tangem.features.send.v2.impl.R import com.tangem.utils.Provider import com.tangem.utils.converter.Converter +import com.tangem.utils.extensions.isZero internal class FeeTokenForListConverter( private val appCurrencyProvider: Provider, private val onTokenClick: () -> Unit, -) : Converter { +) : Converter { - override fun convert(value: CryptoCurrencyStatus): TokenItemState { + override fun convert(value: CryptoCurrencyStatus): FeeTokenItemState { if (value.value !is CryptoCurrencyStatus.Loaded) { - return TokenItemState.Loading(id = value.currency.id.value) + return FeeTokenItemState(TokenItemState.Loading(id = value.currency.id.value)) } val appCurrency = appCurrencyProvider() + val isBalanceZero = value.value.amount?.isZero() == true val tokenItemConverter = TokenItemStateConverter( appCurrency = appCurrencyProvider(), - onItemClick = { _, _ -> onTokenClick() }, + onItemClick = if (isBalanceZero.not()) { + { _, _ -> onTokenClick() } + } else { + null + }, ) val state = tokenItemConverter.convert(value) - return when (state) { + val updatedState = when (state) { is TokenItemState.Content -> { val balance = value.value.fiatAmount.format { fiat(fiatCurrencyCode = appCurrency.code, fiatCurrencySymbol = appCurrency.symbol) @@ -46,10 +53,14 @@ internal class FeeTokenForListConverter( isAvailable = false, ), subtitle2State = null, - ) } else -> state } + + return FeeTokenItemState( + state = updatedState, + isAvailable = isBalanceZero.not(), + ) } } \ No newline at end of file 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 4afcd80323..eb7e57e5b0 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 @@ -7,13 +7,13 @@ import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.navigation.url.UrlOpener -import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.feeselector.component.FeeSelectorComponentParams import com.tangem.features.send.v2.feeselector.component.token.FeeTokenSelectorIntents +import com.tangem.features.send.v2.feeselector.component.token.entity.FeeTokenItemState import com.tangem.features.send.v2.feeselector.component.token.entity.FeeTokenSelectorUM import com.tangem.features.send.v2.feeselector.model.FeeSelectorIntents import com.tangem.utils.Provider @@ -78,7 +78,7 @@ internal class FeeTokenSelectorModel @Inject constructor( ) } - private fun convertToken(status: CryptoCurrencyStatus): TokenItemState { + private fun convertToken(status: CryptoCurrencyStatus): FeeTokenItemState { return FeeTokenForListConverter( appCurrencyProvider = Provider { appCurrency }, onTokenClick = { onTokenSelected(status) }, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/ui/FeeTokenSelectorContent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/ui/FeeTokenSelectorContent.kt index 97f3d02076..76adedfcaf 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/ui/FeeTokenSelectorContent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/component/token/ui/FeeTokenSelectorContent.kt @@ -9,8 +9,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.key import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.text.* import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration @@ -37,10 +39,12 @@ import com.tangem.domain.models.network.Network import com.tangem.features.send.v2.api.entity.* import com.tangem.features.send.v2.feeselector.component.token.FeeTokenSelectorIntents import com.tangem.features.send.v2.feeselector.component.token.StubFeeTokenSelectorIntents +import com.tangem.features.send.v2.feeselector.component.token.entity.FeeTokenItemState import com.tangem.features.send.v2.feeselector.component.token.entity.FeeTokenSelectorUM import com.tangem.features.send.v2.impl.R import com.tangem.utils.StringsSigns import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import java.math.BigDecimal @Composable @@ -55,6 +59,7 @@ internal fun FeeTokenSelectorContent( start = 16.dp, end = 16.dp, ), + horizontalAlignment = Alignment.CenterHorizontally, ) { Title(onLearnMoreClick = intents::onLearnMoreClick) SpacerH8() @@ -101,17 +106,27 @@ private fun Title(modifier: Modifier = Modifier, onLearnMoreClick: () -> Unit) { ) } +@Suppress("MagicNumber") @Composable internal fun TokenListContent(state: FeeTokenSelectorUM, modifier: Modifier = Modifier) { Column( modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp), ) { - state.tokens.fastForEach { item -> + val (availableTokens, notAvailableTokens) = remember(state.tokens) { + state.tokens.partition { it.isAvailable }.let { pair -> + Pair( + first = pair.first.map { it.state }.toImmutableList(), + second = pair.second.map { it.state }.toImmutableList(), + ) + } + } + + availableTokens.fastForEach { item -> key(item.id) { TokenItem( modifier = Modifier - .selectedBorder(isSelected = state.selectedToken.id == item.id) + .selectedBorder(isSelected = state.selectedToken.state.id == item.id) .clip(RoundedCornerShape(TangemTheme.dimens.radius14)) .background(color = TangemTheme.colors.background.action), state = item, @@ -119,6 +134,28 @@ internal fun TokenListContent(state: FeeTokenSelectorUM, modifier: Modifier = Mo ) } } + + if (notAvailableTokens.isNotEmpty()) { + Text( + modifier = Modifier.padding(start = 12.dp, top = 8.dp), + text = stringResourceSafe(R.string.common_not_available), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + + notAvailableTokens.fastForEach { item -> + key(item.id) { + TokenItem( + modifier = Modifier + .graphicsLayer { alpha = 0.5f } + .clip(RoundedCornerShape(TangemTheme.dimens.radius14)) + .background(color = TangemTheme.colors.background.action), + state = item, + isBalanceHidden = false, + ) + } + } + } } } @@ -209,10 +246,17 @@ private fun Preview() { onItemLongClick = {}, ) + val feeTokenItemState = FeeTokenItemState(tokenItemState) + val state = FeeTokenSelectorUM( parent = parentUM, - selectedToken = tokenItemState, - tokens = persistentListOf(tokenItemState, tokenItemState.copy(id = "2")), + selectedToken = feeTokenItemState, + tokens = persistentListOf( + feeTokenItemState, + feeTokenItemState.copy(state = tokenItemState.copy(id = "2")), + feeTokenItemState.copy(state = tokenItemState.copy(id = "3"), isAvailable = false), + feeTokenItemState.copy(state = tokenItemState.copy(id = "4"), isAvailable = false), + ), ) TangemThemePreview { 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 42ffaa7515..8afabe494a 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 @@ -1,6 +1,7 @@ package com.tangem.features.send.v2.feeselector.model import arrow.core.Either +import arrow.core.flatMap import arrow.core.getOrElse import arrow.core.raise.either import com.tangem.blockchain.common.AmountType @@ -209,25 +210,34 @@ internal class FeeSelectorLogic @AssistedInject constructor( @Suppress("UnreachableCode") private suspend fun callLoadFee(): Either { val extended = params.onLoadFeeExtended - return if (extended != null && sendFeatureToggles.isGaslessTransactionsEnabled) { - val selectedToken = (uiState.value as? FeeSelectorUM.Content)?.feeExtraInfo?.feeCryptoCurrencyStatus - ?.takeIf { it.currency is CryptoCurrency.Token } - - extended(selectedToken).fold( - ifLeft = { error -> - if (error is GetFeeError.GaslessError) { - params.onLoadFee().map { LoadedFeeResult.Basic(it) } - } else { - Either.Left(error) - } - }, - ifRight = { fee -> - populateExtendedFee(fee) - }, - ) - } else { - params.onLoadFee().map { LoadedFeeResult.Basic(it) } + if (extended == null || !sendFeatureToggles.isGaslessTransactionsEnabled) { + return params.onLoadFee().map { LoadedFeeResult.Basic(it) } } + + val selectedTokenOrNull = (uiState.value as? FeeSelectorUM.Content)?.feeExtraInfo?.feeCryptoCurrencyStatus + + if (selectedTokenOrNull?.currency is CryptoCurrency.Coin) { + return params.onLoadFee().flatMap { loadedFee -> + val feeExtended = TransactionFeeExtended( + transactionFee = loadedFee, + feeTokenId = selectedTokenOrNull.currency.id, + ) + populateExtendedFee(feeExtended) + } + } + + return extended(selectedTokenOrNull).fold( + ifLeft = { error -> + if (error is GetFeeError.GaslessError) { + params.onLoadFee().map { LoadedFeeResult.Basic(it) } + } else { + Either.Left(error) + } + }, + ifRight = { fee -> + populateExtendedFee(fee) + }, + ) } private suspend fun populateExtendedFee(