Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-26 00:26:27 +04:00
parent ff2559df3e
commit 0bbc78f821
15 changed files with 368 additions and 17 deletions

View file

@ -28,4 +28,8 @@ internal class DefaultSwapFeatureToggles @Inject constructor(
override val isSwapRateExperienceEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.AND_15103_SWAP_RATE_EXPERIENCE_ENABLED,
)
override val isSwapPredefinedButtonsEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
toggle = FeatureToggles.AND_15122_SWAP_PREDEFINED_BUTTONS_ENABLED,
)
}

View file

@ -62,7 +62,9 @@ import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
import com.tangem.domain.stories.ShouldShowStoriesUseCase
import com.tangem.domain.stories.models.StoryContentIds
import com.tangem.domain.swap.models.PredefinedPercentAmount
import com.tangem.domain.swap.models.SwapCurrencyStatus
import com.tangem.domain.swap.usecase.CalculateAmountUseCase
import com.tangem.domain.tangempay.GetTangemPayCustomerIdUseCase
import com.tangem.domain.tangempay.TangemPayWithdrawUseCase
import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
@ -162,6 +164,7 @@ internal class SwapModel @Inject constructor(
swapFeatureToggles: SwapFeatureToggles,
private val getSwapUiModeUseCase: GetSwapUiModeUseCase,
private val setSwapUiModeUseCase: SetSwapUiModeUseCase,
private val calculateAmountUseCase: CalculateAmountUseCase,
) : Model() {
private val params = paramsContainer.require<SwapComponent.Params>()
@ -1514,6 +1517,25 @@ internal class SwapModel @Inject constructor(
}
}
private fun onPredefinedPercentSelected(percent: PredefinedPercentAmount) {
if (percent == PredefinedPercentAmount.MAX) {
onMaxAmountClicked()
return
}
val fromCurrency = dataState.fromSwapCurrencyStatus ?: return
val newValue = calculateAmountUseCase(
balance = fromCurrency.status.value.amount ?: BigDecimal.ZERO,
decimals = fromCurrency.status.currency.decimals,
percent = percent,
)
onAmountChanged(
SwapAmount(
value = newValue,
decimals = fromCurrency.status.currency.decimals,
).formatToUIRepresentation(),
)
}
private fun onReduceAmountClicked(newAmount: SwapAmount, reduceBalanceBy: BigDecimal = BigDecimal.ZERO) {
onAmountChanged(
value = newAmount.formatToUIRepresentation(),
@ -1658,6 +1680,7 @@ internal class SwapModel @Inject constructor(
}
},
onMaxAmountSelected = ::onMaxAmountClicked,
onPredefinedPercentSelected = ::onPredefinedPercentSelected,
onReduceToAmount = ::onReduceAmountClicked,
onReduceByAmount = ::onReduceAmountClicked,
openPermissionBottomSheet = {

View file

@ -8,6 +8,7 @@ import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.swap.models.PredefinedPercentAmount
import com.tangem.feature.swap.domain.models.domain.SwapUIMode
import com.tangem.feature.swap.domain.models.ui.PriceImpact
import com.tangem.feature.swap.models.states.ProviderState
@ -32,6 +33,7 @@ internal data class SwapStateHolder(
val tosState: TosState? = null,
val swapUIMode: SwapUIMode = SwapUIMode.Detailed,
val shouldShowAbMenu: Boolean = false,
val isPredefinedButtonsEnabled: Boolean = false,
val transferFooter: TextReference? = null,
@ -41,6 +43,7 @@ internal data class SwapStateHolder(
val onSelectTokenClick: ((TokenSelectionDirection) -> Unit),
val onSuccess: (() -> Unit),
val onMaxAmountSelected: (() -> Unit)? = null,
val onPredefinedPercentSelected: ((PredefinedPercentAmount) -> Unit)? = null,
val onShowPermissionBottomSheet: () -> Unit = {},
val onSwapUIModeChange: (SwapUIMode) -> Unit = {},
)

View file

@ -2,6 +2,7 @@ package com.tangem.feature.swap.models
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.express.models.ProviderFilterType
import com.tangem.domain.swap.models.PredefinedPercentAmount
import com.tangem.feature.swap.domain.models.SwapAmount
import com.tangem.feature.swap.domain.models.domain.SwapUIMode
import java.math.BigDecimal
@ -14,6 +15,7 @@ internal data class UiActions(
val onChangeCardsClicked: () -> Unit,
val onBackClicked: () -> Unit,
val onMaxAmountSelected: () -> Unit,
val onPredefinedPercentSelected: (PredefinedPercentAmount) -> Unit,
val onReduceToAmount: (SwapAmount) -> Unit,
val onReduceByAmount: (SwapAmount, reduceBy: BigDecimal) -> Unit,
val openPermissionBottomSheet: () -> Unit,

View file

@ -97,6 +97,7 @@ internal class StateBuilder(
onBackClicked = actions.onBackClicked,
onChangeCardsClicked = actions.onChangeCardsClicked,
onMaxAmountSelected = actions.onMaxAmountSelected,
onPredefinedPercentSelected = actions.onPredefinedPercentSelected,
changeCardsButtonState = ChangeCardsButtonState.DISABLED,
onShowPermissionBottomSheet = actions.openPermissionBottomSheet,
onSelectTokenClick = actions.onSelectTokenClick,
@ -108,6 +109,7 @@ internal class StateBuilder(
swapUIMode = swapUIMode,
onSwapUIModeChange = actions.onSwapUIModeChange,
shouldShowAbMenu = swapFeatureToggles.isSwapAbEnabled,
isPredefinedButtonsEnabled = swapFeatureToggles.isSwapPredefinedButtonsEnabled,
)
}

View file

@ -33,13 +33,17 @@ import androidx.constraintlayout.compose.ConstraintLayout
import com.tangem.common.ui.footers.SendingText
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.buttons.predefined.PredefinedPercentButtonUM
import com.tangem.core.ui.components.buttons.predefined.PredefinedPercentButtonsRow
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.SwapTokenScreenTestTags
import com.tangem.domain.swap.models.PredefinedPercentAmount
import com.tangem.feature.swap.domain.models.domain.SwapUIMode
import com.tangem.feature.swap.domain.models.ui.PriceImpact
import com.tangem.feature.swap.models.*
@ -49,6 +53,7 @@ import com.tangem.feature.swap.presentation.R
import com.tangem.feature.swap.ui.preview.SwapTransactionCardPreview.receiveCard
import com.tangem.feature.swap.ui.preview.SwapTransactionCardPreview.sendCard
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@Suppress("LongMethod")
@Composable
@ -111,26 +116,49 @@ internal fun SwapScreenContent(
}
if (state.shouldShowMaxAmount && keyboard is Keyboard.Opened) {
Text(
text = stringResourceSafe(id = R.string.send_max_amount_label),
style = TangemTheme.typography.button,
color = TangemTheme.colors.text.primary1,
modifier = Modifier
.align(Alignment.BottomCenter)
.imePadding()
.fillMaxWidth()
.background(TangemTheme.colors.button.secondary)
.clickable { state.onMaxAmountSelected?.invoke() }
.padding(
horizontal = TangemTheme.dimens.spacing14,
vertical = TangemTheme.dimens.spacing16,
),
textAlign = TextAlign.Start,
)
val onPercentClick = state.onPredefinedPercentSelected
if (state.isPredefinedButtonsEnabled && onPercentClick != null) {
PredefinedPercentButtonsRow(
items = PredefinedPercentAmount.entries.map { percent ->
PredefinedPercentButtonUM(
id = percent.name,
label = percent.toLabel(),
onClick = { onPercentClick(percent) },
)
}.toImmutableList(),
modifier = Modifier
.align(Alignment.BottomCenter)
.imePadding(),
)
} else {
Text(
text = stringResourceSafe(id = R.string.send_max_amount_label),
style = TangemTheme.typography.button,
color = TangemTheme.colors.text.primary1,
modifier = Modifier
.align(Alignment.BottomCenter)
.imePadding()
.fillMaxWidth()
.background(TangemTheme.colors.button.secondary)
.clickable { state.onMaxAmountSelected?.invoke() }
.padding(
horizontal = TangemTheme.dimens.spacing14,
vertical = TangemTheme.dimens.spacing16,
),
textAlign = TextAlign.Start,
)
}
}
}
}
private fun PredefinedPercentAmount.toLabel() = when (this) {
PredefinedPercentAmount.PERCENT_25 -> stringReference("25%")
PredefinedPercentAmount.PERCENT_50 -> stringReference("50%")
PredefinedPercentAmount.PERCENT_75 -> stringReference("75%")
PredefinedPercentAmount.MAX -> resourceReference(R.string.send_max_amount)
}
@Composable
private fun MainInfo(state: SwapStateHolder) {
ConstraintLayout(

View file

@ -52,7 +52,7 @@ internal class StateBuilderInitialStateTest {
isAccountsModeProvider = isAccountsModeProvider,
isGaslessFeeSupportedForNetwork = isGaslessFeeSupportedForNetwork,
swapFeatureToggles = swapFeatureToggles,
appRouter = appRouter
appRouter = appRouter,
)
}