From 430eced63a1fc377ad3b2c768fc211b662816f3e Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 30 Jul 2026 21:50:33 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../model/intents/WalletClickIntents.kt | 10 +++++ .../wallet/state/model/WalletActionButtons.kt | 12 +++++- .../wallet/state/model/WalletUM.kt | 1 + .../SetRefreshStateTransformer.kt | 10 +---- .../SetTokenListErrorTransformer.kt | 10 ++++- .../transformers/SetTokenListTransformer.kt | 27 +++++++++--- .../state/utils/MultiWalletActionsExt.kt | 25 ----------- .../state/utils/WalletActionButtonsFactory.kt | 43 +++++++++++++++++++ .../state/utils/WalletLoadingStateFactory.kt | 36 +++------------- .../SetTokenListTransformerTest.kt | 32 ++++++++++---- 10 files changed, 126 insertions(+), 80 deletions(-) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletActionButtonsFactory.kt diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletClickIntents.kt index a40d697620..16cfe24897 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletClickIntents.kt @@ -3,6 +3,7 @@ package com.tangem.feature.wallet.child.wallet.model.intents import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.isLocked @@ -18,6 +19,7 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletContentFetcher import com.tangem.feature.wallet.presentation.wallet.domain.unwrap import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertUM import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetRefreshStateTransformer import kotlinx.coroutines.CoroutineScope @@ -46,6 +48,7 @@ internal class WalletClickIntents @Inject constructor( private val tangemPayIntents: TangemPayClickIntentsImplementor, private val yieldSupplyApyUpdateUseCase: YieldSupplyApyUpdateUseCase, private val analyticsEventHandler: AnalyticsEventHandler, + private val uiMessageSender: UiMessageSender, ) : BaseWalletClickIntents(), WalletCardClickIntents by walletCardClickIntentsImplementor, WalletWarningsClickIntents by warningsClickIntentsImplementer, @@ -108,6 +111,13 @@ internal class WalletClickIntents @Inject constructor( fun onTransferClick(userWalletId: UserWalletId) { analyticsEventHandler.send(MainScreenAnalyticsEvent.ButtonTransfer()) + + val selectedWallet = stateController.getSelectedWalletUM() as? WalletUM.Content + if (selectedWallet?.areActionsAvailable == false) { + uiMessageSender.send(WalletAlertUM.unavailableOperation()) + return + } + router.openTransfer(userWalletId) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletActionButtons.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletActionButtons.kt index c1210fbbd4..10ed719757 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletActionButtons.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletActionButtons.kt @@ -8,6 +8,7 @@ import com.tangem.core.ui.ds.button.TangemButtonUM import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R /** @@ -26,7 +27,16 @@ internal sealed class WalletActionButtons( val buttonUM: TangemButtonUM get() = TangemButtonUM( text = text, - tangemIconUM = TangemIconUM.Icon(iconRes = iconRes), + tangemIconUM = TangemIconUM.Icon( + iconRes = iconRes, + tintReference = { + if (isEnabled) { + TangemTheme.colors2.graphic.neutral.primary + } else { + TangemTheme.colors2.graphic.neutral.quaternary + } + }, + ), type = TangemButtonType.Secondary, shape = TangemButtonShape.Rounded, onClick = onClick, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletUM.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletUM.kt index 907a2a76fa..b331c01a9c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletUM.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletUM.kt @@ -40,6 +40,7 @@ internal sealed interface WalletUM { override val type: WalletType, override val tangemPayMainUM: TangemPayMainUM, override val virtualAccountMainUM: VirtualAccountMainUM, + val areActionsAvailable: Boolean = false, ) : WalletUM data class Locked( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetRefreshStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetRefreshStateTransformer.kt index c90b41d9bb..19c70e6c9d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetRefreshStateTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetRefreshStateTransformer.kt @@ -3,8 +3,6 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.domain.models.wallet.UserWalletId import com.tangem.feature.wallet.presentation.wallet.state.model.* -import com.tangem.feature.wallet.presentation.wallet.state.utils.disableButtons -import com.tangem.feature.wallet.presentation.wallet.state.utils.enableButtons import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.mutate @@ -36,15 +34,9 @@ internal class SetRefreshStateTransformer( override fun transform(walletUM: WalletUM): WalletUM { return when (walletUM) { is WalletUM.Content -> { - val tokensListUM = walletUM.tokensListUM.toUpdatedState() walletUM.copy( pullToRefreshConfig = walletUM.pullToRefreshConfig.toUpdatedState(isRefreshing), - tokensListUM = tokensListUM, - buttons = if (tokensListUM is WalletTokensListUM.Empty) { - walletUM.disableButtons() - } else { - walletUM.enableButtons() - }, + tokensListUM = walletUM.tokensListUM.toUpdatedState(), ) } is WalletUM.Locked -> walletUM diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListErrorTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListErrorTransformer.kt index 9d03a077a4..b165c27c45 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListErrorTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListErrorTransformer.kt @@ -12,6 +12,7 @@ import com.tangem.domain.tokens.error.TokenListError import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.state.model.* +import com.tangem.feature.wallet.presentation.wallet.state.utils.createWalletActionButtons import com.tangem.feature.wallet.presentation.wallet.state.utils.disableButtons import com.tangem.utils.logging.TangemLogger import java.math.BigDecimal @@ -64,7 +65,14 @@ internal class SetTokenListErrorTransformer( clickIntents.onAssetsDiscoveryManageClick(walletUM.walletsBalanceUM.id) }, ), - buttons = walletUM.disableButtons(), + areActionsAvailable = false, + buttons = createWalletActionButtons( + userWallet = selectedWallet, + clickIntents = clickIntents, + isAddFundsEnabled = false, + isSwapEnabled = false, + isTransferEnabled = false, + ), ) } is WalletUM.Locked -> { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt index cb7f27de52..96088f53d5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt @@ -2,6 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.common.ui.tokens.TokenConverterParams import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.models.StatusSource +import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.account.AccountStatus import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.wallet.UserWallet @@ -9,7 +11,7 @@ import com.tangem.domain.staking.model.StakingAvailability import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.state.model.* import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.* -import com.tangem.feature.wallet.presentation.wallet.state.utils.disableButtons +import com.tangem.feature.wallet.presentation.wallet.state.utils.createWalletActionButtons import com.tangem.feature.wallet.presentation.wallet.state.utils.enableButtons import com.tangem.features.tangempay.entity.TangemPayMainUM import com.tangem.features.virtualaccount.main.entity.VirtualAccountMainUM @@ -68,16 +70,21 @@ internal class SetTokenListTransformer( return when (walletUM) { is WalletUM.Content -> { val tokensListUM = toLoadedState() + val isTokensEmpty = tokensListUM is WalletTokensListUM.Empty + val areActionsAvailable = getTotalFiatBalance().areActionsAvailable() walletUM.copy( walletsBalanceUM = walletUM.walletsBalanceUM.toLoadedState2(), tangemPayMainUM = walletUM.tangemPayMainUM.toLoadedState(), virtualAccountMainUM = walletUM.virtualAccountMainUM.toLoadedVirtualState(), tokensListUM = tokensListUM, - buttons = if (tokensListUM is WalletTokensListUM.Empty) { - walletUM.disableButtons() - } else { - walletUM.enableButtons() - }, + areActionsAvailable = areActionsAvailable, + buttons = createWalletActionButtons( + userWallet = userWallet, + clickIntents = clickIntents, + isAddFundsEnabled = !isTokensEmpty, + isSwapEnabled = !isTokensEmpty && areActionsAvailable, + isTransferEnabled = !isTokensEmpty, + ), ) } is WalletUM.Locked -> { @@ -87,6 +94,14 @@ internal class SetTokenListTransformer( } } + private fun getTotalFiatBalance(): TotalFiatBalance = when (params) { + is TokenConverterParams.Account -> params.accountList.totalFiatBalance + is TokenConverterParams.Wallet -> params.tokenList.totalFiatBalance + } + + private fun TotalFiatBalance.areActionsAvailable(): Boolean = + (this as? TotalFiatBalance.Loaded)?.source != StatusSource.ONLY_CACHE + private fun WalletCardState.toLoadedState(): WalletCardState { val fiatBalance = when (params) { is TokenConverterParams.Account -> params.accountList.totalFiatBalance diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/MultiWalletActionsExt.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/MultiWalletActionsExt.kt index 2147e17885..a36279ea1c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/MultiWalletActionsExt.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/MultiWalletActionsExt.kt @@ -1,11 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.utils -import com.tangem.core.ui.ds.button.TangemButtonUM -import com.tangem.core.ui.ds.image.TangemIconUM -import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.toPersistentList @@ -17,27 +13,6 @@ internal fun WalletState.MultiCurrency.Content.disableButtons(): PersistentList< return changeAvailability(enabled = false) } -internal fun WalletUM.Content.enableButtons(): PersistentList { - return buttons.map { it.withEnabled(isEnabled = true) }.toPersistentList() -} - -internal fun WalletUM.Content.disableButtons(): PersistentList { - return buttons.map { it.withEnabled(isEnabled = false) }.toPersistentList() -} - -private fun TangemButtonUM.withEnabled(isEnabled: Boolean): TangemButtonUM { - val refreshedIcon = (tangemIconUM as? TangemIconUM.Icon)?.copy( - tint = { - if (isEnabled) { - TangemTheme.colors2.graphic.neutral.primary - } else { - TangemTheme.colors2.graphic.neutral.quaternary - } - }, - ) ?: tangemIconUM - return copy(isEnabled = isEnabled, tangemIconUM = refreshedIcon) -} - private fun WalletState.MultiCurrency.Content.changeAvailability(enabled: Boolean): PersistentList { return buttons .map { action -> diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletActionButtonsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletActionButtonsFactory.kt new file mode 100644 index 0000000000..3096798024 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletActionButtonsFactory.kt @@ -0,0 +1,43 @@ +package com.tangem.feature.wallet.presentation.wallet.state.utils + +import com.tangem.core.ui.ds.button.TangemButtonUM +import com.tangem.domain.card.common.util.cardTypesResolver +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletActionButtons +import com.tangem.utils.extensions.addIf +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.toPersistentList + +internal fun createWalletActionButtons( + userWallet: UserWallet, + clickIntents: WalletClickIntents, + isAddFundsEnabled: Boolean, + isSwapEnabled: Boolean, + isTransferEnabled: Boolean, +): PersistentList { + val isSingleWalletWithToken = userWallet is UserWallet.Cold && + userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() + + return buildList { + add( + WalletActionButtons.AddFunds( + isEnabled = isAddFundsEnabled, + onClick = { clickIntents.onAddFundsClick(userWalletId = userWallet.walletId) }, + ).buttonUM, + ) + addIf( + condition = !userWallet.isSingleWallet() && !isSingleWalletWithToken, + element = WalletActionButtons.Swap( + isEnabled = isSwapEnabled, + onClick = { clickIntents.onMultiWalletSwapClick(userWalletId = userWallet.walletId) }, + ).buttonUM, + ) + add( + WalletActionButtons.Transfer( + isEnabled = isTransferEnabled, + onClick = { clickIntents.onTransferClick(userWalletId = userWallet.walletId) }, + ).buttonUM, + ) + }.toPersistentList() +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt index edfaea0f65..faa64acc55 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt @@ -18,11 +18,9 @@ import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver import com.tangem.feature.wallet.presentation.wallet.state.model.* import com.tangem.features.tangempay.entity.TangemPayMainUM import com.tangem.features.virtualaccount.main.entity.VirtualAccountMainUM -import com.tangem.utils.extensions.addIf import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.MutableStateFlow /** @@ -174,33 +172,13 @@ internal class WalletLoadingStateFactory( } private fun createWalletActions(userWallet: UserWallet): PersistentList { - val isSingleWalletWithToken = - userWallet is UserWallet.Cold && userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() - return buildList { - add( - WalletActionButtons.AddFunds( - isEnabled = false, - onClick = { clickIntents.onAddFundsClick(userWalletId = userWallet.walletId) }, - ).buttonUM, - ) - addIf( - condition = !userWallet.isSingleWallet() && !isSingleWalletWithToken, - element = WalletActionButtons.Swap( - isEnabled = false, - onClick = { - clickIntents.onMultiWalletSwapClick(userWalletId = userWallet.walletId) - }, - ).buttonUM, - ) - add( - WalletActionButtons.Transfer( - isEnabled = false, - onClick = { - clickIntents.onTransferClick(userWalletId = userWallet.walletId) - }, - ).buttonUM, - ) - }.toPersistentList() + return createWalletActionButtons( + userWallet = userWallet, + clickIntents = clickIntents, + isAddFundsEnabled = false, + isSwapEnabled = false, + isTransferEnabled = false, + ) } private fun createDimmedButtons(): PersistentList { diff --git a/features/wallet/impl/src/test/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformerTest.kt b/features/wallet/impl/src/test/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformerTest.kt index 998b8a5db9..355749f782 100644 --- a/features/wallet/impl/src/test/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformerTest.kt +++ b/features/wallet/impl/src/test/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformerTest.kt @@ -44,19 +44,33 @@ class SetTokenListTransformerTest { val result = transformer.transform(walletUM(buttonEnabled = true)) as WalletUM.Content assertThat(result.tokensListUM).isInstanceOf(WalletTokensListUM.Empty::class.java) - assertThat(result.buttons).hasSize(1) - assertThat(result.buttons.single().isEnabled).isFalse() + assertThat(result.buttons.map { it.isEnabled }).containsExactly(false, false, false) } @Test - fun `GIVEN non-empty account list WHEN transform THEN tokens not Empty and buttons enabled`() { - val transformer = createTransformer(accountList = nonEmptyAccountList()) + fun `GIVEN actual balance WHEN transform THEN tokens not Empty and all buttons enabled`() { + val transformer = createTransformer( + accountList = nonEmptyAccountList(TotalFiatBalance.Loaded(BigDecimal.ZERO, StatusSource.ACTUAL)), + ) val result = transformer.transform(walletUM(buttonEnabled = false)) as WalletUM.Content assertThat(result.tokensListUM).isNotInstanceOf(WalletTokensListUM.Empty::class.java) - assertThat(result.buttons).hasSize(1) - assertThat(result.buttons.single().isEnabled).isTrue() + assertThat(result.areActionsAvailable).isTrue() + assertThat(result.buttons.map { it.isEnabled }).containsExactly(true, true, true) + } + + @Test + fun `GIVEN only cached balance WHEN transform THEN Swap disabled while AddFunds and Transfer enabled`() { + val transformer = createTransformer( + accountList = nonEmptyAccountList(TotalFiatBalance.Loaded(BigDecimal.ZERO, StatusSource.ONLY_CACHE)), + ) + + val result = transformer.transform(walletUM(buttonEnabled = false)) as WalletUM.Content + + assertThat(result.areActionsAvailable).isFalse() + // buttons order: AddFunds, Swap, Transfer + assertThat(result.buttons.map { it.isEnabled }).containsExactly(true, false, true) } private fun createTransformer(accountList: AccountStatusList): SetTokenListTransformer { @@ -88,11 +102,11 @@ class SetTokenListTransformerTest { groupType = TokensGroupType.NONE, ) - private fun nonEmptyAccountList(): AccountStatusList { + private fun nonEmptyAccountList(totalFiatBalance: TotalFiatBalance): AccountStatusList { val token = createToken() val tokenList = TokenList.Ungrouped( currencies = listOf(createLoadedStatus(token)), - totalFiatBalance = TotalFiatBalance.Loaded(BigDecimal.ZERO, StatusSource.ACTUAL), + totalFiatBalance = totalFiatBalance, sortedBy = TokensSortType.NONE, ) return AccountStatusList( @@ -100,7 +114,7 @@ class SetTokenListTransformerTest { accountStatuses = listOf(mainCryptoPortfolioStatus(tokenList = tokenList)), totalAccounts = 1, totalArchivedAccounts = 0, - totalFiatBalance = TotalFiatBalance.Loading, + totalFiatBalance = totalFiatBalance, sortType = TokensSortType.NONE, groupType = TokensGroupType.NONE, )