Updated on 2026-08-14
This commit is contained in:
parent
4d75db3cdd
commit
22dc2eb3e9
57 changed files with 931 additions and 258 deletions
|
|
@ -2,4 +2,5 @@ package com.tangem.features.tokendetails
|
|||
|
||||
interface TokenDetailsFeatureToggles {
|
||||
val isQuickTopUpEnabled: Boolean
|
||||
val isManageFundsEnabled: Boolean
|
||||
}
|
||||
|
|
@ -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,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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 */ }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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> {
|
|||
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,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue