Updated on 2026-08-14
This commit is contained in:
parent
c6fc619530
commit
c922665d8b
7 changed files with 203 additions and 81 deletions
|
|
@ -76,4 +76,6 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
|
|||
fun onActivateTonAccountNotificationShow()
|
||||
|
||||
fun onActivateTonAccountClick()
|
||||
|
||||
fun onAmountReduceByFeeClick(reduceAmount: BigDecimal, notification: Class<out NotificationUM>)
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.blockchain.common.TransactionData
|
|||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
|
||||
|
|
@ -1081,6 +1082,17 @@ internal class StakingModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onAmountReduceByFeeClick(reduceAmount: BigDecimal, notification: Class<out NotificationUM>) {
|
||||
stateController.update(
|
||||
AmountReduceByStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
value = ReduceByData(reduceAmount, reduceAmount),
|
||||
minimumTransactionAmount = null,
|
||||
),
|
||||
)
|
||||
onNotificationCancel(notification)
|
||||
}
|
||||
|
||||
private suspend fun setupApprovalNeeded() {
|
||||
val approval = StakingIntegrationID.create(currencyId = cryptoCurrencyStatus.currency.id)?.approval
|
||||
?: StakingApproval.Empty
|
||||
|
|
|
|||
|
|
@ -89,9 +89,20 @@ internal object StakingNotification {
|
|||
subtitle = subtitleText,
|
||||
)
|
||||
|
||||
data object StakeEntireBalance : Info(
|
||||
data class StakeEntireBalance(
|
||||
private val reduceAmountValue: TextReference?,
|
||||
private val onReduceClick: () -> Unit,
|
||||
) : Info(
|
||||
title = resourceReference(R.string.common_network_fee_title),
|
||||
subtitle = resourceReference(R.string.staking_notification_stake_entire_balance_text),
|
||||
buttonsState = if (reduceAmountValue != null) {
|
||||
NotificationConfig.ButtonsState.PrimaryButtonConfig(
|
||||
text = reduceAmountValue,
|
||||
onClick = onReduceClick,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
|
||||
data class Unstake(
|
||||
|
|
|
|||
|
|
@ -84,4 +84,6 @@ internal object StakingClickIntentsStub : StakingClickIntents {
|
|||
override fun onActivateTonAccountNotificationShow() {}
|
||||
|
||||
override fun onActivateTonAccountClick() {}
|
||||
|
||||
override fun onAmountReduceByFeeClick(reduceAmount: BigDecimal, notification: Class<out NotificationUM>) {}
|
||||
}
|
||||
|
|
@ -33,6 +33,7 @@ import com.tangem.lib.crypto.BlockchainUtils.isTon
|
|||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -56,29 +57,44 @@ internal class AddStakingNotificationsTransformer(
|
|||
isSubtractAvailable = isSubtractAvailable,
|
||||
)
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val balance = cryptoCurrencyStatus.value.amount.orZero()
|
||||
|
||||
val confirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState
|
||||
val amountState = prevState.amountState as? AmountState.Data ?: return prevState
|
||||
val feeState = confirmationState.feeState as? FeeState.Content
|
||||
|
||||
val amountValue = amountState.amountTextField.cryptoAmount.value.orZero()
|
||||
val feeValue = feeState?.fee?.amount?.value.orZero()
|
||||
val reduceAmountBy = confirmationState.reduceAmountBy.orZero()
|
||||
|
||||
val isEnterAction = prevState.actionType is StakingActionCommonType.Enter
|
||||
val isFeeCoverage = checkFeeCoverage(
|
||||
amountValue = amountValue,
|
||||
feeValue = feeValue,
|
||||
balance = balance,
|
||||
isSubtractAvailable = isSubtractAvailable,
|
||||
reduceAmountBy = reduceAmountBy,
|
||||
val sendingAmount = calculateSendingAmount(
|
||||
prevState = prevState,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
amountState = amountState,
|
||||
confirmationState = confirmationState,
|
||||
)
|
||||
val minimumRequirement = integration.enterMinimumAmount.orZero()
|
||||
val sendingAmount = if (isEnterAction) {
|
||||
val notifications = buildNotifications(
|
||||
prevState = prevState,
|
||||
amountState = amountState,
|
||||
confirmationState = confirmationState,
|
||||
sendingAmount = sendingAmount,
|
||||
)
|
||||
val isActualSources = areSourcesActual(cryptoCurrencyStatus)
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = confirmationState.copy(
|
||||
notifications = notifications,
|
||||
isPrimaryButtonEnabled = isPrimaryButtonEnabled(notifications, isActualSources),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun calculateSendingAmount(
|
||||
prevState: StakingUiState,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
amountState: AmountState.Data,
|
||||
confirmationState: StakingStates.ConfirmationState.Data,
|
||||
): BigDecimal {
|
||||
val isEnterAction = prevState.actionType is StakingActionCommonType.Enter
|
||||
return if (isEnterAction) {
|
||||
val amountValue = amountState.amountTextField.cryptoAmount.value.orZero()
|
||||
val feeValue = (confirmationState.feeState as? FeeState.Content)?.fee?.amount?.value.orZero()
|
||||
val reduceAmountBy = confirmationState.reduceAmountBy.orZero()
|
||||
checkAndCalculateSubtractedAmount(
|
||||
isAmountSubtractAvailable = isSubtractAvailable,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
|
|
@ -87,63 +103,89 @@ internal class AddStakingNotificationsTransformer(
|
|||
reduceAmountBy = reduceAmountBy,
|
||||
)
|
||||
} else {
|
||||
// No amount is taken from account balance on exit or pending actions
|
||||
BigDecimal.ZERO
|
||||
}
|
||||
|
||||
val notifications = if (isAccountInitializedProvider.invoke()) {
|
||||
buildList {
|
||||
// errors
|
||||
addErrorNotifications(
|
||||
prevState = prevState,
|
||||
feeError = feeError,
|
||||
sendingAmount = sendingAmount,
|
||||
onReload = prevState.clickIntents::getFee,
|
||||
feeValue = feeValue,
|
||||
)
|
||||
addStakingErrorNotifications(stakingError = stakingError, onReload = prevState.clickIntents::getFee)
|
||||
// warnings
|
||||
addWarningNotifications(
|
||||
prevState = prevState,
|
||||
amountState = amountState,
|
||||
feeState = feeState,
|
||||
sendingAmount = sendingAmount,
|
||||
isFeeCoverage = isFeeCoverage && isEnterAction && !sendingAmount.equals(minimumRequirement),
|
||||
)
|
||||
|
||||
stakingInfoNotificationsFactory.addInfoNotifications(
|
||||
notifications = this,
|
||||
prevState = prevState,
|
||||
sendingAmount = sendingAmount,
|
||||
actionAmount = amountValue,
|
||||
feeValue = feeValue,
|
||||
tonBalanceExtraFeeThreshold = TON_BALANCE_EXTRA_FEE_THRESHOLD,
|
||||
)
|
||||
}.toImmutableList()
|
||||
} else {
|
||||
buildList {
|
||||
addTonInitializeAccountNotification(prevState)
|
||||
}.toImmutableList()
|
||||
}
|
||||
|
||||
val isActualSources = with(cryptoCurrencyStatus.value) {
|
||||
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
}
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = confirmationState.copy(
|
||||
notifications = notifications.toImmutableList(),
|
||||
isPrimaryButtonEnabled = notifications.none {
|
||||
it is StakingNotification.Error ||
|
||||
it is NotificationUM.Error ||
|
||||
it is NotificationUM.Warning.NetworkFeeUnreachable ||
|
||||
it is StakingNotification.Warning.TransactionInProgress ||
|
||||
it is StakingNotification.Warning.InitializeTonAccount
|
||||
} && isActualSources,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildNotifications(
|
||||
prevState: StakingUiState,
|
||||
amountState: AmountState.Data,
|
||||
confirmationState: StakingStates.ConfirmationState.Data,
|
||||
sendingAmount: BigDecimal,
|
||||
) = if (isAccountInitializedProvider.invoke()) {
|
||||
buildInitializedAccountNotifications(
|
||||
prevState = prevState,
|
||||
amountState = amountState,
|
||||
confirmationState = confirmationState,
|
||||
sendingAmount = sendingAmount,
|
||||
)
|
||||
} else {
|
||||
buildList { addTonInitializeAccountNotification(prevState) }.toImmutableList()
|
||||
}
|
||||
|
||||
private fun buildInitializedAccountNotifications(
|
||||
prevState: StakingUiState,
|
||||
amountState: AmountState.Data,
|
||||
confirmationState: StakingStates.ConfirmationState.Data,
|
||||
sendingAmount: BigDecimal,
|
||||
) = buildList {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val balance = cryptoCurrencyStatus.value.amount.orZero()
|
||||
val feeState = confirmationState.feeState as? FeeState.Content
|
||||
val amountValue = amountState.amountTextField.cryptoAmount.value.orZero()
|
||||
val feeValue = feeState?.fee?.amount?.value.orZero()
|
||||
val reduceAmountBy = confirmationState.reduceAmountBy.orZero()
|
||||
val isEnterAction = prevState.actionType is StakingActionCommonType.Enter
|
||||
val minimumRequirement = integration.enterMinimumAmount.orZero()
|
||||
|
||||
val isFeeCoverage = checkFeeCoverage(
|
||||
amountValue = amountValue,
|
||||
feeValue = feeValue,
|
||||
balance = balance,
|
||||
isSubtractAvailable = isSubtractAvailable,
|
||||
reduceAmountBy = reduceAmountBy,
|
||||
)
|
||||
|
||||
addErrorNotifications(
|
||||
prevState = prevState,
|
||||
feeError = feeError,
|
||||
sendingAmount = sendingAmount,
|
||||
onReload = prevState.clickIntents::getFee,
|
||||
feeValue = feeValue,
|
||||
)
|
||||
addStakingErrorNotifications(stakingError = stakingError, onReload = prevState.clickIntents::getFee)
|
||||
addWarningNotifications(
|
||||
prevState = prevState,
|
||||
amountState = amountState,
|
||||
feeState = feeState,
|
||||
sendingAmount = sendingAmount,
|
||||
isFeeCoverage = isFeeCoverage && isEnterAction && !sendingAmount.equals(minimumRequirement),
|
||||
)
|
||||
stakingInfoNotificationsFactory.addInfoNotifications(
|
||||
notifications = this,
|
||||
prevState = prevState,
|
||||
sendingAmount = sendingAmount,
|
||||
actionAmount = amountValue,
|
||||
feeAmount = feeState?.fee?.amount,
|
||||
isFeeApproximate = feeState?.isFeeApproximate == true,
|
||||
onAmountReduceByFeeClick = prevState.clickIntents::onAmountReduceByFeeClick,
|
||||
tonBalanceExtraFeeThreshold = TON_BALANCE_EXTRA_FEE_THRESHOLD,
|
||||
)
|
||||
}.toImmutableList()
|
||||
|
||||
private fun areSourcesActual(cryptoCurrencyStatus: CryptoCurrencyStatus) = with(cryptoCurrencyStatus.value) {
|
||||
sources.stakingBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
}
|
||||
|
||||
private fun isPrimaryButtonEnabled(notifications: ImmutableList<NotificationUM>, isActualSources: Boolean) =
|
||||
notifications.none {
|
||||
it is StakingNotification.Error ||
|
||||
it is NotificationUM.Error ||
|
||||
it is NotificationUM.Warning.NetworkFeeUnreachable ||
|
||||
it is StakingNotification.Warning.TransactionInProgress ||
|
||||
it is StakingNotification.Warning.InitializeTonAccount
|
||||
} && isActualSources
|
||||
|
||||
private fun MutableList<NotificationUM>.addStakingErrorNotifications(
|
||||
stakingError: StakingError?,
|
||||
onReload: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ internal class DismissStakingNotificationsStateTransformer(
|
|||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val confirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val updatedNotifications = confirmationState?.notifications
|
||||
?.filterNot { it::class == notification }?.toPersistentList()
|
||||
?.filterNot { it::class.java == notification }?.toPersistentList()
|
||||
?: persistentListOf()
|
||||
|
||||
return prevState.copy(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers.notifications
|
||||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.ui.extensions.pluralReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fee
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.BalanceType
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
|
|
@ -35,7 +39,10 @@ internal class StakingInfoNotificationsFactory(
|
|||
* @param prevState current screen state to update
|
||||
* @param sendingAmount amount being transferred from user account
|
||||
* @param actionAmount any amount being transferred or used action
|
||||
* @param feeValue fee amount payed from user account
|
||||
* @param feeAmount fee amount payed from user account
|
||||
* @param tonBalanceExtraFeeThreshold threshold for TON extra fee notification
|
||||
* @param isFeeApproximate whether fee is approximate
|
||||
* @param onAmountReduceByFeeClick callback to reduce amount by fee
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
fun addInfoNotifications(
|
||||
|
|
@ -43,17 +50,24 @@ internal class StakingInfoNotificationsFactory(
|
|||
prevState: StakingUiState,
|
||||
sendingAmount: BigDecimal,
|
||||
actionAmount: BigDecimal,
|
||||
feeValue: BigDecimal,
|
||||
feeAmount: Amount?,
|
||||
tonBalanceExtraFeeThreshold: BigDecimal,
|
||||
isFeeApproximate: Boolean,
|
||||
onAmountReduceByFeeClick: (BigDecimal, notification: Class<out NotificationUM>) -> Unit,
|
||||
) = with(notifications) {
|
||||
addStakingLowBalanceNotification(prevState, actionAmount)
|
||||
addTonExtraFeeInfoNotification(tonBalanceExtraFeeThreshold)
|
||||
|
||||
when (prevState.actionType) {
|
||||
is StakingActionCommonType.Enter -> addEnterInfoNotifications(sendingAmount, feeValue)
|
||||
is StakingActionCommonType.Enter -> addEnterInfoNotifications(
|
||||
sendingAmount = sendingAmount,
|
||||
feeAmount = feeAmount,
|
||||
isFeeApproximate = isFeeApproximate,
|
||||
onAmountReduceByFeeClick = onAmountReduceByFeeClick,
|
||||
)
|
||||
is StakingActionCommonType.Exit -> addExitInfoNotifications(prevState)
|
||||
is StakingActionCommonType.Pending -> {
|
||||
addCardanoRestakeMinimumAmountNotification(feeValue)
|
||||
addCardanoRestakeMinimumAmountNotification(feeAmount?.value.orZero())
|
||||
addPendingInfoNotifications(prevState)
|
||||
addTonHaveToUnstakeAllNotification(prevState)
|
||||
}
|
||||
|
|
@ -67,12 +81,19 @@ internal class StakingInfoNotificationsFactory(
|
|||
|
||||
private fun MutableList<NotificationUM>.addEnterInfoNotifications(
|
||||
sendingAmount: BigDecimal,
|
||||
feeValue: BigDecimal,
|
||||
feeAmount: Amount?,
|
||||
isFeeApproximate: Boolean,
|
||||
onAmountReduceByFeeClick: (BigDecimal, notification: Class<out NotificationUM>) -> Unit,
|
||||
) {
|
||||
addCardanoStakeMinimumAmountNotification(feeValue)
|
||||
addCardanoStakeMinimumAmountNotification(feeAmount?.value.orZero())
|
||||
addTronRevoteNotification()
|
||||
addCardanoStakeNotification()
|
||||
addStakingEntireBalanceNotification(sendingAmount, feeValue)
|
||||
addStakingEntireBalanceNotification(
|
||||
sendingAmount = sendingAmount,
|
||||
feeAmount = feeAmount,
|
||||
isFeeApproximate = isFeeApproximate,
|
||||
onAmountReduceByFeeClick = onAmountReduceByFeeClick,
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addPendingInfoNotifications(prevState: StakingUiState) {
|
||||
|
|
@ -194,15 +215,46 @@ internal class StakingInfoNotificationsFactory(
|
|||
|
||||
private fun MutableList<NotificationUM>.addStakingEntireBalanceNotification(
|
||||
sendingAmount: BigDecimal,
|
||||
feeValue: BigDecimal,
|
||||
feeAmount: Amount?,
|
||||
isFeeApproximate: Boolean,
|
||||
onAmountReduceByFeeClick: (BigDecimal, notification: Class<out NotificationUM>) -> Unit,
|
||||
) {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val balance = cryptoCurrencyStatus.value.amount.orZero()
|
||||
val feeValue = feeAmount?.value.orZero()
|
||||
|
||||
val isEntireBalance = sendingAmount.plus(feeValue) == balance
|
||||
|
||||
if (isEntireBalance && isSubtractAvailable && !isCardano(cryptoCurrencyStatus.currency.network.rawId)) {
|
||||
add(StakingNotification.Info.StakeEntireBalance)
|
||||
val value = feeValue.multiply(FEE_DECIMALS_MULTIPLIER)
|
||||
|
||||
val reduceAmountValue = feeAmount
|
||||
?.takeIf { feeValue != BigDecimal.ZERO }
|
||||
?.let { amount ->
|
||||
resourceReference(
|
||||
R.string.send_notification_reduce_by,
|
||||
wrappedList(
|
||||
value.format {
|
||||
crypto(
|
||||
symbol = amount.currencySymbol,
|
||||
decimals = amount.decimals,
|
||||
).fee(canBeLower = isFeeApproximate)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
add(
|
||||
StakingNotification.Info.StakeEntireBalance(
|
||||
reduceAmountValue,
|
||||
{
|
||||
onAmountReduceByFeeClick.invoke(
|
||||
value,
|
||||
StakingNotification.Info.StakeEntireBalance::class.java,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -286,5 +338,6 @@ internal class StakingInfoNotificationsFactory(
|
|||
private companion object {
|
||||
val MINIMUM_STAKE_BALANCE = "5".toBigDecimal()
|
||||
val MINIMUM_RESTAKE_BALANCE = "3".toBigDecimal()
|
||||
val FEE_DECIMALS_MULTIPLIER = "3".toBigDecimal()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue