Updated on 2026-08-14
This commit is contained in:
parent
4d75db3cdd
commit
22dc2eb3e9
57 changed files with 931 additions and 258 deletions
|
|
@ -44,4 +44,8 @@ dependencies {
|
|||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
||||
/** Test */
|
||||
testImplementation(projects.test.core)
|
||||
testImplementation(projects.common.test)
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TokenActionsState.ActionState>, 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<TokenActionsState.ActionState>): ImmutableList<QuickActionUM> {
|
||||
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<TokenActionsState.ActionState>,
|
||||
isRedesignEnabled: Boolean,
|
||||
context: TokenActionsContext = TokenActionsContext.Markets,
|
||||
): ImmutableList<QuickActionUM> {
|
||||
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<TokenActionsState.ActionState>): ImmutableList<QuickActionUM> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<Action>?,
|
||||
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,
|
||||
),
|
||||
}
|
||||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<AccountStatus.CryptoPortfolio>(relaxed = true),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `GIVEN AddFunds context WHEN handle Exchange THEN swap pushed with TO position`() {
|
||||
// Arrange
|
||||
val slot = slot<Route>()
|
||||
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<Route>()
|
||||
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<Route>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue