Updated on 2026-08-14

This commit is contained in:
Tangem 2024-11-01 17:47:18 +02:00
parent 8202b6bf2a
commit ab85772d10
47 changed files with 356 additions and 116 deletions

View file

@ -30,9 +30,10 @@ internal data class BalanceState(
val subtitle: TextReference?,
val isClickable: Boolean,
val cryptoValue: String,
val cryptoDecimal: BigDecimal,
val cryptoAmount: TextReference,
val fiatAmount: TextReference,
val cryptoAmount: BigDecimal,
val formattedCryptoAmount: TextReference,
val fiatAmount: BigDecimal?,
val formattedFiatAmount: TextReference,
val rawCurrencyId: String?,
val validator: Yield.Validator?,
val pendingActions: ImmutableList<PendingAction>,

View file

@ -75,6 +75,7 @@ internal class StakingStateController @Inject constructor(
walletName = "",
cryptoCurrencyName = "",
cryptoCurrencySymbol = "",
cryptoCurrencyNetworkId = "",
currentStep = StakingStep.InitialInfo,
initialInfoState = StakingStates.InitialInfoState.Empty(),
amountState = AmountState.Empty(),
@ -86,6 +87,7 @@ internal class StakingStateController @Inject constructor(
bottomSheetConfig = null,
actionType = StakingActionCommonType.Enter,
buttonsState = NavigationButtonsState.Empty,
balanceState = null,
)
}
}

View file

@ -5,6 +5,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.staking.analytics.StakingAnalyticsEvent
import com.tangem.features.staking.impl.analytics.utils.StakingAnalyticSender
import com.tangem.lib.crypto.BlockchainUtils.isSolana
internal class StakingStateRouter(
private val appRouter: AppRouter,
@ -24,8 +25,13 @@ internal class StakingStateRouter(
when (stateController.value.currentStep) {
StakingStep.InitialInfo -> when (stateController.value.actionType) {
StakingActionCommonType.Enter -> showAmount()
// TODO staking [REDACTED_TASK_KEY] support solana multisize hashes signing
StakingActionCommonType.Exit -> if (isSolana(stateController.value.cryptoCurrencyNetworkId)) {
showConfirmation()
} else {
showAmount()
}
StakingActionCommonType.Pending.Other,
StakingActionCommonType.Exit,
StakingActionCommonType.Pending.Rewards,
-> showConfirmation()
StakingActionCommonType.Pending.Restake -> showRestakeValidators()

View file

@ -29,6 +29,7 @@ internal data class StakingUiState(
val walletName: String,
val cryptoCurrencyName: String,
val cryptoCurrencySymbol: String,
val cryptoCurrencyNetworkId: String,
val currentStep: StakingStep,
val initialInfoState: StakingStates.InitialInfoState,
val amountState: AmountState,
@ -40,6 +41,7 @@ internal data class StakingUiState(
val actionType: StakingActionCommonType,
val buttonsState: NavigationButtonsState,
val event: StateEvent<StakingEvent>,
val balanceState: BalanceState?,
) {
fun copyWrapped(

View file

@ -47,11 +47,12 @@ internal class BalanceItemConverter(
subtitle = getSubtitle(value),
type = value.type,
cryptoValue = cryptoAmount.parseBigDecimal(cryptoCurrency.decimals),
cryptoDecimal = cryptoAmount,
cryptoAmount = stringReference(
cryptoAmount = cryptoAmount,
formattedCryptoAmount = stringReference(
cryptoAmount.format { crypto(cryptoCurrency) },
),
fiatAmount = stringReference(
fiatAmount = fiatAmount,
formattedFiatAmount = stringReference(
BigDecimalFormatter.formatFiatAmount(
fiatAmount = fiatAmount,
fiatCurrencyCode = appCurrency.code,

View file

@ -71,7 +71,7 @@ internal class RewardsValidatorStateConverter(
crypto(cryptoCurrency)
},
)
val fiatAmount = stringReference(
val formattedFiatAmount = stringReference(
BigDecimalFormatter.formatFiatAmount(
fiatAmount = fiatValue,
fiatCurrencyCode = appCurrency.code,
@ -85,9 +85,10 @@ internal class RewardsValidatorStateConverter(
title = stringReference(this.name),
subtitle = null,
cryptoValue = cryptoValue.parseBigDecimal(cryptoCurrency.decimals),
cryptoDecimal = cryptoValue,
cryptoAmount = cryptoAmount,
fiatAmount = fiatAmount,
cryptoAmount = cryptoValue,
formattedCryptoAmount = cryptoAmount,
fiatAmount = fiatValue,
formattedFiatAmount = formattedFiatAmount,
rawCurrencyId = cryptoCurrency.id.rawCurrencyId,
pendingActions = balance.pendingActions.toPersistentList(),
isClickable = true,

View file

@ -63,7 +63,7 @@ internal class YieldBalancesConverter(
private fun List<BalanceItem>.mapBalances() = asSequence()
.filterNot { it.amount.isZero() || it.type == BalanceType.REWARDS }
.mapNotNull(balanceItemConverter::convert)
.sortedByDescending { it.cryptoDecimal }
.sortedByDescending { it.cryptoAmount }
.sortedBy { it.type.order }
.toPersistentList()

View file

@ -20,7 +20,7 @@ import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.staking.impl.presentation.state.StakingStateController
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.state.utils.isComposePendingActions
import com.tangem.features.staking.impl.presentation.state.utils.isCompositePendingActions
import com.tangem.utils.extensions.orZero
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -95,7 +95,11 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor(
val sourceAddress = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
?: error("No available address")
val gasEstimate = if (isComposePendingActions(cryptoCurrencyStatus.currency.network.id.value, pendingActions)) {
val gasEstimate = if (isCompositePendingActions(
networkId = cryptoCurrencyStatus.currency.network.id.value,
pendingActions = pendingActions,
)
) {
val result = coroutineScope {
pendingActions?.map { action ->
async {

View file

@ -20,7 +20,7 @@ import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.staking.impl.presentation.state.*
import com.tangem.features.staking.impl.presentation.state.utils.checkAndCalculateSubtractedAmount
import com.tangem.features.staking.impl.presentation.state.utils.isComposePendingActions
import com.tangem.features.staking.impl.presentation.state.utils.isCompositePendingActions
import com.tangem.utils.extensions.orZero
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -96,7 +96,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
confirmationState: StakingStates.ConfirmationState.Data,
onConstructError: (StakingError) -> Unit,
) = coroutineScope {
val isComposePendingActions = isComposePendingActions(
val isComposePendingActions = isCompositePendingActions(
cryptoCurrencyStatus.currency.network.id.value,
confirmationState.pendingActions,
)

View file

@ -73,9 +73,10 @@ internal object InitialStakingStatePreview {
groupId = "groupId",
title = stringReference("Binance"),
cryptoValue = "100",
cryptoAmount = stringReference("100 SOL"),
cryptoDecimal = "100".toBigDecimal(),
fiatAmount = stringReference("100 $"),
formattedCryptoAmount = stringReference("100 SOL"),
cryptoAmount = "100".toBigDecimal(),
fiatAmount = null,
formattedFiatAmount = stringReference("100 $"),
rawCurrencyId = null,
validator = Yield.Validator(
address = "address",

View file

@ -26,7 +26,7 @@ internal object StakingClickIntentsStub : StakingClickIntents {
override fun onInfoClick(infoType: InfoType) {}
override fun onEnterClick() {}
override fun onAmountEnterClick() {}
override fun onAmountValueChange(value: String) {}

View file

@ -0,0 +1,55 @@
package com.tangem.features.staking.impl.presentation.state.transformers
import com.tangem.common.ui.amountScreen.converters.AmountStateConverter
import com.tangem.common.ui.amountScreen.models.AmountParameters
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.presentation.state.*
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
import com.tangem.utils.Provider
import com.tangem.utils.transformer.Transformer
internal class SetAmountDataTransformer(
private val clickIntents: StakingClickIntents,
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
private val userWalletProvider: Provider<UserWallet>,
private val appCurrencyProvider: Provider<AppCurrency>,
private val maxEnterAmountProvider: Provider<MaxEnterAmount>,
) : Transformer<StakingUiState> {
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
private val amountStateConverter by lazy(LazyThreadSafetyMode.NONE) {
AmountStateConverter(
clickIntents = clickIntents,
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
appCurrencyProvider = appCurrencyProvider,
iconStateConverter = iconStateConverter,
maxEnterAmountProvider = maxEnterAmountProvider,
)
}
override fun transform(prevState: StakingUiState): StakingUiState {
val title = if (prevState.actionType == StakingActionCommonType.Exit) {
resourceReference(R.string.staking_staked_amount)
} else {
stringReference(userWalletProvider().name)
}
return prevState.copy(
amountState = amountStateConverter.convert(
AmountParameters(
title = title,
value = "",
),
),
)
}
}

View file

@ -148,7 +148,7 @@ internal class SetButtonsStateTransformer(
StakingStep.Validators,
StakingStep.RestakeValidator,
-> clickIntents.onNextClick()
StakingStep.Amount -> clickIntents.onEnterClick()
StakingStep.Amount -> clickIntents.onAmountEnterClick()
StakingStep.Confirmation -> onConfirmationClick()
StakingStep.RewardsValidators -> Unit
}

View file

@ -11,6 +11,7 @@ internal object SetConfirmationStateEmptyTransformer : Transformer<StakingUiStat
actionType = StakingActionCommonType.Enter,
validatorState = StakingStates.ValidatorState.Empty(),
confirmationState = StakingStates.ConfirmationState.Empty(),
balanceState = null,
)
}
}

View file

@ -7,7 +7,7 @@ import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.staking.impl.presentation.state.*
import com.tangem.features.staking.impl.presentation.state.utils.isComposePendingActions
import com.tangem.features.staking.impl.presentation.state.utils.isCompositePendingActions
import com.tangem.features.staking.impl.presentation.state.utils.isTronStakedBalance
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.ImmutableList
@ -16,29 +16,32 @@ import java.math.BigDecimal
internal class SetConfirmationStateInitTransformer(
private val isEnter: Boolean,
private val isExplicitExit: Boolean,
private val balanceState: BalanceState?,
private val stakingApproval: StakingApproval,
private val stakingAllowance: BigDecimal,
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
private val pendingActions: ImmutableList<PendingAction>? = null,
private val pendingAction: PendingAction? = pendingActions?.firstOrNull(),
) : Transformer<StakingUiState> {
private val networkId
get() = cryptoCurrencyStatus.currency.network.id.value
private val isComposePendingActions
get() = isComposePendingActions(networkId, pendingActions)
get() = isCompositePendingActions(networkId, pendingActions)
private val isTronStakedBalance
get() = isTronStakedBalance(networkId, pendingAction)
private val isExit: Boolean
private val isImplicitExit: Boolean
get() = pendingAction == null && pendingActions?.isEmpty() == true || isTronStakedBalance
override fun transform(prevState: StakingUiState): StakingUiState {
val actionType = when {
isEnter -> StakingActionCommonType.Enter
isExit -> StakingActionCommonType.Exit
isImplicitExit || isExplicitExit -> StakingActionCommonType.Exit
else -> when (pendingAction?.type) {
StakingActionType.STAKE -> StakingActionCommonType.Enter
StakingActionType.UNSTAKE -> StakingActionCommonType.Exit
@ -54,6 +57,7 @@ internal class SetConfirmationStateInitTransformer(
return prevState.copy(
actionType = actionType,
balanceState = balanceState,
confirmationState = StakingStates.ConfirmationState.Data(
isPrimaryButtonEnabled = false,
innerState = InnerConfirmationStakingState.ASSENT,

View file

@ -2,7 +2,9 @@ package com.tangem.features.staking.impl.presentation.state.transformers
import com.tangem.common.extensions.remove
import com.tangem.common.ui.amountScreen.converters.AmountStateConverter
import com.tangem.common.ui.amountScreen.models.AmountParameters
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.components.list.RoundedListWithDividersItemData
import com.tangem.core.ui.extensions.*
@ -39,6 +41,7 @@ internal class SetInitialDataStateTransformer(
private val userWalletProvider: Provider<UserWallet>,
private val appCurrencyProvider: Provider<AppCurrency>,
private val balancesToShowProvider: Provider<List<BalanceItem>>,
private val maxEnterAmountProvider: Provider<MaxEnterAmount>,
) : Transformer<StakingUiState> {
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
@ -48,8 +51,8 @@ internal class SetInitialDataStateTransformer(
clickIntents = clickIntents,
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
appCurrencyProvider = appCurrencyProvider,
userWalletProvider = userWalletProvider,
iconStateConverter = iconStateConverter,
maxEnterAmountProvider = maxEnterAmountProvider,
)
}
@ -72,6 +75,7 @@ internal class SetInitialDataStateTransformer(
title = TextReference.EMPTY,
cryptoCurrencyName = cryptoCurrency.name,
cryptoCurrencySymbol = cryptoCurrency.symbol,
cryptoCurrencyNetworkId = cryptoCurrency.network.id.value,
clickIntents = clickIntents,
currentStep = StakingStep.InitialInfo,
initialInfoState = createInitialInfoState(),
@ -207,7 +211,12 @@ internal class SetInitialDataStateTransformer(
}
private fun createInitialAmountState(): AmountState {
return amountStateConverter.convert("")
return amountStateConverter.convert(
AmountParameters(
title = stringReference(userWalletProvider().name),
value = "",
),
)
}
private fun getAprRange(validators: List<Yield.Validator>): TextReference {

View file

@ -1,7 +1,10 @@
package com.tangem.features.staking.impl.presentation.state.transformers.amount
import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.utils.transformer.Transformer
@ -12,11 +15,21 @@ internal class AmountChangeStateTransformer(
private val yield: Yield,
) : Transformer<StakingUiState> {
private val maxEnterAmountConverter = MaxEnterAmountConverter()
override fun transform(prevState: StakingUiState): StakingUiState {
val updatedAmountState = AmountFieldChangeTransformer(
cryptoCurrencyStatus,
value,
).transform(prevState.amountState)
val actionType = prevState.actionType
val maxEnterAmount = if (actionType == StakingActionCommonType.Exit) {
MaxEnterAmount(
amount = prevState.balanceState?.cryptoAmount,
fiatAmount = prevState.balanceState?.fiatAmount,
fiatRate = cryptoCurrencyStatus.value.fiatRate,
)
} else {
maxEnterAmountConverter.convert(cryptoCurrencyStatus)
}
val updatedAmountState = AmountFieldChangeTransformer(maxEnterAmount, value).transform(prevState.amountState)
return prevState.copy(
amountState = AmountRequirementStateTransformer(

View file

@ -1,18 +1,35 @@
package com.tangem.features.staking.impl.presentation.state.transformers.amount
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldMaxAmountTransformer
import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldSetMaxAmountTransformer
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.utils.transformer.Transformer
internal class AmountMaxValueStateTransformer(
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
private val actionType: StakingActionCommonType,
private val yield: Yield,
) : Transformer<StakingUiState> {
private val maxEnterAmountConverter = MaxEnterAmountConverter()
override fun transform(prevState: StakingUiState): StakingUiState {
val updatedAmountState = AmountFieldMaxAmountTransformer(cryptoCurrencyStatus).transform(prevState.amountState)
val maxEnterAmount = if (actionType == StakingActionCommonType.Exit) {
MaxEnterAmount(
amount = prevState.balanceState?.cryptoAmount,
fiatAmount = prevState.balanceState?.fiatAmount,
fiatRate = cryptoCurrencyStatus.value.fiatRate,
)
} else {
maxEnterAmountConverter.convert(cryptoCurrencyStatus)
}
val updatedAmountState = AmountFieldSetMaxAmountTransformer(maxEnterAmount)
.transform(prevState.amountState)
return prevState.copy(
amountState = AmountRequirementStateTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,

View file

@ -61,10 +61,17 @@ internal class AmountRequirementStateTransformer(
},
),
)
isIntegerOnlyError -> resourceReference(
R.string.staking_amount_tron_integer_error,
wrappedList(value),
)
isIntegerOnlyError -> when (actionType) {
StakingActionCommonType.Enter -> resourceReference(
R.string.staking_amount_tron_integer_error,
wrappedList(value),
)
StakingActionCommonType.Exit -> resourceReference(
R.string.staking_amount_tron_integer_error_unstaking,
wrappedList(value),
)
else -> TODO()
}
else -> TextReference.EMPTY
}
val isError = amountState.amountTextField.isError || isRequirementError
@ -101,12 +108,12 @@ internal class AmountRequirementStateTransformer(
private fun isIntegerOnlyError(amountState: AmountState.Data, actionType: StakingActionCommonType): Boolean {
val cryptoAmountValue = amountState.amountTextField.cryptoAmount.value ?: return false
val isEnter = actionType == StakingActionCommonType.Enter
val isEnterOrExit = actionType == StakingActionCommonType.Enter || actionType == StakingActionCommonType.Exit
val isTron = isTron(cryptoCurrencyStatus.currency.network.id.value)
val isIntegerOnly = cryptoAmountValue.isZero() || cryptoAmountValue.remainder(BigDecimal.ONE).isZero()
return isEnter && isTron && !isIntegerOnly
return isEnterOrExit && isTron && !isIntegerOnly
}
data class Data(

View file

@ -35,12 +35,12 @@ internal fun StakingActionType?.getPendingActionTitle(): TextReference = when (t
internal fun isSingleAction(networkId: String, activeStake: BalanceState): Boolean {
val isSingleAction = activeStake.pendingActions.size <= 1 // Either single or none pending actions
val isComposePendingActions = isComposePendingActions(networkId, activeStake.pendingActions)
val isCompositePendingActions = isCompositePendingActions(networkId, activeStake.pendingActions)
val isBscRestake = isBSC(networkId) && activeStake.pendingActions.any {
it.type == StakingActionType.RESTAKE
}
return isSingleAction && !isBscRestake || isComposePendingActions
return isSingleAction && !isBscRestake || isCompositePendingActions
}
internal fun withStubUnstakeAction(networkId: String, activeStake: BalanceState) = if (isBSC(networkId)) {
@ -59,7 +59,7 @@ internal fun isTronStakedBalance(networkId: String, pendingAction: PendingAction
return isTron(networkId) && pendingAction?.type == StakingActionType.REVOTE
}
internal fun isComposePendingActions(networkId: String, pendingActions: ImmutableList<PendingAction>?): Boolean {
internal fun isCompositePendingActions(networkId: String, pendingActions: ImmutableList<PendingAction>?): Boolean {
return when {
isSolana(networkId) -> pendingActions?.any { it.type == StakingActionType.WITHDRAW } == true
else -> false

View file

@ -47,8 +47,8 @@ internal fun StakingClaimRewardsValidatorContent(
)
},
),
infoTitle = item.fiatAmount,
infoSubtitle = item.cryptoAmount,
infoTitle = item.formattedFiatAmount,
infoSubtitle = item.formattedCryptoAmount,
imageUrl = item.validator?.image.orEmpty(),
onImageError = { ValidatorImagePlaceholder() },
modifier = modifier

View file

@ -270,8 +270,8 @@ private fun ActiveStakingBlock(
InputRowImageInfo(
subtitle = balance.title,
caption = balance.subtitle ?: balance.getAprText(),
infoTitle = balance.fiatAmount.orMaskWithStars(isBalanceHidden),
infoSubtitle = balance.cryptoAmount.orMaskWithStars(isBalanceHidden),
infoTitle = balance.formattedFiatAmount.orMaskWithStars(isBalanceHidden),
infoSubtitle = balance.formattedCryptoAmount.orMaskWithStars(isBalanceHidden),
imageUrl = balance.getImage(),
iconRes = icon,
iconTint = iconTint,

View file

@ -26,7 +26,7 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
fun onInfoClick(infoType: InfoType)
fun onEnterClick()
fun onAmountEnterClick()
fun getFee()

View file

@ -11,6 +11,7 @@ import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.bundle.unbundle
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.MaxEnterAmount
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig
import com.tangem.common.ui.notifications.NotificationUM
@ -31,10 +32,6 @@ import com.tangem.domain.staking.InvalidatePendingTransactionsUseCase
import com.tangem.domain.staking.IsAnyTokenStakedUseCase
import com.tangem.domain.staking.IsApproveNeededUseCase
import com.tangem.domain.staking.model.StakingApproval
import com.tangem.domain.staking.model.stakekit.BalanceItem
import com.tangem.domain.staking.model.stakekit.PendingAction
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.YieldBalance
import com.tangem.domain.staking.model.stakekit.action.StakingAction
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
@ -52,6 +49,7 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.staking.analytics.StakeScreenSource
import com.tangem.domain.staking.analytics.StakingAnalyticsEvent
import com.tangem.domain.staking.model.stakekit.*
import com.tangem.features.staking.impl.analytics.StakingParamsInterceptor
import com.tangem.features.staking.impl.analytics.utils.StakingAnalyticSender
import com.tangem.features.staking.impl.navigation.InnerStakingRouter
@ -156,6 +154,14 @@ internal class StakingViewModel @Inject constructor(
).getOrElse { emptyList() }
}
private val maxEnterAmount: MaxEnterAmount
get() =
MaxEnterAmount(
amount = uiState.value.balanceState?.cryptoAmount,
fiatAmount = uiState.value.balanceState?.fiatAmount,
fiatRate = cryptoCurrencyStatus.value.fiatRate,
)
private var isInitialInfoAnalyticSent: Boolean = false
private val balanceUpdater by lazy(LazyThreadSafetyMode.NONE) {
@ -227,6 +233,18 @@ internal class StakingViewModel @Inject constructor(
}
override fun onNextClick(balanceState: BalanceState?) {
if (value.currentStep == StakingStep.InitialInfo && balanceState == null) {
stateController.update(
SetConfirmationStateInitTransformer(
isEnter = true,
isExplicitExit = false,
balanceState = null,
cryptoCurrencyStatus = cryptoCurrencyStatus,
stakingApproval = stakingApproval,
stakingAllowance = stakingAllowance,
),
)
}
stakingStateRouter.onNextClick()
}
@ -350,24 +368,20 @@ internal class StakingViewModel @Inject constructor(
)
}
override fun onEnterClick() {
override fun onAmountEnterClick() {
if (yield.preferredValidators.isEmpty()) {
stateController.updateEvent(
StakingEvent.ShowAlert(StakingAlertUM.NoAvailableValidators),
)
} else {
stateController.updateAll(
SetConfirmationStateInitTransformer(
isEnter = true,
cryptoCurrencyStatus = cryptoCurrencyStatus,
stakingApproval = stakingApproval,
stakingAllowance = stakingAllowance,
),
ValidatorSelectChangeTransformer(
selectedValidator = null,
yield = yield,
),
)
if (uiState.value.actionType == StakingActionCommonType.Enter) {
stateController.updateAll(
ValidatorSelectChangeTransformer(
selectedValidator = null,
yield = yield,
),
)
}
onNextClick()
}
}
@ -382,7 +396,13 @@ internal class StakingViewModel @Inject constructor(
override fun onMaxValueClick() {
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonMax)
stateController.update(AmountMaxValueStateTransformer(cryptoCurrencyStatus, yield))
stateController.update(
AmountMaxValueStateTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
actionType = uiState.value.actionType,
yield = yield,
),
)
}
override fun onCurrencyChangeClick(isFiat: Boolean) {
@ -439,23 +459,27 @@ internal class StakingViewModel @Inject constructor(
val networkId = cryptoCurrencyStatus.currency.network.id.value
if (isSingleAction(networkId, activeStake)) {
prepareForConfirmation(
balanceType = activeStake.type,
pendingActions = activeStake.pendingActions,
balanceState = activeStake,
validator = activeStake.validator,
amountValue = activeStake.cryptoValue,
)
onNextClick(balanceState = activeStake)
onNextClick(activeStake)
} else {
stateController.update(
ShowActionSelectorBottomSheetTransformer(
pendingActions = withStubUnstakeAction(networkId, activeStake),
onActionSelect = { action ->
prepareForConfirmation(
balanceType = activeStake.type,
pendingAction = action,
balanceState = activeStake,
validator = activeStake.validator,
amountValue = activeStake.cryptoValue,
)
stateController.update(DismissBottomSheetStateTransformer)
onNextClick(balanceState = activeStake)
onNextClick(activeStake)
},
onDismiss = { stateController.update(DismissBottomSheetStateTransformer) },
),
@ -889,12 +913,15 @@ internal class StakingViewModel @Inject constructor(
userWalletProvider = Provider { userWallet },
appCurrencyProvider = Provider { appCurrency },
balancesToShowProvider = Provider { balancesToShow },
maxEnterAmountProvider = Provider { maxEnterAmount },
),
SetConfirmationStateEmptyTransformer,
)
}
private fun prepareForConfirmation(
balanceType: BalanceType,
balanceState: BalanceState,
pendingActions: ImmutableList<PendingAction> = persistentListOf(),
pendingAction: PendingAction? = pendingActions.firstOrNull(),
validator: Yield.Validator?,
@ -903,6 +930,8 @@ internal class StakingViewModel @Inject constructor(
stateController.updateAll(
SetConfirmationStateInitTransformer(
isEnter = false,
isExplicitExit = balanceType == BalanceType.STAKED,
balanceState = balanceState,
cryptoCurrencyStatus = cryptoCurrencyStatus,
stakingApproval = stakingApproval,
pendingActions = pendingActions,
@ -913,6 +942,13 @@ internal class StakingViewModel @Inject constructor(
selectedValidator = validator,
yield = yield,
),
SetAmountDataTransformer(
clickIntents = this,
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
userWalletProvider = Provider { userWallet },
appCurrencyProvider = Provider { appCurrency },
maxEnterAmountProvider = Provider { maxEnterAmount },
),
AmountChangeStateTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
value = amountValue,