Updated on 2026-08-14
This commit is contained in:
parent
8690dc97da
commit
c024ba2836
9 changed files with 109 additions and 40 deletions
|
|
@ -101,8 +101,7 @@ fun TangemTokenRow(
|
|||
.testTag(TokenElementsTestTags.TOKEN_NON_FIAT_BLOCK),
|
||||
)
|
||||
},
|
||||
modifier = modifier
|
||||
.tokenClickable(tokenRowUM = tokenRowUM),
|
||||
modifier = modifier.tokenClickable(tokenRowUM = tokenRowUM),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ internal fun FeeSpeedSelectorContent(
|
|||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Title(onLearnMoreClick = intents::onLearnMoreClick)
|
||||
SpacerH8()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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<TokenItemState>,
|
||||
val selectedToken: FeeTokenItemState,
|
||||
val tokens: ImmutableList<FeeTokenItemState>,
|
||||
)
|
||||
|
|
@ -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<AppCurrency>,
|
||||
private val onTokenClick: () -> Unit,
|
||||
) : Converter<CryptoCurrencyStatus, TokenItemState> {
|
||||
) : Converter<CryptoCurrencyStatus, FeeTokenItemState> {
|
||||
|
||||
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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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) },
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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<GetFeeError, LoadedFeeResult> {
|
||||
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(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue