From 177176b3ee5b898af381b4adf41abb104441a544 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 7 Jul 2026 15:32:30 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../common/ui/markets/action/QuickActions.kt | 3 ++ .../markets/action/QuickActionsConverter.kt | 51 ++++++++++++++----- .../action/QuickActionsConverterTest.kt | 51 +++++++++++++++++++ .../tokens/actions/BaseActionsFactory.kt | 9 ++-- .../tokens/actions/CommonActionsFactory.kt | 2 +- .../actions/OutdatedDataActionsFactory.kt | 2 +- .../tokenactions/ui/TokenActionsContentV2.kt | 6 ++- 7 files changed, 104 insertions(+), 20 deletions(-) diff --git a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActions.kt b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActions.kt index 27d4bfac71..104176d4c6 100644 --- a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActions.kt +++ b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActions.kt @@ -1,9 +1,12 @@ package com.tangem.common.ui.markets.action import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableSet +import kotlinx.collections.immutable.persistentSetOf data class QuickActions( val actions: ImmutableList, val onQuickActionClick: (QuickActionUM) -> Unit, val onQuickActionLongClick: (QuickActionUM) -> Unit, + val disabledActions: ImmutableSet = persistentSetOf(), ) \ No newline at end of file diff --git a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverter.kt b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverter.kt index de1e9704d1..63add1f9fe 100644 --- a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverter.kt +++ b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverter.kt @@ -4,6 +4,7 @@ import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableSet object QuickActionsConverter { @@ -13,8 +14,9 @@ object QuickActionsConverter { isRedesignEnabled: Boolean, context: TokenActionsContext = TokenActionsContext.Markets, ): QuickActions { + val states = toQuickActionStates(cryptoData.actions, isRedesignEnabled, context) return QuickActions( - actions = toQuickActions(cryptoData.actions, isRedesignEnabled, context), + actions = states.map { it.action }.toImmutableList(), onQuickActionClick = { quickActionUM -> tokenActionsHandler.handle( action = quickActionUM.toHandledAction(), @@ -30,6 +32,7 @@ object QuickActionsConverter { ) } }, + disabledActions = states.filterNot { it.isEnabled }.map { it.action }.toImmutableSet(), ) } @@ -45,30 +48,52 @@ object QuickActionsConverter { } /** - * Returns available actions filtered to [context]'s allow-list and ordered by it. - * Omitting [context] (default [TokenActionsContext.Markets]) yields all available actions in source order; - * a context with a non-null [TokenActionsContext.allowedActionsInOrder] filters to and orders by that list. + * Returns actions filtered and ordered for [context]. + * Omitting [context] (default [TokenActionsContext.Markets]) yields only available actions in source order. + * A context with a non-null [TokenActionsContext.allowedActionsInOrder] returns that list's actions in order, + * including unavailable ones (they are meant to be shown disabled by the caller). */ fun toQuickActions( actions: List, isRedesignEnabled: Boolean, context: TokenActionsContext = TokenActionsContext.Markets, - ): ImmutableList { - val available = actions.filter { it.unavailabilityReason == ScenarioUnavailabilityReason.None } - val allowed = context.allowedActionsInOrder - ?: return available.mapNotNull { it.toQuickActionUM(isRedesignEnabled) }.toImmutableList() + ): ImmutableList = + toQuickActionStates(actions, isRedesignEnabled, context).map { it.action }.toImmutableList() - val byBsAction = available.associateBy { it.toBsAction() } - val hasExchange = byBsAction.containsKey(TokenActionsBSContentUM.Action.Exchange) + private fun toQuickActionStates( + actions: List, + isRedesignEnabled: Boolean, + context: TokenActionsContext, + ): List { + val allowed = context.allowedActionsInOrder + ?: return actions + .filter { it.unavailabilityReason == ScenarioUnavailabilityReason.None } + .mapNotNull { action -> + action.toQuickActionUM(isRedesignEnabled)?.let { QuickActionState(it, isEnabled = true) } + } + + val byBsAction = actions.associateBy { it.toBsAction() } + val isExchangeAvailable = byBsAction[TokenActionsBSContentUM.Action.Exchange] + ?.unavailabilityReason == ScenarioUnavailabilityReason.None return allowed.mapNotNull { action -> when (action) { TokenActionsBSContentUM.Action.SendWithSwap -> - if (hasExchange) swapAndSendUM(isRedesignEnabled) else null - else -> byBsAction[action]?.toQuickActionUM(isRedesignEnabled) + if (isExchangeAvailable) { + QuickActionState(swapAndSendUM(isRedesignEnabled), isEnabled = true) + } else { + null + } + else -> { + val state = byBsAction[action] ?: return@mapNotNull null + val um = state.toQuickActionUM(isRedesignEnabled) ?: return@mapNotNull null + QuickActionState(um, isEnabled = state.unavailabilityReason == ScenarioUnavailabilityReason.None) + } } - }.toImmutableList() + } } + private data class QuickActionState(val action: QuickActionUM, val isEnabled: Boolean) + private fun swapAndSendUM(isRedesignEnabled: Boolean): QuickActionUM = if (isRedesignEnabled) QuickActionUM.V2.SwapAndSend else QuickActionUM.V1.SwapAndSend diff --git a/common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverterTest.kt b/common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverterTest.kt index b0028b644c..d6cecd4063 100644 --- a/common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverterTest.kt +++ b/common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverterTest.kt @@ -127,4 +127,55 @@ internal class QuickActionsConverterTest { assertThat(result).doesNotContain(QuickActionUM.V2.SwapAndSend) assertThat(result).doesNotContain(QuickActionUM.V2.Exchange(shouldShowBadge = false)) } + + @Test + fun `GIVEN buy and swap unavailable WHEN context is AddFunds THEN they are still shown (disabled) not hidden`() { + // Arrange + val actions = listOf( + TokenActionsState.ActionState.Buy(ScenarioUnavailabilityReason.BuyUnavailable(cryptoCurrencyName = "BTC")), + TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.SingleWallet, shouldShowBadge = false), + TokenActionsState.ActionState.Receive(ScenarioUnavailabilityReason.None), + ) + + // Act + val result = QuickActionsConverter.toQuickActions( + actions = actions, + isRedesignEnabled = true, + context = TokenActionsContext.AddFunds, + ) + + // Assert + assertThat(result).containsExactly( + QuickActionUM.V2.Buy, + QuickActionUM.V2.Exchange(shouldShowBadge = false), + QuickActionUM.V2.Receive, + ).inOrder() + } + + @Test + fun `GIVEN swap and sell unavailable WHEN context is Transfer THEN swap and sell shown disabled and no swapAndSend`() { + // Arrange + val actions = listOf( + TokenActionsState.ActionState.Send(ScenarioUnavailabilityReason.None), + TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.SingleWallet, shouldShowBadge = false), + TokenActionsState.ActionState.Sell( + ScenarioUnavailabilityReason.NotSupportedBySellService(cryptoCurrencyName = "BTC"), + ), + ) + + // Act + val result = QuickActionsConverter.toQuickActions( + actions = actions, + isRedesignEnabled = true, + context = TokenActionsContext.Transfer, + ) + + // Assert + assertThat(result).containsExactly( + QuickActionUM.V2.Send, + QuickActionUM.V2.Exchange(shouldShowBadge = false), + QuickActionUM.V2.Sell, + ).inOrder() + assertThat(result).doesNotContain(QuickActionUM.V2.SwapAndSend) + } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt index 75b406ee1b..6c821b7893 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/BaseActionsFactory.kt @@ -104,17 +104,20 @@ internal open class BaseActionsFactory( /** * Determines the unavailability reason for the SELL action * - * @param userWalletId the ID of the user's wallet + * @param userWallet the user's wallet * @param status the status of the cryptocurrency * @param sendUnavailabilityReason the reason for unavailability of the send action */ protected suspend fun getSellUnavailabilityReason( - userWalletId: UserWalletId, + userWallet: UserWallet, status: CryptoCurrencyStatus, sendUnavailabilityReason: ScenarioUnavailabilityReason, ): ScenarioUnavailabilityReason { + if (userWallet is UserWallet.Cold && userWallet.cardTypesResolver.isStart2Coin()) { + return ScenarioUnavailabilityReason.NotSupportedBySellService(status.currency.name) + } return rampStateManager.availableForSell( - userWalletId = userWalletId, + userWalletId = userWallet.walletId, status = status, sendUnavailabilityReason = sendUnavailabilityReason, ).fold( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt index 4d9837958f..f158738630 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/CommonActionsFactory.kt @@ -104,7 +104,7 @@ internal class CommonActionsFactory( // region Sell val sellUnavailabilityReason = getSellUnavailabilityReason( - userWalletId = userWallet.walletId, + userWallet = userWallet, status = cryptoCurrencyStatus, sendUnavailabilityReason = sendUnavailabilityReason, ) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt index 49084a3a21..f432fc0495 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/actions/OutdatedDataActionsFactory.kt @@ -120,7 +120,7 @@ internal class OutdatedDataActionsFactory( // region Sell if (sendUnavailabilityReason == ScenarioUnavailabilityReason.None) { val sellUnavailabilityReason = getSellUnavailabilityReason( - userWalletId = userWallet.walletId, + userWallet = userWallet, status = cryptoCurrencyStatus, sendUnavailabilityReason = sendUnavailabilityReason, ) diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/ui/TokenActionsContentV2.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/ui/TokenActionsContentV2.kt index 20c555716f..bf4f21300b 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/ui/TokenActionsContentV2.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/ui/TokenActionsContentV2.kt @@ -127,14 +127,16 @@ private fun QuickActionsList(state: TokenActionsUM, modifier: Modifier = Modifie Modifier.testTag(TokenActionsTestTags.BUY_ACTION) else -> Modifier } + val isEnabled = actionUM !in state.quickActions.disabledActions TokenActionRow( modifier = actionModifier, iconRes = actionUM.icon, title = actionUM.title, description = actionUM.description, - onClick = { state.quickActions.onQuickActionClick(actionUM) }, + isEnabled = isEnabled, + onClick = { state.quickActions.onQuickActionClick(actionUM) }.takeIf { isEnabled }, onLongClick = { state.quickActions.onQuickActionLongClick(actionUM) } - .takeIf { actionUM.isLongClickAvailable }, + .takeIf { actionUM.isLongClickAvailable && isEnabled }, ) } }