From 22dc2eb3e93085f1fcbc7e940e3cc5068e798bd2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 19 Jun 2026 20:01:38 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../tangem/tap/routing/utils/ChildFactory.kt | 1 + .../com/tangem/common/routing/AppRoute.kt | 1 + common/ui-markets/build.gradle.kts | 4 + .../common/ui/markets/action/QuickActionUM.kt | 36 +++++ .../markets/action/QuickActionsConverter.kt | 152 +++++++++--------- .../markets/action/TokenActionsBSContentUM.kt | 4 + .../ui/markets/action/TokenActionsContext.kt | 32 ++++ .../ui/markets/action/TokenActionsHandler.kt | 57 +++++-- .../action/QuickActionsConverterTest.kt | 130 +++++++++++++++ .../markets/action/TokenActionsContextTest.kt | 35 ++++ .../action/TokenActionsHandlerSwapTest.kt | 104 ++++++++++++ .../configs/feature_toggles_config.json | 4 + core/res/src/main/res/values/strings.xml | 2 + .../api/choosetoken/ChooseTokenBridge.kt | 7 + .../ManageFundsComponent.kt} | 9 +- .../addfunds/di/AddFundsComponentModule.kt | 16 -- .../addfunds/model/AddFundsRouteUiSpec.kt | 35 ---- .../DefaultManageFundsComponent.kt} | 87 +++++----- .../analytics/ManageFundsAnalyticsEvent.kt} | 12 +- .../di/ManageFundsComponentModule.kt | 16 ++ .../di/ManageFundsModelModule.kt} | 10 +- .../model/ManageFundsModel.kt} | 38 +++-- .../model/ManageFundsRouteUiSpec.kt | 38 +++++ .../tokenactions/TokenActionsComponent.kt | 2 + .../model/TokenActionsUiBuilder.kt | 2 + .../feed/components/FeedEntryChildFactory.kt | 5 +- .../DefaultMarketsTokenDetailsComponent.kt | 10 +- .../send/api/SendEntryPointComponent.kt | 1 + .../DefaultSendEntryPointComponent.kt | 5 + .../v2/impl/amount/model/SwapAmountModel.kt | 33 +++- .../TokenDetailsFeatureToggles.kt | 1 + .../DefaultTokenDetailsComponent.kt | 10 +- .../DefaultTokenDetailsFeatureToggles.kt | 5 +- .../model/TokenDetailsClickIntents.kt | 4 + .../tokendetails/model/TokenDetailsModel.kt | 23 +++ .../tokendetails/state/TransferUM.kt | 1 + .../transformer/UpdateTransferTransformer.kt | 17 +- .../bottomsheet/TransferBottomSheetContent.kt | 17 ++ .../UpdateTransferTransformerTest.kt | 51 ++++++ .../featuretoggles/WalletFeatureToggles.kt | 2 + .../wallet/child/wallet/WalletComponent.kt | 20 ++- .../wallet/child/wallet/model/WalletModel.kt | 5 + .../model/intents/WalletClickIntents.kt | 4 + .../DefaultWalletFeatureToggles.kt | 3 + .../router/DefaultWalletRouter.kt | 6 + .../presentation/router/InnerWalletRouter.kt | 3 + .../wallet/state/model/WalletActionButtons.kt | 10 +- .../wallet/state/model/WalletDialogConfig.kt | 3 + .../wallet/state/model/WalletManageButton.kt | 25 ++- .../transformers/AddWalletTransformer.kt | 2 + .../InitializeWalletsTransformer.kt | 32 +++- .../ReinitializeNewWalletTransformer.kt | 3 + .../ReinitializeWalletTransformer.kt | 2 + .../SetRefreshStateTransformer.kt | 1 + .../transformers/UnlockWalletTransformer.kt | 2 + .../state/utils/MultiWalletActionsExt.kt | 1 + .../state/utils/WalletLoadingStateFactory.kt | 48 ++++-- 57 files changed, 931 insertions(+), 258 deletions(-) create mode 100644 common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsContext.kt create mode 100644 common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverterTest.kt create mode 100644 common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/TokenActionsContextTest.kt create mode 100644 common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/TokenActionsHandlerSwapTest.kt rename features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/{addfunds/AddFundsComponent.kt => managefunds/ManageFundsComponent.kt} (70%) delete mode 100644 features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/di/AddFundsComponentModule.kt delete mode 100644 features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/model/AddFundsRouteUiSpec.kt rename features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/{addfunds/DefaultAddFundsComponent.kt => managefunds/DefaultManageFundsComponent.kt} (68%) rename features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/{addfunds/analytics/AddFundsAnalyticsEvent.kt => managefunds/analytics/ManageFundsAnalyticsEvent.kt} (53%) create mode 100644 features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/di/ManageFundsComponentModule.kt rename features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/{addfunds/di/AddFundsModelModule.kt => managefunds/di/ManageFundsModelModule.kt} (52%) rename features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/{addfunds/model/AddFundsModel.kt => managefunds/model/ManageFundsModel.kt} (86%) create mode 100644 features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/model/ManageFundsRouteUiSpec.kt diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 1e1942f867..c43ead18f2 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -628,6 +628,7 @@ internal class ChildFactory @Inject constructor( params = SendEntryPointComponent.Params( userWalletId = route.userWalletId, cryptoCurrency = route.currency, + shouldStartWithSwap = route.shouldStartWithSwap, ), componentFactory = sendEntryPointComponentFactory, ) diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index 12df52d117..3b46824c4d 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -442,6 +442,7 @@ sealed class AppRoute(val path: String) : Route { data class SendEntryPoint( val userWalletId: UserWalletId, val currency: CryptoCurrency, + val shouldStartWithSwap: Boolean = false, ) : AppRoute( path = "/send_entry_point/${userWalletId.stringValue}/${currency.id.value}?", ) diff --git a/common/ui-markets/build.gradle.kts b/common/ui-markets/build.gradle.kts index c665f48b03..4155090cee 100644 --- a/common/ui-markets/build.gradle.kts +++ b/common/ui-markets/build.gradle.kts @@ -44,4 +44,8 @@ dependencies { /** DI */ implementation(deps.hilt.android) kapt(deps.hilt.kapt) + + /** Test */ + testImplementation(projects.test.core) + testImplementation(projects.common.test) } \ No newline at end of file diff --git a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionUM.kt b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionUM.kt index 88b65e767a..bc41c68a0f 100644 --- a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionUM.kt +++ b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionUM.kt @@ -60,6 +60,24 @@ sealed class QuickActionUM( description = resourceReference(R.string.yield_module_main_screen_promo_banner_message, wrappedList(apy)), icon = R.drawable.ic_analytics_up_mini_24, ) + + data object Send : V1( + title = resourceReference(R.string.common_send), + description = resourceReference(R.string.quick_action_send_description), + icon = R.drawable.ic_arrow_up_24, + ) + + data object Sell : V1( + title = resourceReference(R.string.common_sell), + description = resourceReference(R.string.quick_action_sell_description), + icon = R.drawable.ic_currency_24, + ) + + data object SwapAndSend : V1( + title = resourceReference(R.string.common_send_with_swap), + description = resourceReference(R.string.quick_action_send_and_swap_description), + icon = R.drawable.ic_exchange_mini_24, + ) } sealed class V2( @@ -107,5 +125,23 @@ sealed class QuickActionUM( description = resourceReference(R.string.yield_module_main_screen_promo_banner_message, wrappedList(apy)), icon = R.drawable.ic_analytics_up_mini_24, ) + + data object Send : V2( + title = resourceReference(R.string.common_send), + description = resourceReference(R.string.quick_action_send_description), + icon = R.drawable.ic_arrow_up_24, + ) + + data object Sell : V2( + title = resourceReference(R.string.common_sell), + description = resourceReference(R.string.quick_action_sell_description), + icon = R.drawable.ic_currency_24, + ) + + data object SwapAndSend : V2( + title = resourceReference(R.string.common_send_with_swap), + description = resourceReference(R.string.quick_action_send_and_swap_description), + icon = R.drawable.ic_exchange_mini_24, + ) } } \ 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 47f53be359..de1e9704d1 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 @@ -11,52 +11,16 @@ object QuickActionsConverter { cryptoData: CryptoCurrencyData, tokenActionsHandler: TokenActionsHandler, isRedesignEnabled: Boolean, + context: TokenActionsContext = TokenActionsContext.Markets, ): QuickActions { return QuickActions( - actions = toQuickActions(cryptoData.actions, isRedesignEnabled), + actions = toQuickActions(cryptoData.actions, isRedesignEnabled, context), onQuickActionClick = { quickActionUM -> - when (quickActionUM) { - QuickActionUM.V1.Buy -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Buy, - cryptoCurrencyData = cryptoData, - ) - is QuickActionUM.V1.Exchange -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Exchange, - cryptoCurrencyData = cryptoData, - ) - QuickActionUM.V1.Receive -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Receive, - cryptoCurrencyData = cryptoData, - ) - QuickActionUM.V1.Stake -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Stake, - cryptoCurrencyData = cryptoData, - ) - is QuickActionUM.V1.YieldMode -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.YieldMode, - cryptoCurrencyData = cryptoData, - ) - QuickActionUM.V2.Buy -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Buy, - cryptoCurrencyData = cryptoData, - ) - is QuickActionUM.V2.Exchange -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Exchange, - cryptoCurrencyData = cryptoData, - ) - QuickActionUM.V2.Receive -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Receive, - cryptoCurrencyData = cryptoData, - ) - QuickActionUM.V2.Stake -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.Stake, - cryptoCurrencyData = cryptoData, - ) - is QuickActionUM.V2.YieldMode -> tokenActionsHandler.handle( - action = TokenActionsBSContentUM.Action.YieldMode, - cryptoCurrencyData = cryptoData, - ) - } + tokenActionsHandler.handle( + action = quickActionUM.toHandledAction(), + cryptoCurrencyData = cryptoData, + context = context, + ) }, onQuickActionLongClick = { actionUM -> if (actionUM == QuickActionUM.V1.Receive || actionUM == QuickActionUM.V2.Receive) { @@ -69,44 +33,78 @@ object QuickActionsConverter { ) } - fun toQuickActions(actions: List, isRedesignEnabled: Boolean) = - if (isRedesignEnabled) { - redesignedQuickActions(actions) - } else { - legacyQuickActions(actions) - } + private fun QuickActionUM.toHandledAction(): TokenActionsBSContentUM.Action = when (this) { + QuickActionUM.V1.Buy, QuickActionUM.V2.Buy -> TokenActionsBSContentUM.Action.Buy + is QuickActionUM.V1.Exchange, is QuickActionUM.V2.Exchange -> TokenActionsBSContentUM.Action.Exchange + QuickActionUM.V1.Receive, QuickActionUM.V2.Receive -> TokenActionsBSContentUM.Action.Receive + QuickActionUM.V1.Stake, QuickActionUM.V2.Stake -> TokenActionsBSContentUM.Action.Stake + is QuickActionUM.V1.YieldMode, is QuickActionUM.V2.YieldMode -> TokenActionsBSContentUM.Action.YieldMode + QuickActionUM.V1.Send, QuickActionUM.V2.Send -> TokenActionsBSContentUM.Action.Send + QuickActionUM.V1.Sell, QuickActionUM.V2.Sell -> TokenActionsBSContentUM.Action.Sell + QuickActionUM.V1.SwapAndSend, QuickActionUM.V2.SwapAndSend -> TokenActionsBSContentUM.Action.SendWithSwap + } - private fun redesignedQuickActions(actions: List): ImmutableList { - return buildList { - actions.forEach { action -> - if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) { - when (action) { - is TokenActionsState.ActionState.Buy -> QuickActionUM.V2.Buy - is TokenActionsState.ActionState.Swap -> QuickActionUM.V2.Exchange(action.shouldShowBadge) - is TokenActionsState.ActionState.Receive -> QuickActionUM.V2.Receive - is TokenActionsState.ActionState.Stake -> QuickActionUM.V2.Stake - is TokenActionsState.ActionState.YieldMode -> QuickActionUM.V2.YieldMode(action.apy) - else -> null - }?.let(::add) - } + /** + * 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. + */ + 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() + + val byBsAction = available.associateBy { it.toBsAction() } + val hasExchange = byBsAction.containsKey(TokenActionsBSContentUM.Action.Exchange) + return allowed.mapNotNull { action -> + when (action) { + TokenActionsBSContentUM.Action.SendWithSwap -> + if (hasExchange) swapAndSendUM(isRedesignEnabled) else null + else -> byBsAction[action]?.toQuickActionUM(isRedesignEnabled) } }.toImmutableList() } - private fun legacyQuickActions(actions: List): ImmutableList { - return buildList { - actions.forEach { action -> - if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) { - when (action) { - is TokenActionsState.ActionState.Buy -> QuickActionUM.V1.Buy - is TokenActionsState.ActionState.Swap -> QuickActionUM.V1.Exchange(action.shouldShowBadge) - is TokenActionsState.ActionState.Receive -> QuickActionUM.V1.Receive - is TokenActionsState.ActionState.Stake -> QuickActionUM.V1.Stake - is TokenActionsState.ActionState.YieldMode -> QuickActionUM.V1.YieldMode(action.apy) - else -> null - }?.let(::add) - } - } - }.toImmutableList() + private fun swapAndSendUM(isRedesignEnabled: Boolean): QuickActionUM = + if (isRedesignEnabled) QuickActionUM.V2.SwapAndSend else QuickActionUM.V1.SwapAndSend + + private fun TokenActionsState.ActionState.toQuickActionUM(isRedesignEnabled: Boolean): QuickActionUM? = + if (isRedesignEnabled) toV2() else toV1() + + private fun TokenActionsState.ActionState.toV2(): QuickActionUM? = when (this) { + is TokenActionsState.ActionState.Buy -> QuickActionUM.V2.Buy + is TokenActionsState.ActionState.Swap -> QuickActionUM.V2.Exchange(shouldShowBadge) + is TokenActionsState.ActionState.Receive -> QuickActionUM.V2.Receive + is TokenActionsState.ActionState.Send -> QuickActionUM.V2.Send + is TokenActionsState.ActionState.Sell -> QuickActionUM.V2.Sell + is TokenActionsState.ActionState.Stake -> QuickActionUM.V2.Stake + is TokenActionsState.ActionState.YieldMode -> QuickActionUM.V2.YieldMode(apy) + else -> null + } + + private fun TokenActionsState.ActionState.toV1(): QuickActionUM? = when (this) { + is TokenActionsState.ActionState.Buy -> QuickActionUM.V1.Buy + is TokenActionsState.ActionState.Swap -> QuickActionUM.V1.Exchange(shouldShowBadge) + is TokenActionsState.ActionState.Receive -> QuickActionUM.V1.Receive + is TokenActionsState.ActionState.Send -> QuickActionUM.V1.Send + is TokenActionsState.ActionState.Sell -> QuickActionUM.V1.Sell + is TokenActionsState.ActionState.Stake -> QuickActionUM.V1.Stake + is TokenActionsState.ActionState.YieldMode -> QuickActionUM.V1.YieldMode(apy) + else -> null + } + + private fun TokenActionsState.ActionState.toBsAction(): TokenActionsBSContentUM.Action? = when (this) { + is TokenActionsState.ActionState.Buy -> TokenActionsBSContentUM.Action.Buy + is TokenActionsState.ActionState.Swap -> TokenActionsBSContentUM.Action.Exchange + is TokenActionsState.ActionState.Receive -> TokenActionsBSContentUM.Action.Receive + is TokenActionsState.ActionState.Send -> TokenActionsBSContentUM.Action.Send + is TokenActionsState.ActionState.Sell -> TokenActionsBSContentUM.Action.Sell + is TokenActionsState.ActionState.Stake -> TokenActionsBSContentUM.Action.Stake + is TokenActionsState.ActionState.YieldMode -> TokenActionsBSContentUM.Action.YieldMode + else -> null } } \ No newline at end of file diff --git a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsBSContentUM.kt b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsBSContentUM.kt index f33fee7c3d..bcbc87b589 100644 --- a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsBSContentUM.kt +++ b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsBSContentUM.kt @@ -43,6 +43,10 @@ data class TokenActionsBSContentUM( text = resourceReference(R.string.common_exchange), iconRes = R.drawable.ic_exchange_horizontal_24, ), + SendWithSwap( + text = resourceReference(R.string.common_send_with_swap), + iconRes = R.drawable.ic_exchange_horizontal_24, + ), Stake( text = resourceReference(R.string.common_stake), iconRes = R.drawable.ic_staking_24, diff --git a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsContext.kt b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsContext.kt new file mode 100644 index 0000000000..15b9d04654 --- /dev/null +++ b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsContext.kt @@ -0,0 +1,32 @@ +package com.tangem.common.ui.markets.action + +import com.tangem.common.routing.AppRoute +import com.tangem.common.ui.markets.action.TokenActionsBSContentUM.Action + +/** + * Drives how the shared token-actions bottom sheet behaves per entry point. + * + * @property allowedActionsInOrder explicit allow-list + display order; `null` means "all actions + * in source order" (legacy Markets behaviour). + * @property swapPosition the [AppRoute.Swap.CurrencyPosition] used when the Swap/Exchange action is + * launched from this context. + */ +enum class TokenActionsContext( + val allowedActionsInOrder: List?, + val swapPosition: AppRoute.Swap.CurrencyPosition, +) { + Markets( + allowedActionsInOrder = null, + swapPosition = AppRoute.Swap.CurrencyPosition.ANY, + ), + AddFunds( + allowedActionsInOrder = listOf(Action.Buy, Action.Exchange, Action.Receive), + swapPosition = AppRoute.Swap.CurrencyPosition.TO, + ), + Transfer( + // Action.SendWithSwap has no TokenActionsState.ActionState counterpart and is never produced from the + // domain action list; it is injected as a synthetic row by the converter when Swap is available. + allowedActionsInOrder = listOf(Action.Send, Action.Exchange, Action.SendWithSwap, Action.Sell), + swapPosition = AppRoute.Swap.CurrencyPosition.FROM, + ), +} \ No newline at end of file diff --git a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsHandler.kt b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsHandler.kt index 1680b7253d..3b59bc5471 100644 --- a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsHandler.kt +++ b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/TokenActionsHandler.kt @@ -51,7 +51,11 @@ class TokenActionsHandler @AssistedInject constructor( add(TokenActionsBSContentUM.Action.Sell) } - fun handle(action: TokenActionsBSContentUM.Action, cryptoCurrencyData: CryptoCurrencyData) { + fun handle( + action: TokenActionsBSContentUM.Action, + cryptoCurrencyData: CryptoCurrencyData, + context: TokenActionsContext = TokenActionsContext.Markets, + ) { if (isTopUpBlockedByBackupError(action, cryptoCurrencyData.userWallet)) return onHandleQuickAction( @@ -59,25 +63,37 @@ class TokenActionsHandler @AssistedInject constructor( action = action, cryptoCurrencyData = cryptoCurrencyData, ), - when (action) { - TokenActionsBSContentUM.Action.Receive, - TokenActionsBSContentUM.Action.CopyAddress, - TokenActionsBSContentUM.Action.Sell, - -> false - TokenActionsBSContentUM.Action.Send, - TokenActionsBSContentUM.Action.Stake, - TokenActionsBSContentUM.Action.YieldMode, - TokenActionsBSContentUM.Action.Buy, - TokenActionsBSContentUM.Action.Exchange, - -> true - }, + action.shouldDismissBottomSheet(), ) val userWallet = cryptoCurrencyData.userWallet if (userWallet is UserWallet.Cold && handleDemoMode(action, userWallet)) return + dispatchAction(action, cryptoCurrencyData, context) + } + + private fun TokenActionsBSContentUM.Action.shouldDismissBottomSheet(): Boolean = when (this) { + TokenActionsBSContentUM.Action.Receive, + TokenActionsBSContentUM.Action.CopyAddress, + TokenActionsBSContentUM.Action.Sell, + -> false + TokenActionsBSContentUM.Action.Send, + TokenActionsBSContentUM.Action.Stake, + TokenActionsBSContentUM.Action.YieldMode, + TokenActionsBSContentUM.Action.Buy, + TokenActionsBSContentUM.Action.Exchange, + TokenActionsBSContentUM.Action.SendWithSwap, + -> true + } + + private fun dispatchAction( + action: TokenActionsBSContentUM.Action, + cryptoCurrencyData: CryptoCurrencyData, + context: TokenActionsContext, + ) { when (action) { TokenActionsBSContentUM.Action.Buy -> onBuyClick(cryptoCurrencyData) - TokenActionsBSContentUM.Action.Exchange -> onExchangeClick(cryptoCurrencyData) + TokenActionsBSContentUM.Action.Exchange -> onExchangeClick(cryptoCurrencyData, context) + TokenActionsBSContentUM.Action.SendWithSwap -> onSwapAndSendClick(cryptoCurrencyData) TokenActionsBSContentUM.Action.Receive -> Unit TokenActionsBSContentUM.Action.CopyAddress -> onCopyAddress(cryptoCurrencyData) TokenActionsBSContentUM.Action.Sell -> onSellClick(cryptoCurrencyData) @@ -155,12 +171,13 @@ class TokenActionsHandler @AssistedInject constructor( } } - private fun onExchangeClick(cryptoCurrencyData: CryptoCurrencyData) { + private fun onExchangeClick(cryptoCurrencyData: CryptoCurrencyData, context: TokenActionsContext) { router.push( AppRoute.Swap( fromCryptoCurrency = cryptoCurrencyData.status.currency, userWalletId = cryptoCurrencyData.userWallet.walletId, screenSource = AnalyticsParam.ScreensSources.Markets.value, + fromCurrencyPosition = context.swapPosition, ), ) } @@ -173,6 +190,16 @@ class TokenActionsHandler @AssistedInject constructor( router.push(route) } + private fun onSwapAndSendClick(cryptoCurrencyData: CryptoCurrencyData) { + router.push( + AppRoute.SendEntryPoint( + userWalletId = cryptoCurrencyData.userWallet.walletId, + currency = cryptoCurrencyData.status.currency, + shouldStartWithSwap = true, + ), + ) + } + private fun onStakeClick(cryptoCurrencyData: CryptoCurrencyData) { val option = cryptoCurrencyData.actions.firstOrNull { it is TokenActionsState.ActionState.Stake } ?.let { it as TokenActionsState.ActionState.Stake } 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 new file mode 100644 index 0000000000..b0028b644c --- /dev/null +++ b/common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverterTest.kt @@ -0,0 +1,130 @@ +package com.tangem.common.ui.markets.action + +import com.google.common.truth.Truth.assertThat +import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason +import com.tangem.domain.tokens.model.TokenActionsState +import org.junit.jupiter.api.Test + +internal class QuickActionsConverterTest { + + @Test + fun `GIVEN actions in source order WHEN context is AddFunds THEN buy exchange receive in order with no stake`() { + // Arrange + val actions = listOf( + TokenActionsState.ActionState.Receive(ScenarioUnavailabilityReason.None), + TokenActionsState.ActionState.Stake(ScenarioUnavailabilityReason.None, option = null), + TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.None, shouldShowBadge = false), + TokenActionsState.ActionState.Buy(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() + assertThat(result).doesNotContain(QuickActionUM.V2.Stake) + } + + @Test + fun `GIVEN receive and stake WHEN context is Markets THEN preserves source order and keeps stake`() { + // Arrange + val actions = listOf( + TokenActionsState.ActionState.Receive(ScenarioUnavailabilityReason.None), + TokenActionsState.ActionState.Stake(ScenarioUnavailabilityReason.None, option = null), + ) + + // Act + val result = QuickActionsConverter.toQuickActions( + actions = actions, + isRedesignEnabled = true, + context = TokenActionsContext.Markets, + ) + + // Assert + assertThat(result).containsExactly( + QuickActionUM.V2.Receive, + QuickActionUM.V2.Stake, + ).inOrder() + } + + @Test + fun `GIVEN buy with unavailability reason mixed with available actions WHEN toQuickActions THEN unavailable action is excluded`() { + // Arrange + val actions = listOf( + TokenActionsState.ActionState.Buy(ScenarioUnavailabilityReason.BuyUnavailable(cryptoCurrencyName = "BTC")), + TokenActionsState.ActionState.Receive(ScenarioUnavailabilityReason.None), + TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.None, shouldShowBadge = false), + ) + + // Act + val result = QuickActionsConverter.toQuickActions( + actions = actions, + isRedesignEnabled = true, + context = TokenActionsContext.Markets, + ) + + // Assert + assertThat(result).containsExactly( + QuickActionUM.V2.Receive, + QuickActionUM.V2.Exchange(shouldShowBadge = false), + ).inOrder() + assertThat(result).doesNotContain(QuickActionUM.V2.Buy) + } + + @Test + fun `GIVEN send swap sell available WHEN context is Transfer THEN send exchange swapAndSend sell in order`() { + // Arrange + val actions = listOf( + TokenActionsState.ActionState.Send(ScenarioUnavailabilityReason.None), + TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.None, shouldShowBadge = false), + TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.None), + ) + + // 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.SwapAndSend, + QuickActionUM.V2.Sell, + ).inOrder() + } + + @Test + fun `GIVEN send and sell but no swap WHEN context is Transfer THEN only send and sell with no swapAndSend`() { + // Arrange + val actions = listOf( + TokenActionsState.ActionState.Send(ScenarioUnavailabilityReason.None), + TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.None), + ) + + // Act + val result = QuickActionsConverter.toQuickActions( + actions = actions, + isRedesignEnabled = true, + context = TokenActionsContext.Transfer, + ) + + // Assert + assertThat(result).containsExactly( + QuickActionUM.V2.Send, + QuickActionUM.V2.Sell, + ).inOrder() + assertThat(result).doesNotContain(QuickActionUM.V2.SwapAndSend) + assertThat(result).doesNotContain(QuickActionUM.V2.Exchange(shouldShowBadge = false)) + } +} \ No newline at end of file diff --git a/common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/TokenActionsContextTest.kt b/common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/TokenActionsContextTest.kt new file mode 100644 index 0000000000..4e97c83e41 --- /dev/null +++ b/common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/TokenActionsContextTest.kt @@ -0,0 +1,35 @@ +package com.tangem.common.ui.markets.action + +import com.google.common.truth.Truth.assertThat +import com.tangem.common.routing.AppRoute +import com.tangem.common.ui.markets.action.TokenActionsBSContentUM.Action +import org.junit.jupiter.api.Test + +internal class TokenActionsContextTest { + + @Test + fun `GIVEN AddFunds context WHEN allowedActionsInOrder THEN Buy Swap Receive`() { + // Act + val actual = TokenActionsContext.AddFunds.allowedActionsInOrder + + // Assert + assertThat(actual).containsExactly(Action.Buy, Action.Exchange, Action.Receive).inOrder() + } + + @Test + fun `GIVEN AddFunds context WHEN swapPosition THEN TO`() { + assertThat(TokenActionsContext.AddFunds.swapPosition) + .isEqualTo(AppRoute.Swap.CurrencyPosition.TO) + } + + @Test + fun `GIVEN Transfer context WHEN swapPosition THEN FROM`() { + assertThat(TokenActionsContext.Transfer.swapPosition) + .isEqualTo(AppRoute.Swap.CurrencyPosition.FROM) + } + + @Test + fun `GIVEN Markets context WHEN allowedActionsInOrder THEN null meaning all`() { + assertThat(TokenActionsContext.Markets.allowedActionsInOrder).isNull() + } +} \ No newline at end of file diff --git a/common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/TokenActionsHandlerSwapTest.kt b/common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/TokenActionsHandlerSwapTest.kt new file mode 100644 index 0000000000..280aee77db --- /dev/null +++ b/common/ui-markets/src/test/kotlin/com/tangem/common/ui/markets/action/TokenActionsHandlerSwapTest.kt @@ -0,0 +1,104 @@ +package com.tangem.common.ui.markets.action + +import com.google.common.truth.Truth.assertThat +import com.tangem.common.routing.AppRoute +import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory +import com.tangem.core.decompose.navigation.Route +import com.tangem.core.decompose.navigation.Router +import com.tangem.domain.models.account.AccountStatus +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.utils.Provider +import io.mockk.every +import io.mockk.mockk +import io.mockk.slot +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import org.junit.jupiter.api.Test + +@OptIn(ExperimentalCoroutinesApi::class) +internal class TokenActionsHandlerSwapTest { + + private val router: Router = mockk(relaxed = true) + + private val handler = TokenActionsHandler( + router = router, + clipboardManager = mockk(relaxed = true), + uiMessageSender = mockk(relaxed = true), + getOfframpUrlUseCase = mockk(relaxed = true), + urlOpener = mockk(relaxed = true), + analyticsEventHandler = mockk(relaxed = true), + currentAppCurrency = Provider { mockk(relaxed = true) }, + onHandleQuickAction = { _, _ -> }, + coroutineScope = CoroutineScope(UnconfinedTestDispatcher()), + isDemoCardUseCase = mockk(relaxed = true), + isWalletBackupProblematicUseCase = mockk(relaxed = true), + sendBackupProblemEmailUseCase = mockk(relaxed = true), + messageSender = mockk(relaxed = true), + ) + + private val userWallet: UserWallet = mockk(relaxed = true) + private val currency: CryptoCurrency = MockCryptoCurrencyFactory().ethereum + private val data = CryptoCurrencyData( + userWallet = userWallet, + status = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading), + actions = emptyList(), + isAccountMode = false, + account = mockk(relaxed = true), + ) + + @Test + fun `GIVEN AddFunds context WHEN handle Exchange THEN swap pushed with TO position`() { + // Arrange + val slot = slot() + every { router.push(capture(slot), any()) } returns Unit + + // Act + handler.handle( + action = TokenActionsBSContentUM.Action.Exchange, + cryptoCurrencyData = data, + context = TokenActionsContext.AddFunds, + ) + + // Assert + val pushed = slot.captured as AppRoute.Swap + assertThat(pushed.fromCurrencyPosition).isEqualTo(AppRoute.Swap.CurrencyPosition.TO) + } + + @Test + fun `GIVEN Transfer context WHEN handle Exchange THEN swap pushed with FROM position`() { + // Arrange + val slot = slot() + every { router.push(capture(slot), any()) } returns Unit + + // Act + handler.handle( + action = TokenActionsBSContentUM.Action.Exchange, + cryptoCurrencyData = data, + context = TokenActionsContext.Transfer, + ) + + // Assert + val pushed = slot.captured as AppRoute.Swap + assertThat(pushed.fromCurrencyPosition).isEqualTo(AppRoute.Swap.CurrencyPosition.FROM) + } + + @Test + fun `GIVEN default context WHEN handle Exchange THEN swap pushed with ANY position`() { + // Arrange + val slot = slot() + every { router.push(capture(slot), any()) } returns Unit + + // Act + handler.handle( + action = TokenActionsBSContentUM.Action.Exchange, + cryptoCurrencyData = data, + ) + + // Assert + val pushed = slot.captured as AppRoute.Swap + assertThat(pushed.fromCurrencyPosition).isEqualTo(AppRoute.Swap.CurrencyPosition.ANY) + } +} \ No newline at end of file diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json index 71a0bd9d5d..e0464082f0 100644 --- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json @@ -87,6 +87,10 @@ "name": "AND_15310_ADD_FUNDS_STAGE1", "version": "5.39" }, + { + "name": "TWI_1377_MANAGE_FUNDS", + "version": "5.40" + }, { "name": "AND_15009_SWAP_PROVIDER_FILTER_ENABLED", "version": "5.39" diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index bfac57f269..3e4488d270 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -332,6 +332,7 @@ Error Top-up network fee Swap + Send&Swap Explore Explore transaction history Explorer @@ -1294,6 +1295,7 @@ Sell crypto securely Send to another wallet Between your portfolios + Send with swap to another token Other Quick top up No memo required diff --git a/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/choosetoken/ChooseTokenBridge.kt b/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/choosetoken/ChooseTokenBridge.kt index e62479cdbf..0ba2e187cf 100644 --- a/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/choosetoken/ChooseTokenBridge.kt +++ b/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/choosetoken/ChooseTokenBridge.kt @@ -55,6 +55,13 @@ interface ChooseTokenBridge : ChooseTokenBridgeInternal { isAppBarShown = false, isShowSingleCurrencyWallets = true, ) + val Transfer = Settings( + title = resourceReference(R.string.common_transfer), + isShowMarketBlock = false, + isShowPaymentAccount = false, + isAppBarShown = false, + isShowSingleCurrencyWallets = true, + ) } } diff --git a/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/addfunds/AddFundsComponent.kt b/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/managefunds/ManageFundsComponent.kt similarity index 70% rename from features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/addfunds/AddFundsComponent.kt rename to features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/managefunds/ManageFundsComponent.kt index 94533d5fda..855c301e01 100644 --- a/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/addfunds/AddFundsComponent.kt +++ b/features/common-features/api/src/main/java/com/tangem/features/commonfeatures/api/managefunds/ManageFundsComponent.kt @@ -1,17 +1,20 @@ -package com.tangem.features.commonfeatures.api.addfunds +package com.tangem.features.commonfeatures.api.managefunds import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.wallet.UserWalletId -interface AddFundsComponent : ComposableBottomSheetComponent { +interface ManageFundsComponent : ComposableBottomSheetComponent { data class Params( val launchMode: LaunchMode, val onDismiss: () -> Unit, + val flowType: FlowType = FlowType.AddFunds, ) + enum class FlowType { AddFunds, Transfer } + sealed interface LaunchMode { data class ChooseToken(val userWalletId: UserWalletId) : LaunchMode @@ -25,5 +28,5 @@ interface AddFundsComponent : ComposableBottomSheetComponent { ) : LaunchMode } - interface Factory : ComponentFactory + interface Factory : ComponentFactory } \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/di/AddFundsComponentModule.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/di/AddFundsComponentModule.kt deleted file mode 100644 index 7ebd2f3ddf..0000000000 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/di/AddFundsComponentModule.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tangem.features.commonfeatures.impl.addfunds.di - -import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent -import com.tangem.features.commonfeatures.impl.addfunds.DefaultAddFundsComponent -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -internal interface AddFundsComponentModule { - - @Binds - fun bindAddFundsComponentFactory(factory: DefaultAddFundsComponent.Factory): AddFundsComponent.Factory -} \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/model/AddFundsRouteUiSpec.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/model/AddFundsRouteUiSpec.kt deleted file mode 100644 index 067e6b76a6..0000000000 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/model/AddFundsRouteUiSpec.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.tangem.features.commonfeatures.impl.addfunds.model - -import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.features.commonfeatures.impl.R -import com.tangem.core.ui.R as CoreR - -internal data class AddFundsRouteUiSpec( - val title: TextReference, - val shouldApplyHorizontalPadding: Boolean, - val shouldFillHeight: Boolean, -) - -internal fun AddFundsModel.UiRoute.uiSpec(): AddFundsRouteUiSpec = when (this) { - AddFundsModel.UiRoute.Loading -> AddFundsRouteUiSpec( - title = resourceReference(R.string.common_add_funds), - shouldApplyHorizontalPadding = false, - shouldFillHeight = false, - ) - AddFundsModel.UiRoute.ChooseToken -> AddFundsRouteUiSpec( - title = resourceReference(R.string.common_add_funds), - shouldApplyHorizontalPadding = false, - shouldFillHeight = true, - ) - AddFundsModel.UiRoute.UserPortfolio -> AddFundsRouteUiSpec( - title = resourceReference(R.string.common_add_funds), - shouldApplyHorizontalPadding = false, - shouldFillHeight = false, - ) - AddFundsModel.UiRoute.TokenActions -> AddFundsRouteUiSpec( - title = resourceReference(CoreR.string.common_get_token), - shouldApplyHorizontalPadding = true, - shouldFillHeight = true, - ) -} \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/DefaultAddFundsComponent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/DefaultManageFundsComponent.kt similarity index 68% rename from features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/DefaultAddFundsComponent.kt rename to features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/DefaultManageFundsComponent.kt index 011dbadec6..acd951b2b2 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/DefaultAddFundsComponent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/DefaultManageFundsComponent.kt @@ -1,4 +1,4 @@ -package com.tangem.features.commonfeatures.impl.addfunds +package com.tangem.features.commonfeatures.impl.managefunds import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.layout.fillMaxSize @@ -22,51 +22,56 @@ import com.tangem.core.ui.ds2.button.TangemButton import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemeRedesign import com.tangem.core.ui.test.BaseBottomSheetTestTags -import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent +import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenComponent -import com.tangem.features.commonfeatures.impl.addfunds.model.AddFundsModel -import com.tangem.features.commonfeatures.impl.addfunds.model.uiSpec +import com.tangem.features.commonfeatures.impl.managefunds.model.ManageFundsModel +import com.tangem.features.commonfeatures.impl.managefunds.model.uiSpec +import com.tangem.common.ui.markets.action.TokenActionsContext import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent import com.tangem.features.commonfeatures.impl.userportfolio.UserPortfolioComponent +import com.tangem.features.commonfeatures.impl.R import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import com.tangem.core.ui.R as CoreR @Suppress("LongParameterList") -internal class DefaultAddFundsComponent @AssistedInject constructor( +internal class DefaultManageFundsComponent @AssistedInject constructor( @Assisted appComponentContext: AppComponentContext, - @Assisted private val params: AddFundsComponent.Params, + @Assisted private val params: ManageFundsComponent.Params, chooseTokenComponentFactory: ChooseTokenComponent.Factory, tokenActionsComponentFactory: TokenActionsComponent.Factory, userPortfolioComponentFactory: UserPortfolioComponent.Factory, walletFeatureToggles: WalletFeatureToggles, -) : AppComponentContext by appComponentContext, AddFundsComponent { +) : AppComponentContext by appComponentContext, ManageFundsComponent { - private val model: AddFundsModel = getOrCreateModel(params) + private val model: ManageFundsModel = getOrCreateModel(params) - private val isCompactTokenActions: Boolean = params.launchMode is AddFundsComponent.LaunchMode.TokenActionsOnly + private val isCompactTokenActions: Boolean = params.launchMode is ManageFundsComponent.LaunchMode.TokenActionsOnly private val isAddFundsStage1Enabled: Boolean = walletFeatureToggles.isAddFundsStage1Enabled private val tokenActionsComponent: TokenActionsComponent by lazy { tokenActionsComponentFactory.create( - context = child(key = "addFundsTokenActions"), + context = child(key = "manageFundsTokenActions"), params = TokenActionsComponent.Params( data = model.tokenActionsData, callbacks = model, bottomAction = model.currentBottomAction, isRedesignForced = true, isCompact = isCompactTokenActions, + context = when (model.flowType) { + ManageFundsComponent.FlowType.AddFunds -> TokenActionsContext.AddFunds + ManageFundsComponent.FlowType.Transfer -> TokenActionsContext.Transfer + }, ), ) } private val chooseTokenComponent: ChooseTokenComponent? by lazy { - (params.launchMode as? AddFundsComponent.LaunchMode.ChooseToken)?.let { + (params.launchMode as? ManageFundsComponent.LaunchMode.ChooseToken)?.let { chooseTokenComponentFactory.create( - context = child(key = "addFundsChooseToken"), + context = child(key = "manageFundsChooseToken"), params = ChooseTokenComponent.Params( bridge = model.chooseTokenBridge, ), @@ -76,7 +81,7 @@ internal class DefaultAddFundsComponent @AssistedInject constructor( private val userPortfolioComponent: UserPortfolioComponent by lazy { userPortfolioComponentFactory.create( - context = child(key = "addFundsUserPortfolio"), + context = child(key = "manageFundsUserPortfolio"), params = UserPortfolioComponent.Params( uiState = model.userPortfolioStateController.uiState, callbacks = object : UserPortfolioComponent.Callbacks { @@ -94,8 +99,8 @@ internal class DefaultAddFundsComponent @AssistedInject constructor( val canGoBack by model.canGoBack.collectAsStateWithLifecycle() LaunchedEffect(route) { - if (route != AddFundsModel.UiRoute.UserPortfolio) return@LaunchedEffect - val mode = params.launchMode as? AddFundsComponent.LaunchMode.FilteredByRawId ?: return@LaunchedEffect + if (route != ManageFundsModel.UiRoute.UserPortfolio) return@LaunchedEffect + val mode = params.launchMode as? ManageFundsComponent.LaunchMode.FilteredByRawId ?: return@LaunchedEffect model.userPortfolioStateController.updateAndWaitNotNullState( allAvailableData = model.buildAvailableToAddDataForChooser(), rawCurrencyId = mode.rawCurrencyId, @@ -111,10 +116,10 @@ internal class DefaultAddFundsComponent @AssistedInject constructor( content = TangemBottomSheetConfigContent.Empty, ), type = when (params.launchMode) { - is AddFundsComponent.LaunchMode.TokenActionsOnly -> TangemBottomSheetType.Modal - is AddFundsComponent.LaunchMode.ChooseToken -> TangemBottomSheetType.Default - is AddFundsComponent.LaunchMode.FilteredByRawId -> - if (route is AddFundsModel.UiRoute.TokenActions) { + is ManageFundsComponent.LaunchMode.TokenActionsOnly -> TangemBottomSheetType.Modal + is ManageFundsComponent.LaunchMode.ChooseToken -> TangemBottomSheetType.Default + is ManageFundsComponent.LaunchMode.FilteredByRawId -> + if (route is ManageFundsModel.UiRoute.TokenActions) { TangemBottomSheetType.Default } else { TangemBottomSheetType.Modal @@ -122,7 +127,7 @@ internal class DefaultAddFundsComponent @AssistedInject constructor( }, containerColor = TangemTheme.colors2.surface.level2, title = { - AddFundsBottomSheetTitle( + ManageFundsBottomSheetTitle( route = route, canGoBack = canGoBack, onBackClick = model::onBack, @@ -131,7 +136,7 @@ internal class DefaultAddFundsComponent @AssistedInject constructor( }, content = { val animatedContentModifier = - if (params.launchMode is AddFundsComponent.LaunchMode.ChooseToken) { + if (params.launchMode is ManageFundsComponent.LaunchMode.ChooseToken) { Modifier.fillMaxSize() } else { Modifier @@ -139,11 +144,12 @@ internal class DefaultAddFundsComponent @AssistedInject constructor( AnimatedContent( targetState = route, modifier = animatedContentModifier, - label = "AddFundsContentAnimation", + label = "ManageFundsContentAnimation", ) { animatedRoute -> - AddFundsRouteContent( + ManageFundsRouteContent( route = animatedRoute, - shouldFillHeight = !isCompactTokenActions && animatedRoute.uiSpec().shouldFillHeight, + shouldFillHeight = !isCompactTokenActions && + animatedRoute.uiSpec(model.flowType).shouldFillHeight, ) } }, @@ -152,8 +158,8 @@ internal class DefaultAddFundsComponent @AssistedInject constructor( } @Composable - private fun AddFundsRouteContent(route: AddFundsModel.UiRoute, shouldFillHeight: Boolean) { - val spec = route.uiSpec() + private fun ManageFundsRouteContent(route: ManageFundsModel.UiRoute, shouldFillHeight: Boolean) { + val spec = route.uiSpec(model.flowType) val horizontalPadding = if (spec.shouldApplyHorizontalPadding) { Modifier.padding(horizontal = TangemTheme.dimens2.x4) } else { @@ -164,33 +170,33 @@ internal class DefaultAddFundsComponent @AssistedInject constructor( } @Composable - private fun RenderRoute(route: AddFundsModel.UiRoute, modifier: Modifier = Modifier) { + private fun RenderRoute(route: ManageFundsModel.UiRoute, modifier: Modifier = Modifier) { when (route) { - AddFundsModel.UiRoute.Loading -> Unit - AddFundsModel.UiRoute.ChooseToken -> chooseTokenComponent?.Content(modifier) - AddFundsModel.UiRoute.UserPortfolio -> CompositionLocalProvider( + ManageFundsModel.UiRoute.Loading -> Unit + ManageFundsModel.UiRoute.ChooseToken -> chooseTokenComponent?.Content(modifier) + ManageFundsModel.UiRoute.UserPortfolio -> CompositionLocalProvider( LocalTangemBottomSheetContentBottomInset provides TangemTheme.dimens2.x4, ) { userPortfolioComponent.Content(modifier) } - AddFundsModel.UiRoute.TokenActions -> tokenActionsComponent.Content(modifier) + ManageFundsModel.UiRoute.TokenActions -> tokenActionsComponent.Content(modifier) } } @Composable - private fun AddFundsBottomSheetTitle( - route: AddFundsModel.UiRoute, + private fun ManageFundsBottomSheetTitle( + route: ManageFundsModel.UiRoute, canGoBack: Boolean, onBackClick: () -> Unit, onCloseClick: () -> Unit, ) { TangemTopBar( - title = route.uiSpec().title, + title = route.uiSpec(model.flowType).title, type = TangemTopBarType.BottomSheet, startContent = if (canGoBack) { { TangemButton( - iconStart = TangemIconUM.Icon(iconRes = CoreR.drawable.ic_arrow_back_28), + iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_arrow_back_28), onClick = onBackClick, size = TangemButton.Size.X11, variant = TangemButton.Variant.Material, @@ -202,7 +208,7 @@ internal class DefaultAddFundsComponent @AssistedInject constructor( endContent = { TangemButton( modifier = Modifier.testTag(BaseBottomSheetTestTags.CLOSE_BUTTON), - iconStart = TangemIconUM.Icon(iconRes = CoreR.drawable.ic_close_24), + iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_close_24), onClick = onCloseClick, size = TangemButton.Size.X11, variant = TangemButton.Variant.Material, @@ -221,7 +227,10 @@ internal class DefaultAddFundsComponent @AssistedInject constructor( } @AssistedFactory - interface Factory : AddFundsComponent.Factory { - override fun create(context: AppComponentContext, params: AddFundsComponent.Params): DefaultAddFundsComponent + interface Factory : ManageFundsComponent.Factory { + override fun create( + context: AppComponentContext, + params: ManageFundsComponent.Params, + ): DefaultManageFundsComponent } } \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/analytics/AddFundsAnalyticsEvent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/analytics/ManageFundsAnalyticsEvent.kt similarity index 53% rename from features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/analytics/AddFundsAnalyticsEvent.kt rename to features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/analytics/ManageFundsAnalyticsEvent.kt index 86cb642022..19c26683b6 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/analytics/AddFundsAnalyticsEvent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/analytics/ManageFundsAnalyticsEvent.kt @@ -1,23 +1,23 @@ -package com.tangem.features.commonfeatures.impl.addfunds.analytics +package com.tangem.features.commonfeatures.impl.managefunds.analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam -internal sealed class AddFundsAnalyticsEvent( +internal sealed class ManageFundsAnalyticsEvent( event: String, params: Map = emptyMap(), ) : AnalyticsEvent(category = CATEGORY, event = event, params = params) { - class MethodScreenOpened(source: String) : AddFundsAnalyticsEvent( + class MethodScreenOpened(source: String) : ManageFundsAnalyticsEvent( event = "Method Screen Opened", params = mapOf(AnalyticsParam.SOURCE to source), ) - class ButtonBuy : AddFundsAnalyticsEvent(event = "Button - Buy") + class ButtonBuy : ManageFundsAnalyticsEvent(event = "Button - Buy") - class ButtonSwap : AddFundsAnalyticsEvent(event = "Button - Swap") + class ButtonSwap : ManageFundsAnalyticsEvent(event = "Button - Swap") - class ButtonReceive : AddFundsAnalyticsEvent(event = "Button - Receive") + class ButtonReceive : ManageFundsAnalyticsEvent(event = "Button - Receive") companion object { private const val CATEGORY = "Add Funds" diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/di/ManageFundsComponentModule.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/di/ManageFundsComponentModule.kt new file mode 100644 index 0000000000..e44ac1a3e1 --- /dev/null +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/di/ManageFundsComponentModule.kt @@ -0,0 +1,16 @@ +package com.tangem.features.commonfeatures.impl.managefunds.di + +import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent +import com.tangem.features.commonfeatures.impl.managefunds.DefaultManageFundsComponent +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +internal interface ManageFundsComponentModule { + + @Binds + fun bindManageFundsComponentFactory(factory: DefaultManageFundsComponent.Factory): ManageFundsComponent.Factory +} \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/di/AddFundsModelModule.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/di/ManageFundsModelModule.kt similarity index 52% rename from features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/di/AddFundsModelModule.kt rename to features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/di/ManageFundsModelModule.kt index efc483ebf2..2c8eec95cc 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/di/AddFundsModelModule.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/di/ManageFundsModelModule.kt @@ -1,8 +1,8 @@ -package com.tangem.features.commonfeatures.impl.addfunds.di +package com.tangem.features.commonfeatures.impl.managefunds.di import com.tangem.core.decompose.di.ModelComponent import com.tangem.core.decompose.model.Model -import com.tangem.features.commonfeatures.impl.addfunds.model.AddFundsModel +import com.tangem.features.commonfeatures.impl.managefunds.model.ManageFundsModel import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -11,10 +11,10 @@ import dagger.multibindings.IntoMap @Module @InstallIn(ModelComponent::class) -internal interface AddFundsModelModule { +internal interface ManageFundsModelModule { @Binds @IntoMap - @ClassKey(AddFundsModel::class) - fun addFundsModel(model: AddFundsModel): Model + @ClassKey(ManageFundsModel::class) + fun manageFundsModel(model: ManageFundsModel): Model } \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/model/AddFundsModel.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/model/ManageFundsModel.kt similarity index 86% rename from features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/model/AddFundsModel.kt rename to features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/model/ManageFundsModel.kt index f3d991be49..71f307963d 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addfunds/model/AddFundsModel.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/model/ManageFundsModel.kt @@ -1,4 +1,4 @@ -package com.tangem.features.commonfeatures.impl.addfunds.model +package com.tangem.features.commonfeatures.impl.managefunds.model import androidx.compose.runtime.Immutable import com.tangem.blockchainsdk.compatibility.getTokenIdIfL2Network @@ -19,14 +19,14 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.wallets.usecase.GetUserWalletUseCase -import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent +import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent import com.tangem.features.commonfeatures.api.addtoportfolio.AvailableToAddData import com.tangem.features.commonfeatures.api.addtoportfolio.AvailableToAddWallet import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenAnalyticsPayload import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenBridge import com.tangem.features.commonfeatures.api.choosetoken.ChooseTokenResult import com.tangem.features.commonfeatures.api.tokenactions.BottomAction -import com.tangem.features.commonfeatures.impl.addfunds.analytics.AddFundsAnalyticsEvent +import com.tangem.features.commonfeatures.impl.managefunds.analytics.ManageFundsAnalyticsEvent import com.tangem.features.commonfeatures.impl.tokenactions.TokenActionsComponent import com.tangem.features.commonfeatures.impl.userportfolio.state.UserPortfolioStateController import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -37,7 +37,7 @@ import javax.inject.Inject @ModelScoped @Suppress("LongParameterList") -internal class AddFundsModel @Inject constructor( +internal class ManageFundsModel @Inject constructor( paramsContainer: ParamsContainer, chooseTokenBridgeFactory: ChooseTokenBridge.Factory, userPortfolioStateControllerFactory: UserPortfolioStateController.Factory, @@ -50,8 +50,9 @@ internal class AddFundsModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, ) : Model(), TokenActionsComponent.Callbacks { - private val params = paramsContainer.require() - val launchMode: AddFundsComponent.LaunchMode = params.launchMode + private val params = paramsContainer.require() + val launchMode: ManageFundsComponent.LaunchMode = params.launchMode + val flowType: ManageFundsComponent.FlowType = params.flowType private val routeStack = MutableStateFlow(listOf(UiRoute.Loading)) @@ -95,7 +96,10 @@ internal class AddFundsModel @Inject constructor( val chooseTokenBridge: ChooseTokenBridge by lazy { chooseTokenBridgeFactory.create( modelScope = modelScope, - settings = ChooseTokenBridge.Settings.AddFunds, + settings = when (flowType) { + ManageFundsComponent.FlowType.AddFunds -> ChooseTokenBridge.Settings.AddFunds + ManageFundsComponent.FlowType.Transfer -> ChooseTokenBridge.Settings.Transfer + }, analyticsPayload = setOf(ChooseTokenAnalyticsPayload.ScreensSources(SCREEN_SOURCE)), ) } @@ -116,9 +120,9 @@ internal class AddFundsModel @Inject constructor( init { when (val mode = launchMode) { - is AddFundsComponent.LaunchMode.ChooseToken -> initChooseToken(mode) - is AddFundsComponent.LaunchMode.TokenActionsOnly -> initTokenActionsOnly(mode) - is AddFundsComponent.LaunchMode.FilteredByRawId -> initFilteredByRawId(mode) + is ManageFundsComponent.LaunchMode.ChooseToken -> initChooseToken(mode) + is ManageFundsComponent.LaunchMode.TokenActionsOnly -> initTokenActionsOnly(mode) + is ManageFundsComponent.LaunchMode.FilteredByRawId -> initFilteredByRawId(mode) } } @@ -143,9 +147,9 @@ internal class AddFundsModel @Inject constructor( override fun onQuickActionClick(action: TokenActionsBSContentUM.Action, shouldDismiss: Boolean) { val event = when (action) { - TokenActionsBSContentUM.Action.Buy -> AddFundsAnalyticsEvent.ButtonBuy() - TokenActionsBSContentUM.Action.Exchange -> AddFundsAnalyticsEvent.ButtonSwap() - TokenActionsBSContentUM.Action.Receive -> AddFundsAnalyticsEvent.ButtonReceive() + TokenActionsBSContentUM.Action.Buy -> ManageFundsAnalyticsEvent.ButtonBuy() + TokenActionsBSContentUM.Action.Exchange -> ManageFundsAnalyticsEvent.ButtonSwap() + TokenActionsBSContentUM.Action.Receive -> ManageFundsAnalyticsEvent.ButtonReceive() else -> null } event?.let { analyticsEventHandler.send(it) } @@ -168,10 +172,10 @@ internal class AddFundsModel @Inject constructor( ) } - private fun initChooseToken(mode: AddFundsComponent.LaunchMode.ChooseToken) { + private fun initChooseToken(mode: ManageFundsComponent.LaunchMode.ChooseToken) { chooseTokenBridge.selectWalletTab(mode.userWalletId) analyticsEventHandler.send( - AddFundsAnalyticsEvent.MethodScreenOpened(source = AddFundsAnalyticsEvent.SOURCE_MAIN_SCREEN), + ManageFundsAnalyticsEvent.MethodScreenOpened(source = ManageFundsAnalyticsEvent.SOURCE_MAIN_SCREEN), ) replaceRoot(UiRoute.ChooseToken) modelScope.launch { @@ -182,7 +186,7 @@ internal class AddFundsModel @Inject constructor( } } - private fun initTokenActionsOnly(mode: AddFundsComponent.LaunchMode.TokenActionsOnly) { + private fun initTokenActionsOnly(mode: ManageFundsComponent.LaunchMode.TokenActionsOnly) { modelScope.launch { val wallet = getUserWalletUseCase.invokeFlow(mode.userWalletId) .mapNotNull { it.getOrNull() } @@ -206,7 +210,7 @@ internal class AddFundsModel @Inject constructor( } } - private fun initFilteredByRawId(mode: AddFundsComponent.LaunchMode.FilteredByRawId) { + private fun initFilteredByRawId(mode: ManageFundsComponent.LaunchMode.FilteredByRawId) { modelScope.launch { val entries = collectFilteredEntries(mode.rawCurrencyId) when (entries.size) { diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/model/ManageFundsRouteUiSpec.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/model/ManageFundsRouteUiSpec.kt new file mode 100644 index 0000000000..46258a31b1 --- /dev/null +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/model/ManageFundsRouteUiSpec.kt @@ -0,0 +1,38 @@ +package com.tangem.features.commonfeatures.impl.managefunds.model + +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent +import com.tangem.features.commonfeatures.impl.R + +internal data class ManageFundsRouteUiSpec( + val title: TextReference, + val shouldApplyHorizontalPadding: Boolean, + val shouldFillHeight: Boolean, +) + +internal fun ManageFundsModel.UiRoute.uiSpec(flowType: ManageFundsComponent.FlowType): ManageFundsRouteUiSpec { + val isTransfer = flowType == ManageFundsComponent.FlowType.Transfer + return when (this) { + ManageFundsModel.UiRoute.Loading -> ManageFundsRouteUiSpec( + title = resourceReference(if (isTransfer) R.string.common_choose_token else R.string.common_add_funds), + shouldApplyHorizontalPadding = false, + shouldFillHeight = false, + ) + ManageFundsModel.UiRoute.ChooseToken -> ManageFundsRouteUiSpec( + title = resourceReference(R.string.common_choose_token), + shouldApplyHorizontalPadding = false, + shouldFillHeight = true, + ) + ManageFundsModel.UiRoute.UserPortfolio -> ManageFundsRouteUiSpec( + title = resourceReference(R.string.common_add_funds), + shouldApplyHorizontalPadding = false, + shouldFillHeight = false, + ) + ManageFundsModel.UiRoute.TokenActions -> ManageFundsRouteUiSpec( + title = resourceReference(if (isTransfer) R.string.common_transfer else R.string.common_get_token), + shouldApplyHorizontalPadding = true, + shouldFillHeight = true, + ) + } +} \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/TokenActionsComponent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/TokenActionsComponent.kt index 5ad5e141d6..09f7eb1a18 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/TokenActionsComponent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/TokenActionsComponent.kt @@ -10,6 +10,7 @@ import com.arkivanov.decompose.router.slot.childSlot import com.arkivanov.decompose.router.slot.dismiss import com.tangem.common.ui.markets.action.CryptoCurrencyData import com.tangem.common.ui.markets.action.TokenActionsBSContentUM +import com.tangem.common.ui.markets.action.TokenActionsContext import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.factory.ComponentFactory @@ -79,6 +80,7 @@ internal class TokenActionsComponent @AssistedInject constructor( val bottomAction: Flow = flowOf(BottomAction.None), val isRedesignForced: Boolean = false, val isCompact: Boolean = false, + val context: TokenActionsContext = TokenActionsContext.Markets, ) interface Callbacks { diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/model/TokenActionsUiBuilder.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/model/TokenActionsUiBuilder.kt index 91c88bc364..cc1ec83458 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/model/TokenActionsUiBuilder.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/tokenactions/model/TokenActionsUiBuilder.kt @@ -92,6 +92,7 @@ internal class TokenActionsUiBuilder @Inject constructor( cryptoData = cryptoCurrencyData, tokenActionsHandler = tokenActionsHandler, isRedesignEnabled = false, + context = params.context, ), bottomActionText = bottomActionText(bottomAction), onBottomActionClick = { @@ -124,6 +125,7 @@ internal class TokenActionsUiBuilder @Inject constructor( cryptoData = cryptoCurrencyData, tokenActionsHandler = tokenActionsHandler, isRedesignEnabled = true, + context = params.context, ), bottomActionText = bottomActionText(bottomAction), onBottomActionClick = { diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/FeedEntryChildFactory.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/FeedEntryChildFactory.kt index 41a971c02d..b5814e9491 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/FeedEntryChildFactory.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/FeedEntryChildFactory.kt @@ -8,6 +8,7 @@ import com.tangem.core.decompose.navigation.Route import com.tangem.core.ui.DesignFeatureToggles import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent +import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent import com.tangem.features.feed.components.earn.DefaultEarnComponent import com.tangem.features.feed.components.feed.DefaultFeedComponent import com.tangem.features.feed.components.feed.DefaultFeedComponent.FeedParams @@ -29,7 +30,7 @@ internal class FeedEntryChildFactory @Inject constructor( private val portfolioComponentFactory: MarketsPortfolioComponent.Factory, private val portfolioBlockComponentFactory: PortfolioBlockComponent.Factory, private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory, - private val addFundsComponentFactory: com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent.Factory, + private val manageFundsComponentFactory: ManageFundsComponent.Factory, private val promoBannersBlockComponentFactory: PromoBannersBlockComponent.Factory, private val designFeatureToggles: DesignFeatureToggles, ) { @@ -84,7 +85,7 @@ internal class FeedEntryChildFactory @Inject constructor( portfolioBlockComponentFactory = portfolioBlockComponentFactory, designFeatureToggles = designFeatureToggles, addToPortfolioComponentFactory = addToPortfolioComponentFactory, - addFundsComponentFactory = addFundsComponentFactory, + manageFundsComponentFactory = manageFundsComponentFactory, ) } is Child.TokenList -> { diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt index 8d2cc3164c..32e69dc413 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/DefaultMarketsTokenDetailsComponent.kt @@ -42,7 +42,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.markets.PreselectedTokenDetailsSection import com.tangem.domain.markets.TokenMarketParams import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent +import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioComponent import com.tangem.features.feed.components.market.details.portfolio.api.MarketsPortfolioComponent import com.tangem.features.feed.components.market.details.portfolioblock.PortfolioBlockComponent @@ -66,7 +66,7 @@ internal class DefaultMarketsTokenDetailsComponent( portfolioBlockComponentFactory: PortfolioBlockComponent.Factory, val params: Params, private val addToPortfolioComponentFactory: AddToPortfolioComponent.Factory, - private val addFundsComponentFactory: AddFundsComponent.Factory, + private val manageFundsComponentFactory: ManageFundsComponent.Factory, ) : ComposableModularBottomSheetContentComponent, AppComponentContext by appComponentContext { // applying l2 compatibility @@ -174,10 +174,10 @@ internal class DefaultMarketsTokenDetailsComponent( config: AddFundsSlotRoute, componentContext: ComponentContext, ): ComposableBottomSheetComponent { - val launchMode = AddFundsComponent.LaunchMode.FilteredByRawId(rawCurrencyId = config.rawCurrencyId) - return addFundsComponentFactory.create( + val launchMode = ManageFundsComponent.LaunchMode.FilteredByRawId(rawCurrencyId = config.rawCurrencyId) + return manageFundsComponentFactory.create( context = childByContext(componentContext), - params = AddFundsComponent.Params( + params = ManageFundsComponent.Params( launchMode = launchMode, onDismiss = { model.addFundsSheetNavigation.dismiss() }, ), diff --git a/features/send/api/src/main/java/com/tangem/features/send/api/SendEntryPointComponent.kt b/features/send/api/src/main/java/com/tangem/features/send/api/SendEntryPointComponent.kt index 546a470a6e..21b81bf8a4 100644 --- a/features/send/api/src/main/java/com/tangem/features/send/api/SendEntryPointComponent.kt +++ b/features/send/api/src/main/java/com/tangem/features/send/api/SendEntryPointComponent.kt @@ -10,6 +10,7 @@ interface SendEntryPointComponent : ComposableContentComponent { data class Params( val userWalletId: UserWalletId, val cryptoCurrency: CryptoCurrency, + val shouldStartWithSwap: Boolean = false, ) interface Factory : ComponentFactory diff --git a/features/send/impl/src/main/java/com/tangem/features/send/entrypoint/DefaultSendEntryPointComponent.kt b/features/send/impl/src/main/java/com/tangem/features/send/entrypoint/DefaultSendEntryPointComponent.kt index 020cb7a708..5a724b9ef4 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/entrypoint/DefaultSendEntryPointComponent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/entrypoint/DefaultSendEntryPointComponent.kt @@ -98,6 +98,11 @@ internal class DefaultSendEntryPointComponent @AssistedInject constructor( model.currentRoute.emit(stack.active.configuration) } } + if (params.shouldStartWithSwap) { + // Direct Swap&Send entry: replicate the canonical "send with swap" trigger so the receive-token + // selector opens like the regular Send -> Send&Swap flow (this was the less-broken behaviour). + model.onConvertToAnotherToken(lastAmount = "", isEnterInFiatSelected = false) + } } @Composable diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index ea54e5c2b3..09064c9ed5 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -565,12 +565,11 @@ internal class SwapAmountModel @Inject constructor( (params as? SwapAmountComponentParams.AmountParams)?.callback?.resetSendWithSwapNavigation( data.shouldResetNavigation, ) + ensurePrimaryReady() + initPairs(data.swapCurrencies, data.cryptoCurrency) uiState.update { amountUM -> if (amountUM is SwapAmountUM.Content) { - initPairs(data.swapCurrencies, data.cryptoCurrency) - amountUM.copy( - isPrimaryButtonEnabled = false, - ) + amountUM.copy(isPrimaryButtonEnabled = false) } else { amountUM } @@ -579,6 +578,32 @@ internal class SwapAmountModel @Inject constructor( .launchIn(modelScope) } + /** + * Forces the amount UI into the primary-ready [SwapAmountUM.Content] state using the current primary + * status. Needed for the direct Send-with-Swap (Swap&Send) entry where the state can be [SwapAmountUM.Empty] and + * the balance-`distinctUntilChanged` primary status flow won't re-emit to rebuild it. No-op when already Content. + */ + private suspend fun ensurePrimaryReady() { + if (uiState.value is SwapAmountUM.Content) return + val primaryCurrencyStatus = params.primaryCryptoCurrencyStatusFlow.value + initCurrencies(primaryStatus = primaryCurrencyStatus, secondaryStatus = null) + val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1 + uiState.transformerUpdate( + SwapAmountPrimaryReadyStateTransformer( + userWallet = userWallet, + primaryCryptoCurrencyStatus = primaryCurrencyStatus, + appCurrency = appCurrency, + swapDirection = swapDirection, + clickIntents = this, + isBalanceHidden = params.isBalanceHidingFlow.value, + isShowBestRateAnimation = isShowBestRateAnimation, + isSingleWallet = isOnlyOneWallet, + isAccountsMode = params.isAccountModeFlow.value, + account = params.accountFlow.value, + ), + ) + } + private fun initPairs(swapCurrencies: SwapCurrencies, secondaryCryptoCurrency: CryptoCurrency?) { modelScope.launch { val secondaryCurrency = selectInitialPairUseCase( diff --git a/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/TokenDetailsFeatureToggles.kt b/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/TokenDetailsFeatureToggles.kt index 71f92a9e18..5cf0e73735 100644 --- a/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/TokenDetailsFeatureToggles.kt +++ b/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/TokenDetailsFeatureToggles.kt @@ -2,4 +2,5 @@ package com.tangem.features.tokendetails interface TokenDetailsFeatureToggles { val isQuickTopUpEnabled: Boolean + val isManageFundsEnabled: Boolean } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsComponent.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsComponent.kt index 50c9102fa5..aec7dd1d42 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsComponent.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsComponent.kt @@ -22,7 +22,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.route.TokenDeta import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreen import com.tangem.feature.tokendetails.presentation.tokendetails.ui.TokenDetailsScreenLegacy import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.ChooseAddressBottomSheetComponent -import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent +import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.CloreMigrationBottomSheetComponent import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.DynamicAddressesBottomSheetComponent import com.tangem.feature.tokendetails.presentation.tokendetails.ui.bottomsheet.TransferBottomSheetComponent @@ -47,7 +47,7 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor( expressTransactionsComponentFactory: ExpressTransactionsComponent.Factory, private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory, private val yieldSupplyWarningComponentFactory: YieldSupplyDepositedWarningComponent.Factory, - private val addFundsComponentFactory: AddFundsComponent.Factory, + private val manageFundsComponentFactory: ManageFundsComponent.Factory, yieldSupplyComponentFactory: YieldSupplyComponent.Factory, private val ratingComponentFactory: RatingComponent.Factory, ) : TokenDetailsComponent, AppComponentContext by appComponentContext { @@ -178,10 +178,10 @@ internal class DefaultTokenDetailsComponent @AssistedInject constructor( dynamicAddressesDelegate = model.dynamicAddressesDelegate, onDismiss = model.bottomSheetNavigation::dismiss, ) - is TokenDetailsBottomSheetConfig.AddFunds -> addFundsComponentFactory.create( + is TokenDetailsBottomSheetConfig.AddFunds -> manageFundsComponentFactory.create( context = childByContext(componentContext), - params = AddFundsComponent.Params( - launchMode = AddFundsComponent.LaunchMode.TokenActionsOnly( + params = ManageFundsComponent.Params( + launchMode = ManageFundsComponent.LaunchMode.TokenActionsOnly( userWalletId = route.userWalletId, currency = route.currency, ), diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsFeatureToggles.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsFeatureToggles.kt index c3d81a3a9b..24b973acdd 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsFeatureToggles.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/DefaultTokenDetailsFeatureToggles.kt @@ -6,10 +6,13 @@ import com.tangem.features.tokendetails.TokenDetailsFeatureToggles import javax.inject.Inject internal class DefaultTokenDetailsFeatureToggles @Inject constructor( - featureTogglesManager: FeatureTogglesManager, + private val featureTogglesManager: FeatureTogglesManager, ) : TokenDetailsFeatureToggles { override val isQuickTopUpEnabled: Boolean = featureTogglesManager.isFeatureEnabled( toggle = FeatureToggles.AND_15258_QUICK_TOP_UP_ENABLED, ) + + override val isManageFundsEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.TWI_1377_MANAGE_FUNDS) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt index 6ae77ff99c..319472b5ea 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt @@ -22,6 +22,8 @@ interface TokenDetailsClickIntents { fun onSwapFromClick(unavailabilityReason: ScenarioUnavailabilityReason) + fun onSwapAndSendClick(unavailabilityReason: ScenarioUnavailabilityReason) + fun onSwapToClick(unavailabilityReason: ScenarioUnavailabilityReason) fun onBuyClick(unavailabilityReason: ScenarioUnavailabilityReason) @@ -151,6 +153,8 @@ internal class EmptyTokenDetailsClickIntents : TokenDetailsClickIntents { override fun onSwapFromClick(unavailabilityReason: ScenarioUnavailabilityReason) { /* no op */ } + override fun onSwapAndSendClick(unavailabilityReason: ScenarioUnavailabilityReason) { /* no op */ } + override fun onSwapToClick(unavailabilityReason: ScenarioUnavailabilityReason) { /* no op */ } override fun onHideClick() { /* no op */ } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index 2e1bbdb28f..8c9f7b835c 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -599,6 +599,15 @@ internal class TokenDetailsModel @Inject constructor( } override fun onTransferClick() { + val amount = cryptoCurrencyStatus?.value?.amount + if (amount == null || amount.signum() <= 0) { + uiMessageSender.send( + message = SnackbarMessage( + message = resourceReference(R.string.token_button_unavailability_reason_empty_balance_send), + ), + ) + return + } bottomSheetNavigation.activate(TokenDetailsBottomSheetConfig.Transfer) } @@ -832,6 +841,20 @@ internal class TokenDetailsModel @Inject constructor( handleSwap(unavailabilityReason, AppRoute.Swap.CurrencyPosition.FROM, checkYieldSupply = true) } + override fun onSwapAndSendClick(unavailabilityReason: ScenarioUnavailabilityReason) { + if (handleUnavailabilityReason(unavailabilityReason = unavailabilityReason)) { + return + } + + appRouter.push( + AppRoute.SendEntryPoint( + userWalletId = userWalletId, + currency = cryptoCurrency, + shouldStartWithSwap = true, + ), + ) + } + override fun onSwapToClick(unavailabilityReason: ScenarioUnavailabilityReason) { handleSwap(unavailabilityReason, AppRoute.Swap.CurrencyPosition.TO, checkYieldSupply = false) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TransferUM.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TransferUM.kt index 7f7d1ee2b9..946c563e64 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TransferUM.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TransferUM.kt @@ -21,6 +21,7 @@ internal sealed interface TransferUM : TangemBottomSheetConfigContent { data class Content( val send: Row?, val swap: Row?, + val swapAndSend: Row?, val sell: Row?, ) : TransferUM diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformer.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformer.kt index c87c35954e..87a40e29dd 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformer.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformer.kt @@ -43,6 +43,16 @@ internal class UpdateTransferTransformer( }, ) } + val swapAndSendRow = swapAction?.let { action -> + TransferUM.Row( + isLoading = action.unavailabilityReason.isLoading, + isEnabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None, + onClick = { + onActionDispatched() + clickIntents.onSwapAndSendClick(action.unavailabilityReason) + }, + ) + } val sellRow = sellAction?.let { action -> TransferUM.Row( isLoading = action.unavailabilityReason.isOutdatedLoading(), @@ -55,7 +65,12 @@ internal class UpdateTransferTransformer( } return prevState.copy( - transferUM = TransferUM.Content(send = sendRow, swap = swapRow, sell = sellRow), + transferUM = TransferUM.Content( + send = sendRow, + swap = swapRow, + swapAndSend = swapAndSendRow, + sell = sellRow, + ), ) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/bottomsheet/TransferBottomSheetContent.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/bottomsheet/TransferBottomSheetContent.kt index c784518a3e..99a2cc8b34 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/bottomsheet/TransferBottomSheetContent.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/bottomsheet/TransferBottomSheetContent.kt @@ -36,6 +36,7 @@ internal fun TransferBottomSheetContent(state: TransferUM, onCloseClick: () -> U ) { SendActionRow(state = state) SwapActionRow(state = state) + SwapAndSendActionRow(state = state) SellActionRow(state = state) SpacerH(TangemTheme.dimens2.x2) @@ -80,6 +81,19 @@ private fun SwapActionRow(state: TransferUM) { ) } +@Composable +private fun SwapAndSendActionRow(state: TransferUM) { + val row = (state as? TransferUM.Content)?.swapAndSend + if (state is TransferUM.Content && row == null) return + ActionRow( + iconRes = CoreR.drawable.ic_exchange_mini_24, + title = resourceReference(CoreR.string.common_send_with_swap), + description = resourceReference(CoreR.string.quick_action_send_and_swap_description), + row = row, + isLoading = state is TransferUM.Loading, + ) +} + @Composable private fun SellActionRow(state: TransferUM) { val row = (state as? TransferUM.Content)?.sell @@ -149,16 +163,19 @@ private class TransferPreviewProvider : PreviewParameterProvider { TransferUM.Content( send = TransferUM.Row(isLoading = false, isEnabled = true, onClick = {}), swap = TransferUM.Row(isLoading = false, isEnabled = true, onClick = {}), + swapAndSend = TransferUM.Row(isLoading = false, isEnabled = true, onClick = {}), sell = TransferUM.Row(isLoading = false, isEnabled = true, onClick = {}), ), TransferUM.Content( send = TransferUM.Row(isLoading = false, isEnabled = true, onClick = {}), swap = TransferUM.Row(isLoading = false, isEnabled = false, onClick = {}), + swapAndSend = TransferUM.Row(isLoading = false, isEnabled = false, onClick = {}), sell = TransferUM.Row(isLoading = false, isEnabled = false, onClick = {}), ), TransferUM.Content( send = TransferUM.Row(isLoading = false, isEnabled = true, onClick = {}), swap = null, + swapAndSend = null, sell = null, ), ) diff --git a/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformerTest.kt b/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformerTest.kt index 6391fb3cef..c4354538ab 100644 --- a/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformerTest.kt +++ b/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformerTest.kt @@ -319,6 +319,57 @@ class UpdateTransferTransformerTest { assertThat(content.swap?.isEnabled).isTrue() } + @Test + fun `GIVEN Swap action available WHEN transform THEN swapAndSend row is not null`() { + // Arrange + val transformer = createTransformer( + actions = listOf(TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.None, false)), + ) + + // Act + val result = transformer.transform(initialState()) + + // Assert + val content = result.transferUM as TransferUM.Content + assertThat(content.swapAndSend).isNotNull() + } + + @Test + fun `GIVEN Swap action available WHEN swapAndSend onClick invoked THEN onSwapAndSendClick is called`() { + // Arrange + val transformer = createTransformer( + actions = listOf(TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.None, false)), + ) + + // Act + val content = transformer.transform(initialState()).transferUM as TransferUM.Content + content.swapAndSend!!.onClick() + + // Assert + verifyOrder { + onActionDispatched.invoke() + clickIntents.onSwapAndSendClick(ScenarioUnavailabilityReason.None) + } + } + + @Test + fun `GIVEN no Swap action WHEN transform THEN swapAndSend row is null`() { + // Arrange + val transformer = createTransformer( + actions = listOf( + TokenActionsState.ActionState.Send(ScenarioUnavailabilityReason.None), + TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.None), + ), + ) + + // Act + val result = transformer.transform(initialState()) + + // Assert + val content = result.transferUM as TransferUM.Content + assertThat(content.swapAndSend).isNull() + } + @Test fun `GIVEN row WHEN not clicked THEN no callbacks fire`() { // GIVEN diff --git a/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/featuretoggles/WalletFeatureToggles.kt b/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/featuretoggles/WalletFeatureToggles.kt index 7c72192ae5..d61d46ab1f 100644 --- a/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/featuretoggles/WalletFeatureToggles.kt +++ b/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/featuretoggles/WalletFeatureToggles.kt @@ -10,4 +10,6 @@ interface WalletFeatureToggles { val isAddAndManageTokensEnabled: Boolean val isAddFundsStage1Enabled: Boolean + + val isManageFundsEnabled: Boolean } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt index b2c1be6913..87135b6a10 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt @@ -25,7 +25,7 @@ import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.tokens.model.details.TokenAction import com.tangem.feature.wallet.child.managetokens.AddAndManageBottomSheetComponent import com.tangem.feature.wallet.child.organizetokens.OrganizeTokensComponent -import com.tangem.features.commonfeatures.api.addfunds.AddFundsComponent +import com.tangem.features.commonfeatures.api.managefunds.ManageFundsComponent import com.tangem.feature.wallet.child.tokenActions.DefaultTokenActionsComponent import com.tangem.feature.wallet.child.tokenActions.TokenActionsComponent import com.tangem.feature.wallet.child.wallet.model.WalletModel @@ -70,7 +70,7 @@ internal class WalletComponent @AssistedInject constructor( private val networkSelectionComponentFactory: NetworkSelectionComponent.Factory, private val tokenActionsComponentFactory: TokenActionsComponent.Factory, private val portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory, - private val addFundsComponentFactory: AddFundsComponent.Factory, + private val manageFundsComponentFactory: ManageFundsComponent.Factory, private val designFeatureToggles: DesignFeatureToggles, ) : ComposableContentComponent, AppComponentContext by appComponentContext { @@ -191,10 +191,20 @@ internal class WalletComponent @AssistedInject constructor( ) } is WalletDialogConfig.AddFunds -> { - addFundsComponentFactory.create( + manageFundsComponentFactory.create( context = childByContext(componentContext), - params = AddFundsComponent.Params( - launchMode = AddFundsComponent.LaunchMode.ChooseToken(dialogConfig.userWalletId), + params = ManageFundsComponent.Params( + launchMode = ManageFundsComponent.LaunchMode.ChooseToken(dialogConfig.userWalletId), + onDismiss = model.innerWalletRouter.dialogNavigation::dismiss, + ), + ) + } + is WalletDialogConfig.Transfer -> { + manageFundsComponentFactory.create( + context = childByContext(componentContext), + params = ManageFundsComponent.Params( + launchMode = ManageFundsComponent.LaunchMode.ChooseToken(dialogConfig.userWalletId), + flowType = ManageFundsComponent.FlowType.Transfer, onDismiss = model.innerWalletRouter.dialogNavigation::dismiss, ), ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt index 71d49f3081..644d1602c5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt @@ -566,6 +566,7 @@ internal class WalletModel @Inject constructor( walletImageResolver = walletImageResolver, getWalletIconUseCase = getWalletIconUseCase, isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled, + isManageFundsEnabled = walletFeatureToggles.isManageFundsEnabled, ), ) @@ -613,6 +614,7 @@ internal class WalletModel @Inject constructor( walletImageResolver = walletImageResolver, getWalletIconUseCase = getWalletIconUseCase, isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled, + isManageFundsEnabled = walletFeatureToggles.isManageFundsEnabled, ), ) } @@ -635,6 +637,7 @@ internal class WalletModel @Inject constructor( walletImageResolver = walletImageResolver, getWalletIconUseCase = getWalletIconUseCase, isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled, + isManageFundsEnabled = walletFeatureToggles.isManageFundsEnabled, ), ) } @@ -650,6 +653,7 @@ internal class WalletModel @Inject constructor( walletImageResolver = walletImageResolver, getWalletIconUseCase = getWalletIconUseCase, isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled, + isManageFundsEnabled = walletFeatureToggles.isManageFundsEnabled, ), ) @@ -712,6 +716,7 @@ internal class WalletModel @Inject constructor( walletImageResolver = walletImageResolver, getWalletIconUseCase = getWalletIconUseCase, isAddFundsStage1Enabled = walletFeatureToggles.isAddFundsStage1Enabled, + isManageFundsEnabled = walletFeatureToggles.isManageFundsEnabled, ), ) 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 952cf4764d..d983fe385c 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 @@ -124,6 +124,10 @@ internal class WalletClickIntents @Inject constructor( router.openAddFunds(userWalletId) } + fun onTransferClick(userWalletId: UserWalletId) { + router.openTransfer(userWalletId) + } + fun onAddFundsPromoClick(userWalletId: UserWalletId) { analyticsEventHandler.send(WalletScreenAnalyticsEvent.MainScreen.ButtonAddFundsPromo()) router.openAddFunds(userWalletId) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/featuretoggles/DefaultWalletFeatureToggles.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/featuretoggles/DefaultWalletFeatureToggles.kt index 1695e2fe76..d465ad74b7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/featuretoggles/DefaultWalletFeatureToggles.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/featuretoggles/DefaultWalletFeatureToggles.kt @@ -14,4 +14,7 @@ internal class DefaultWalletFeatureToggles @Inject constructor( override val isAddFundsStage1Enabled: Boolean get() = featureToggles.isFeatureEnabled(FeatureToggles.AND_15310_ADD_FUNDS_STAGE1) + + override val isManageFundsEnabled: Boolean + get() = featureToggles.isFeatureEnabled(FeatureToggles.TWI_1377_MANAGE_FUNDS) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 3a66c7a075..10e80ff7c5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -125,6 +125,12 @@ internal class DefaultWalletRouter @Inject constructor( ) } + override fun openTransfer(userWalletId: UserWalletId) { + dialogNavigation.activate( + configuration = WalletDialogConfig.Transfer(userWalletId = userWalletId), + ) + } + override fun isWalletLastScreen(): Boolean { return router.stack.lastOrNull() is AppRoute.Wallet } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index 456ee387ec..eed653fcfe 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -120,4 +120,7 @@ internal interface InnerWalletRouter { /** Open Add Funds screen */ fun openAddFunds(userWalletId: UserWalletId) + + /** Open Transfer screen */ + fun openTransfer(userWalletId: UserWalletId) } \ No newline at end of file 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 158d3ad6e5..31daa69aca 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 @@ -56,7 +56,7 @@ internal sealed class WalletActionButtons( override val isEnabled: Boolean, ) : WalletActionButtons( text = resourceReference(R.string.common_add_funds), - iconRes = R.drawable.ic_plus_default_24, + iconRes = R.drawable.ic_arrow_down_24, ) data class Swap( @@ -74,4 +74,12 @@ internal sealed class WalletActionButtons( text = resourceReference(R.string.common_sell), iconRes = R.drawable.ic_dollar_default_24, ) + + data class Transfer( + override val onClick: () -> Unit, + override val isEnabled: Boolean, + ) : WalletActionButtons( + text = resourceReference(R.string.common_transfer), + iconRes = R.drawable.ic_arrow_up_24, + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletDialogConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletDialogConfig.kt index afa05fb850..a21b1a2fb5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletDialogConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletDialogConfig.kt @@ -65,6 +65,9 @@ internal sealed interface WalletDialogConfig { @Serializable data class AddFunds(val userWalletId: UserWalletId) : WalletDialogConfig + @Serializable + data class Transfer(val userWalletId: UserWalletId) : WalletDialogConfig + @Serializable data class OrganizeTokens(val userWalletId: UserWalletId) : WalletDialogConfig diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletManageButton.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletManageButton.kt index f2ab77907e..449087b947 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletManageButton.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletManageButton.kt @@ -58,7 +58,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) { ) : WalletManageButton( config = ActionButtonConfig( text = TextReference.Res(id = R.string.common_add_funds), - iconResId = R.drawable.ic_plus_24, + iconResId = R.drawable.ic_arrow_down_24, onClick = onClick, isEnabled = enabled, shouldDimContent = dimContent, @@ -145,6 +145,29 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) { ), ) + /** + * Transfer + * + * @property enabled button click availability + * @property dimContent determines whether the button content will be dimmed + * @property onClick lambda be invoked when Transfer button is clicked + */ + data class Transfer( + override val enabled: Boolean, + override val dimContent: Boolean, + override val onClick: () -> Unit, + val isInProgress: Boolean = false, + ) : WalletManageButton( + config = ActionButtonConfig( + text = TextReference.Res(id = R.string.common_transfer), + iconResId = R.drawable.ic_arrow_up_24, + onClick = onClick, + isEnabled = enabled, + shouldDimContent = dimContent, + isInProgress = isInProgress, + ), + ) + /** * Swap * diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/AddWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/AddWalletTransformer.kt index f41f1471ae..f33e2908eb 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/AddWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/AddWalletTransformer.kt @@ -14,6 +14,7 @@ internal class AddWalletTransformer( private val walletImageResolver: WalletImageResolver, private val getWalletIconUseCase: GetWalletIconUseCase, private val isAddFundsStage1Enabled: Boolean, + private val isManageFundsEnabled: Boolean, ) : WalletScreenStateTransformer { private val walletLoadingStateFactory by lazy { @@ -22,6 +23,7 @@ internal class AddWalletTransformer( walletImageResolver = walletImageResolver, getWalletIconUseCase = getWalletIconUseCase, isAddFundsStage1Enabled = isAddFundsStage1Enabled, + isManageFundsEnabled = isManageFundsEnabled, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/InitializeWalletsTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/InitializeWalletsTransformer.kt index d6ac34d9eb..edfe70df42 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/InitializeWalletsTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/InitializeWalletsTransformer.kt @@ -21,6 +21,7 @@ import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toPersistentList import com.tangem.core.ui.R as CoreUiR +@Suppress("LongParameterList") internal class InitializeWalletsTransformer( private val selectedWalletIndex: Int, private val wallets: List, @@ -28,6 +29,7 @@ internal class InitializeWalletsTransformer( private val walletImageResolver: WalletImageResolver, private val getWalletIconUseCase: GetWalletIconUseCase, private val isAddFundsStage1Enabled: Boolean, + private val isManageFundsEnabled: Boolean, ) : WalletScreenStateTransformer { private val walletLoadingStateFactory by lazy { @@ -36,6 +38,7 @@ internal class InitializeWalletsTransformer( walletImageResolver = walletImageResolver, getWalletIconUseCase = getWalletIconUseCase, isAddFundsStage1Enabled = isAddFundsStage1Enabled, + isManageFundsEnabled = isManageFundsEnabled, ) } @@ -159,10 +162,16 @@ internal class InitializeWalletsTransformer( WalletManageButton.Buy(enabled = false, dimContent = false, onClick = {}) } + val lastButton = if (isManageFundsEnabled) { + WalletManageButton.Transfer(enabled = false, dimContent = false, onClick = {}) + } else { + WalletManageButton.Sell(enabled = false, dimContent = false, onClick = {}) + } + return persistentListOf( firstButton, WalletManageButton.Swap(enabled = false, dimContent = false, onClick = {}), - WalletManageButton.Sell(enabled = false, dimContent = false, onClick = {}), + lastButton, ) } @@ -190,12 +199,21 @@ internal class InitializeWalletsTransformer( onClick = {}, ).buttonUM, ) - add( - WalletActionButtons.Sell( - isEnabled = false, - onClick = {}, - ).buttonUM, - ) + if (isManageFundsEnabled) { + add( + WalletActionButtons.Transfer( + isEnabled = false, + onClick = {}, + ).buttonUM, + ) + } else { + add( + WalletActionButtons.Sell( + isEnabled = false, + onClick = {}, + ).buttonUM, + ) + } }.toPersistentList() } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeNewWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeNewWalletTransformer.kt index 2a7f70ad83..66fa7bb5b2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeNewWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeNewWalletTransformer.kt @@ -18,6 +18,7 @@ import kotlinx.collections.immutable.toImmutableList * [REDACTED_AUTHOR] */ +@Suppress("LongParameterList") internal class ReinitializeNewWalletTransformer( private val prevWalletId: UserWalletId, private val newUserWallet: UserWallet, @@ -25,6 +26,7 @@ internal class ReinitializeNewWalletTransformer( private val walletImageResolver: WalletImageResolver, private val getWalletIconUseCase: GetWalletIconUseCase, private val isAddFundsStage1Enabled: Boolean, + private val isManageFundsEnabled: Boolean, ) : WalletScreenStateTransformer { private val walletLoadingStateFactory by lazy { @@ -33,6 +35,7 @@ internal class ReinitializeNewWalletTransformer( walletImageResolver = walletImageResolver, getWalletIconUseCase = getWalletIconUseCase, isAddFundsStage1Enabled = isAddFundsStage1Enabled, + isManageFundsEnabled = isManageFundsEnabled, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeWalletTransformer.kt index 1729b92b47..5032c7975a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeWalletTransformer.kt @@ -21,6 +21,7 @@ internal class ReinitializeWalletTransformer( private val walletImageResolver: WalletImageResolver, private val getWalletIconUseCase: GetWalletIconUseCase, private val isAddFundsStage1Enabled: Boolean, + private val isManageFundsEnabled: Boolean, ) : WalletStateTransformer(userWalletId = userWallet.walletId) { private val walletLoadingStateFactory by lazy { @@ -29,6 +30,7 @@ internal class ReinitializeWalletTransformer( walletImageResolver = walletImageResolver, getWalletIconUseCase = getWalletIconUseCase, isAddFundsStage1Enabled = isAddFundsStage1Enabled, + isManageFundsEnabled = isManageFundsEnabled, ) } 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 9ad9b59660..c90b41d9bb 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 @@ -87,6 +87,7 @@ internal class SetRefreshStateTransformer( is WalletManageButton.AddFunds -> button.copy(enabled = isButtonsEnabled) is WalletManageButton.Send -> button.copy(enabled = isButtonsEnabled) is WalletManageButton.Sell -> button.copy(enabled = isButtonsEnabled) + is WalletManageButton.Transfer -> button.copy(enabled = isButtonsEnabled) is WalletManageButton.Receive -> button is WalletManageButton.Stake -> null is WalletManageButton.Swap -> null diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UnlockWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UnlockWalletTransformer.kt index 5b4c0c0404..c64e5fa443 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UnlockWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UnlockWalletTransformer.kt @@ -19,6 +19,7 @@ internal class UnlockWalletTransformer( private val walletImageResolver: WalletImageResolver, private val getWalletIconUseCase: GetWalletIconUseCase, private val isAddFundsStage1Enabled: Boolean, + private val isManageFundsEnabled: Boolean, ) : WalletScreenStateTransformer { private val walletLoadingStateFactory by lazy { @@ -27,6 +28,7 @@ internal class UnlockWalletTransformer( walletImageResolver = walletImageResolver, getWalletIconUseCase = getWalletIconUseCase, isAddFundsStage1Enabled = isAddFundsStage1Enabled, + isManageFundsEnabled = isManageFundsEnabled, ) } 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 095f5d5654..2147e17885 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 @@ -45,6 +45,7 @@ private fun WalletState.MultiCurrency.Content.changeAvailability(enabled: Boolea is WalletManageButton.Buy -> action.copy(enabled = enabled) is WalletManageButton.AddFunds -> action.copy(enabled = enabled) is WalletManageButton.Sell -> action.copy(enabled = enabled) + is WalletManageButton.Transfer -> action.copy(enabled = enabled) is WalletManageButton.Swap -> action.copy(enabled = enabled) else -> action } 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 08d096da53..1410822ee8 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 @@ -36,6 +36,7 @@ internal class WalletLoadingStateFactory( private val walletImageResolver: WalletImageResolver, private val getWalletIconUseCase: GetWalletIconUseCase, private val isAddFundsStage1Enabled: Boolean, + private val isManageFundsEnabled: Boolean, ) { fun create(userWallet: UserWallet): WalletState { @@ -171,6 +172,20 @@ internal class WalletLoadingStateFactory( ) } + val lastButton = if (isManageFundsEnabled) { + WalletManageButton.Transfer( + enabled = true, + dimContent = false, + onClick = { clickIntents.onTransferClick(userWallet.walletId) }, + ) + } else { + WalletManageButton.Sell( + enabled = true, + dimContent = false, + onClick = { clickIntents.onMultiWalletSellClick(userWalletId = userWallet.walletId) }, + ) + } + return persistentListOf( firstButton, WalletManageButton.Swap( @@ -178,11 +193,7 @@ internal class WalletLoadingStateFactory( dimContent = false, onClick = { clickIntents.onMultiWalletSwapClick(userWalletId = userWallet.walletId) }, ), - WalletManageButton.Sell( - enabled = true, - dimContent = false, - onClick = { clickIntents.onMultiWalletSellClick(userWalletId = userWallet.walletId) }, - ), + lastButton, ) } @@ -205,14 +216,25 @@ internal class WalletLoadingStateFactory( }, ).buttonUM, ) - add( - WalletActionButtons.Sell( - isEnabled = false, - onClick = { - clickIntents.onMultiWalletSellClick(userWalletId = userWallet.walletId) - }, - ).buttonUM, - ) + if (isManageFundsEnabled) { + add( + WalletActionButtons.Transfer( + isEnabled = false, + onClick = { + clickIntents.onTransferClick(userWalletId = userWallet.walletId) + }, + ).buttonUM, + ) + } else { + add( + WalletActionButtons.Sell( + isEnabled = false, + onClick = { + clickIntents.onMultiWalletSellClick(userWalletId = userWallet.walletId) + }, + ).buttonUM, + ) + } }.toPersistentList() }