Updated on 2026-08-14
This commit is contained in:
commit
1706a25d1a
61 changed files with 717 additions and 347 deletions
|
|
@ -36,6 +36,8 @@ internal class SendRecipientMemoFieldConverter(
|
|||
Blockchain.Stellar.id,
|
||||
Blockchain.Hedera.id,
|
||||
Blockchain.Algorand.id,
|
||||
Blockchain.Sei.id,
|
||||
Blockchain.InternetComputer.id,
|
||||
-> {
|
||||
convert(
|
||||
value = Data(memo = memo, label = R.string.send_extras_hint_memo),
|
||||
|
|
|
|||
|
|
@ -85,17 +85,12 @@ internal object StakingNotification {
|
|||
),
|
||||
)
|
||||
|
||||
data class PendingAction(
|
||||
data class Ordinary(
|
||||
val title: TextReference,
|
||||
val text: TextReference,
|
||||
) : StakingNotification.Info(
|
||||
title = title,
|
||||
subtitle = text,
|
||||
)
|
||||
|
||||
data object TronRevote : StakingNotification.Info(
|
||||
title = resourceReference(R.string.staking_revote),
|
||||
subtitle = resourceReference(R.string.staking_notifications_revote_tron_text),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ import com.tangem.core.ui.components.list.RoundedListWithDividersItemData
|
|||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType
|
||||
|
|
@ -98,7 +99,7 @@ internal sealed class StakingStates {
|
|||
val transactionDoneState: TransactionDoneState,
|
||||
val isApprovalNeeded: Boolean,
|
||||
val reduceAmountBy: BigDecimal?,
|
||||
val balanceState: BalanceState?,
|
||||
val possiblePendingTransaction: PendingTransaction?,
|
||||
) : ConfirmationState()
|
||||
|
||||
data class Empty(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.domain.staking.model.stakekit.BalanceItem
|
|||
import com.tangem.domain.staking.model.stakekit.BalanceType
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceType.Companion.isClickable
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
||||
|
|
@ -83,7 +84,11 @@ internal class BalanceItemConverter(
|
|||
private fun getSubtitle(balance: BalanceItem) = when (balance.type) {
|
||||
BalanceType.UNSTAKING -> getUnbondingDate(balance.date)
|
||||
BalanceType.UNSTAKED -> resourceReference(R.string.staking_tap_to_withdraw)
|
||||
BalanceType.LOCKED -> resourceReference(R.string.staking_tap_to_unlock)
|
||||
BalanceType.LOCKED -> if (balance.pendingActions.any { it.type == StakingActionType.VOTE_LOCKED }) {
|
||||
resourceReference(R.string.staking_tap_to_unlock_or_vote)
|
||||
} else {
|
||||
resourceReference(R.string.staking_tap_to_unlock)
|
||||
}
|
||||
BalanceType.PREPARING -> {
|
||||
val warmupPeriod = yield.metadata.warmupPeriod.days
|
||||
combinedReference(
|
||||
|
|
@ -100,8 +105,8 @@ internal class BalanceItemConverter(
|
|||
-> null
|
||||
}
|
||||
|
||||
private fun getUnbondingDate(date: DateTime?): TextReference {
|
||||
val unbondingPeriod = yield.metadata.cooldownPeriod.days
|
||||
private fun getUnbondingDate(date: DateTime?): TextReference? {
|
||||
val unbondingPeriod = yield.metadata.cooldownPeriod?.days ?: return null
|
||||
if (date == null) {
|
||||
return combinedReference(
|
||||
resourceReference(R.string.staking_details_unbonding_period),
|
||||
|
|
|
|||
|
|
@ -28,12 +28,17 @@ internal class YieldBalancesConverter(
|
|||
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance
|
||||
val balanceToShowItems = balancesToShowProvider()
|
||||
|
||||
return if (yieldBalance is YieldBalance.Data) {
|
||||
val cryptoRewardsValue = yieldBalance.getRewardStakingBalance()
|
||||
val fiatRewardsValue = cryptoCurrencyStatus.value.fiatRate?.times(cryptoRewardsValue)
|
||||
return if (yieldBalance is YieldBalance.Data || balanceToShowItems.any { it.isPending }) {
|
||||
val cryptoRewardsValue = (yieldBalance as? YieldBalance.Data)?.getRewardStakingBalance()
|
||||
|
||||
val balanceToShowItems = balancesToShowProvider()
|
||||
val fiatRate = cryptoCurrencyStatus.value.fiatRate
|
||||
val fiatRewardsValue = if (fiatRate != null && cryptoRewardsValue != null) {
|
||||
fiatRate.times(cryptoRewardsValue)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
InnerYieldBalanceState.Data(
|
||||
rewardsCrypto = BigDecimalFormatter.formatCryptoAmount(
|
||||
|
|
|
|||
|
|
@ -8,10 +8,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.domain.staking.*
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.SubmitHashData
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceType
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.*
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
|
||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||
|
|
@ -35,6 +32,7 @@ import kotlinx.coroutines.awaitAll
|
|||
import kotlinx.coroutines.coroutineScope
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.util.UUID
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class StakingTransactionSender @AssistedInject constructor(
|
||||
|
|
@ -91,12 +89,16 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
|
||||
sendStakingTransaction(
|
||||
fullTransactionsData = fullTransactionsData,
|
||||
balanceState = confirmationState.balanceState,
|
||||
possiblePendingTransaction = composePendingTransaction(confirmationState),
|
||||
onSendSuccess = onSendSuccess,
|
||||
onSendError = onSendError,
|
||||
)
|
||||
}
|
||||
|
||||
private fun composePendingTransaction(confirmationState: StakingStates.ConfirmationState.Data): PendingTransaction {
|
||||
return confirmationState.possiblePendingTransaction ?: composeStakeTransaction(confirmationState)
|
||||
}
|
||||
|
||||
private suspend fun getStakingTransactions(
|
||||
state: StakingUiState,
|
||||
confirmationState: StakingStates.ConfirmationState.Data,
|
||||
|
|
@ -204,7 +206,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
|
||||
private suspend fun sendStakingTransaction(
|
||||
fullTransactionsData: List<FullTransactionData>,
|
||||
balanceState: BalanceState?,
|
||||
possiblePendingTransaction: PendingTransaction,
|
||||
onSendSuccess: (txUrl: String) -> Unit,
|
||||
onSendError: (SendTransactionError?) -> Unit,
|
||||
) {
|
||||
|
|
@ -218,13 +220,9 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
},
|
||||
ifRight = { transactionHashes ->
|
||||
submitHash(
|
||||
transactionIds = fullTransactionsData.map { it.stakeKitTransaction.id },
|
||||
transactions = fullTransactionsData.map { it.stakeKitTransaction },
|
||||
transactionHashes = transactionHashes,
|
||||
groupId = balanceState?.groupId,
|
||||
validator = balanceState?.validator,
|
||||
amount = balanceState?.cryptoDecimal,
|
||||
balanceType = balanceState?.type,
|
||||
rawCurrencyId = balanceState?.rawCurrencyId,
|
||||
pendingTransaction = possiblePendingTransaction,
|
||||
)
|
||||
val txUrl = getExplorerTransactionUrlUseCase(
|
||||
txHash = transactionHashes.last(),
|
||||
|
|
@ -238,25 +236,18 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private suspend fun submitHash(
|
||||
transactionIds: List<String>,
|
||||
transactions: List<StakingTransaction>,
|
||||
transactionHashes: List<String>,
|
||||
groupId: String?,
|
||||
validator: Yield.Validator?,
|
||||
amount: BigDecimal?,
|
||||
balanceType: BalanceType?,
|
||||
rawCurrencyId: String?,
|
||||
pendingTransaction: PendingTransaction,
|
||||
) {
|
||||
transactionIds
|
||||
transactions
|
||||
.zip(transactionHashes)
|
||||
.forEach { (transactionId, transactionHash) ->
|
||||
.forEach { (transaction, transactionHash) ->
|
||||
submitHashUseCase(
|
||||
SubmitHashData(
|
||||
transactionId = transactionId,
|
||||
transactionId = transaction.id,
|
||||
transactionHash = transactionHash,
|
||||
validator = validator,
|
||||
amount = amount,
|
||||
balanceType = balanceType,
|
||||
rawCurrencyId = rawCurrencyId,
|
||||
pendingTransaction = pendingTransaction,
|
||||
),
|
||||
)
|
||||
.onLeft { error ->
|
||||
|
|
@ -267,20 +258,14 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
),
|
||||
)
|
||||
saveUnsubmittedHashUseCase.invoke(
|
||||
transactionId = transactionId,
|
||||
transactionId = transaction.id,
|
||||
transactionHash = transactionHash,
|
||||
)
|
||||
}.onRight {
|
||||
Timber.d("Successful hash submission")
|
||||
savePendingTransactionUseCase.invoke(
|
||||
PendingTransaction(
|
||||
groupId = groupId,
|
||||
type = balanceType,
|
||||
amount = amount,
|
||||
rawCurrencyId = rawCurrencyId,
|
||||
validator = validator,
|
||||
),
|
||||
)
|
||||
if (transaction.type != StakingTransactionType.FREEZE_ENERGY) {
|
||||
savePendingTransactionUseCase.invoke(userWallet.walletId, pendingTransaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -299,6 +284,19 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun composeStakeTransaction(confirmationState: StakingStates.ConfirmationState.Data): PendingTransaction {
|
||||
val state = stateController.value
|
||||
|
||||
return PendingTransaction(
|
||||
groupId = UUID.randomUUID().toString(),
|
||||
type = BalanceType.STAKED,
|
||||
amount = (state.amountState as? AmountState.Data)?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO,
|
||||
rawCurrencyId = cryptoCurrencyStatus.currency.id.rawCurrencyId,
|
||||
validator = (confirmationState.validatorState as? ValidatorState.Content)?.chosenValidator,
|
||||
balancesId = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.getBalancesUniqueId() ?: 0,
|
||||
)
|
||||
}
|
||||
|
||||
private data class FullTransactionData(
|
||||
val stakeKitTransaction: StakingTransaction,
|
||||
val tangemTransaction: TransactionData.Compiled,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ internal object ConfirmationStatePreviewData {
|
|||
website = "https://luganodes.com/",
|
||||
votingPower = 0.09778360195377911,
|
||||
preferred = true,
|
||||
isStrategicPartner = false,
|
||||
),
|
||||
Yield.Validator(
|
||||
address = "0x35b1ca0f398905cf752e6fe122b51c88022fca32",
|
||||
|
|
@ -40,6 +41,7 @@ internal object ConfirmationStatePreviewData {
|
|||
website = "https://infstones.com/",
|
||||
votingPower = 0.0034366257754399774,
|
||||
preferred = true,
|
||||
isStrategicPartner = false,
|
||||
),
|
||||
Yield.Validator(
|
||||
address = "0xd14a87025109013b0a2354a775cb335f926af65a",
|
||||
|
|
@ -52,6 +54,7 @@ internal object ConfirmationStatePreviewData {
|
|||
website = "https://infstones.com/",
|
||||
votingPower = 0.023487238579718264,
|
||||
preferred = true,
|
||||
isStrategicPartner = true,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -93,6 +96,6 @@ internal object ConfirmationStatePreviewData {
|
|||
isApprovalNeeded = false,
|
||||
reduceAmountBy = null,
|
||||
pendingActions = null,
|
||||
balanceState = null,
|
||||
possiblePendingTransaction = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -86,6 +86,7 @@ internal object InitialStakingStatePreview {
|
|||
stakedBalance = null,
|
||||
votingPower = null,
|
||||
preferred = false,
|
||||
isStrategicPartner = false,
|
||||
),
|
||||
pendingActions = persistentListOf(),
|
||||
isClickable = true,
|
||||
|
|
|
|||
|
|
@ -255,30 +255,23 @@ internal class AddStakingNotificationsTransformer(
|
|||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addExitInfoNotifications() {
|
||||
add(
|
||||
StakingNotification.Info.Unstake(
|
||||
cooldownPeriodDays = yield.metadata.cooldownPeriod.days,
|
||||
subtitleRes = if (isCosmos(cryptoCurrencyStatusProvider().currency.network.id.value)) {
|
||||
R.string.staking_notification_unstake_cosmos_text
|
||||
} else {
|
||||
R.string.staking_notification_unstake_text
|
||||
},
|
||||
),
|
||||
)
|
||||
val cooldownPeriodDays = yield.metadata.cooldownPeriod?.days
|
||||
if (cooldownPeriodDays != null) {
|
||||
add(
|
||||
StakingNotification.Info.Unstake(
|
||||
cooldownPeriodDays = cooldownPeriodDays,
|
||||
subtitleRes = if (isCosmos(cryptoCurrencyStatusProvider().currency.network.id.value)) {
|
||||
R.string.staking_notification_unstake_cosmos_text
|
||||
} else {
|
||||
R.string.staking_notification_unstake_text
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addEnterInfoNotifications() {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val isTron = isTron(cryptoCurrencyStatus.currency.network.id.value)
|
||||
val hasStakedBalance = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance
|
||||
?.items?.any {
|
||||
it.type == BalanceType.PREPARING ||
|
||||
it.type == BalanceType.STAKED ||
|
||||
it.type == BalanceType.LOCKED
|
||||
} == true
|
||||
if (hasStakedBalance && isTron) {
|
||||
add(StakingNotification.Info.TronRevote)
|
||||
}
|
||||
addTronRevoteNotification()
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addPendingInfoNotifications(prevState: StakingUiState) {
|
||||
|
|
@ -298,28 +291,55 @@ internal class AddStakingNotificationsTransformer(
|
|||
resourceReference(R.string.staking_notification_withdraw_text)
|
||||
}
|
||||
StakingActionType.UNLOCK_LOCKED -> {
|
||||
val cooldownPeriodDays = yield.metadata.cooldownPeriod.days
|
||||
resourceReference(R.string.staking_unlocked_locked) to resourceReference(
|
||||
R.string.staking_notification_unlock_text,
|
||||
wrappedList(
|
||||
pluralReference(
|
||||
id = R.plurals.common_days,
|
||||
count = cooldownPeriodDays,
|
||||
formatArgs = wrappedList(cooldownPeriodDays),
|
||||
val cooldownPeriodDays = yield.metadata.cooldownPeriod?.days
|
||||
if (cooldownPeriodDays != null) {
|
||||
resourceReference(R.string.staking_unlocked_locked) to resourceReference(
|
||||
R.string.staking_notification_unlock_text,
|
||||
wrappedList(
|
||||
pluralReference(
|
||||
id = R.plurals.common_days,
|
||||
count = cooldownPeriodDays,
|
||||
formatArgs = wrappedList(cooldownPeriodDays),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
} else {
|
||||
null to null
|
||||
}
|
||||
}
|
||||
StakingActionType.VOTE_LOCKED -> {
|
||||
resourceReference(R.string.staking_revote) to
|
||||
resourceReference(R.string.staking_notifications_revote_tron_text)
|
||||
}
|
||||
else -> null to null
|
||||
}
|
||||
|
||||
if (titleReference != null && textReference != null) {
|
||||
add(
|
||||
StakingNotification.Info.PendingAction(
|
||||
StakingNotification.Info.Ordinary(
|
||||
title = titleReference,
|
||||
text = textReference,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addTronRevoteNotification() {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val isTron = isTron(cryptoCurrencyStatus.currency.network.id.value)
|
||||
val hasStakedBalance = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.balance
|
||||
?.items?.any {
|
||||
it.type == BalanceType.PREPARING ||
|
||||
it.type == BalanceType.STAKED ||
|
||||
it.type == BalanceType.LOCKED
|
||||
} == true
|
||||
if (isTron && hasStakedBalance) {
|
||||
add(
|
||||
StakingNotification.Info.Ordinary(
|
||||
title = resourceReference(R.string.staking_revote),
|
||||
text = resourceReference(R.string.staking_notifications_revote_tron_text),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SetBalanceStateTransformer(
|
||||
private val balanceState: BalanceState? = null,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val possibleConfirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
?: return prevState
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = possibleConfirmationState.copy(balanceState = balanceState),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -144,9 +144,19 @@ internal class SetButtonsStateTransformer(
|
|||
clickIntents.onAmountValueChange("") // reset amount state
|
||||
clickIntents.onNextClick(StakingActionCommonType.ENTER)
|
||||
}
|
||||
StakingStep.Validators,
|
||||
StakingStep.Amount,
|
||||
-> clickIntents.onNextClick()
|
||||
StakingStep.Validators -> {
|
||||
if (confirmationState is StakingStates.ConfirmationState.Data) {
|
||||
clickIntents.onNextClick(
|
||||
pendingActions = confirmationState.pendingActions,
|
||||
pendingAction = confirmationState.pendingAction,
|
||||
)
|
||||
} else {
|
||||
clickIntents.onNextClick()
|
||||
}
|
||||
}
|
||||
StakingStep.Amount -> {
|
||||
clickIntents.onNextClick()
|
||||
}
|
||||
StakingStep.Confirmation -> onConfirmationClick()
|
||||
StakingStep.RewardsValidators -> Unit
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,12 @@ internal class SetConfirmationStateLoadingTransformer(
|
|||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val filteredValidators = yield.validators.filter {
|
||||
it.preferred
|
||||
}
|
||||
val possibleConfirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val possibleValidatorState = possibleConfirmationState?.validatorState as? ValidatorState.Content
|
||||
val chosenValidator = possibleValidatorState?.chosenValidator ?: yield.validators[0]
|
||||
val chosenValidator = possibleValidatorState?.chosenValidator ?: filteredValidators[0]
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = StakingStates.ConfirmationState.Data(
|
||||
|
|
@ -32,7 +35,7 @@ internal class SetConfirmationStateLoadingTransformer(
|
|||
validatorState = ValidatorState.Content(
|
||||
isClickable = true,
|
||||
chosenValidator = chosenValidator,
|
||||
availableValidators = yield.validators,
|
||||
availableValidators = filteredValidators,
|
||||
),
|
||||
notifications = persistentListOf(),
|
||||
footerText = getFooter(prevState, chosenValidator),
|
||||
|
|
@ -41,7 +44,7 @@ internal class SetConfirmationStateLoadingTransformer(
|
|||
pendingActions = possibleConfirmationState?.pendingActions,
|
||||
isApprovalNeeded = false,
|
||||
reduceAmountBy = null,
|
||||
balanceState = null,
|
||||
possiblePendingTransaction = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers
|
|||
import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.features.staking.impl.presentation.state.ValidatorState
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal object SetConfirmationStateResetAssentTransformer : Transformer<StakingUiState> {
|
||||
|
|
@ -13,7 +14,7 @@ internal object SetConfirmationStateResetAssentTransformer : Transformer<Staking
|
|||
confirmationState.copy(
|
||||
isPrimaryButtonEnabled = true,
|
||||
innerState = InnerConfirmationStakingState.ASSENT,
|
||||
validatorState = confirmationState.validatorState.copySealed(isClickable = true),
|
||||
validatorState = ValidatorState.Loading,
|
||||
)
|
||||
} else {
|
||||
confirmationState
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ internal class SetInitialDataStateTransformer(
|
|||
return listOfNotNull(
|
||||
createAnnualPercentageRateItem(yield.validators),
|
||||
createAvailableItem(cryptoCurrencyStatus),
|
||||
createUnbondingPeriodItem(yield.metadata.cooldownPeriod.days),
|
||||
createUnbondingPeriodItem(yield.metadata.cooldownPeriod?.days),
|
||||
createMinimumRequirementItem(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
minimumCryptoAmount = yield.args.enter.args[Yield.Args.ArgType.AMOUNT]?.minimum,
|
||||
|
|
@ -140,7 +140,8 @@ internal class SetInitialDataStateTransformer(
|
|||
)
|
||||
}
|
||||
|
||||
private fun createUnbondingPeriodItem(cooldownPeriodDays: Int): RoundedListWithDividersItemData {
|
||||
private fun createUnbondingPeriodItem(cooldownPeriodDays: Int?): RoundedListWithDividersItemData? {
|
||||
cooldownPeriodDays ?: return null
|
||||
return RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_unbonding_period,
|
||||
startText = TextReference.Res(R.string.staking_details_unbonding_period),
|
||||
|
|
@ -231,7 +232,7 @@ internal class SetInitialDataStateTransformer(
|
|||
pendingActions = null,
|
||||
isApprovalNeeded = isApprovalNeeded,
|
||||
reduceAmountBy = null,
|
||||
balanceState = null,
|
||||
possiblePendingTransaction = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SetPossiblePendingTransactionTransformer(
|
||||
private val balanceState: BalanceState,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val possibleConfirmationState =
|
||||
prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState
|
||||
|
||||
val balancesId = (cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data)?.getBalancesUniqueId() ?: 0
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = possibleConfirmationState.copy(
|
||||
possiblePendingTransaction = PendingTransaction(
|
||||
groupId = balanceState.groupId,
|
||||
type = balanceState.type,
|
||||
amount = balanceState.cryptoDecimal,
|
||||
rawCurrencyId = balanceState.rawCurrencyId,
|
||||
validator = balanceState.validator,
|
||||
balancesId = balancesId,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
|
|
@ -39,6 +40,7 @@ internal fun StakingConfirmationContent(
|
|||
) {
|
||||
if (state !is StakingStates.ConfirmationState.Data) return
|
||||
val isEnterAction = type == StakingActionCommonType.ENTER
|
||||
val showValidatorBlock = isEnterAction || state.pendingAction?.type == StakingActionType.VOTE_LOCKED
|
||||
val isTransactionSent = state.innerState == InnerConfirmationStakingState.COMPLETED
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
@ -63,7 +65,7 @@ internal fun StakingConfirmationContent(
|
|||
isEditingDisabled = !isEnterAction && state.innerState != InnerConfirmationStakingState.COMPLETED,
|
||||
onClick = clickIntents::onPrevClick,
|
||||
)
|
||||
if (isEnterAction) {
|
||||
if (showValidatorBlock) {
|
||||
ValidatorBlock(validatorState = state.validatorState, onClick = clickIntents::openValidators)
|
||||
}
|
||||
StakingFeeBlock(feeState = state.feeState, isTransactionSent = isTransactionSent)
|
||||
|
|
|
|||
|
|
@ -5,16 +5,22 @@ import androidx.compose.animation.AnimatedVisibility
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.inputrow.InputRowImageSelector
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.extensions.*
|
||||
|
|
@ -78,6 +84,9 @@ internal fun StakingValidatorListContent(
|
|||
false,
|
||||
)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
subtitleExtraContent = {
|
||||
ValidatorLabel(item.isStrategicPartner)
|
||||
},
|
||||
selectorContent = { checked, _, _ ->
|
||||
AnimatedVisibility(
|
||||
modifier = Modifier,
|
||||
|
|
@ -99,6 +108,23 @@ internal fun StakingValidatorListContent(
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.ValidatorLabel(isStrategicPartner: Boolean) {
|
||||
if (isStrategicPartner) {
|
||||
Text(
|
||||
text = stringResource(R.string.staking_validators_label),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.icon.constant,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(horizontal = 6.dp)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.background(TangemTheme.colors.text.accent)
|
||||
.padding(horizontal = 8.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
|
|||
|
|
@ -133,9 +133,12 @@ internal class StakingViewModel @Inject constructor(
|
|||
|
||||
private val balancesToShow: List<BalanceItem>
|
||||
get() {
|
||||
return cryptoCurrencyStatus.value.yieldBalance?.let {
|
||||
invalidatePendingTransactionsUseCase(it).getOrElse { emptyList() }
|
||||
} ?: emptyList()
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
return invalidatePendingTransactionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
balanceItems = yieldBalance?.balance?.items ?: emptyList(),
|
||||
balancesId = yieldBalance?.getBalancesUniqueId() ?: 0,
|
||||
).getOrElse { emptyList() }
|
||||
}
|
||||
|
||||
private var isInitialInfoAnalyticSent: Boolean = false
|
||||
|
|
@ -215,13 +218,31 @@ internal class StakingViewModel @Inject constructor(
|
|||
stakingStateRouter.onNextClick()
|
||||
when {
|
||||
isInitState() -> {
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stateController.update(SetConfirmationStateLoadingTransformer(yield, appCurrency))
|
||||
stateController.update(SetBalanceStateTransformer(balanceState))
|
||||
if (balanceState != null) {
|
||||
stateController.update(SetPossiblePendingTransactionTransformer(balanceState, cryptoCurrencyStatus))
|
||||
}
|
||||
|
||||
stateController.update(
|
||||
transformer = SetInitialDataStateTransformer(
|
||||
clickIntents = this@StakingViewModel,
|
||||
yield = yield,
|
||||
isAnyTokenStaked = true,
|
||||
isApprovalNeeded = stakingApproval is StakingApproval.Needed,
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
userWalletProvider = Provider { userWallet },
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
balancesToShowProvider = Provider { balancesToShow },
|
||||
),
|
||||
)
|
||||
onRefreshSwipe(isRefreshing = false)
|
||||
}
|
||||
isAssentState() -> {
|
||||
getFee(pendingAction, pendingActions)
|
||||
stateController.update(SetBalanceStateTransformer(balanceState))
|
||||
if (balanceState != null) {
|
||||
stateController.update(SetPossiblePendingTransactionTransformer(balanceState, cryptoCurrencyStatus))
|
||||
}
|
||||
val amountState = value.amountState as? AmountState.Data
|
||||
if (amountState?.amountTextField?.isWarning == true) {
|
||||
stateController.update(
|
||||
|
|
@ -325,6 +346,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
if (value.currentStep == StakingStep.Confirmation) {
|
||||
when ((value.confirmationState as? StakingStates.ConfirmationState.Data)?.innerState) {
|
||||
InnerConfirmationStakingState.ASSENT -> {
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stakingStateRouter.onPrevClick()
|
||||
}
|
||||
null,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.tangem.core.ui.utils.BigDecimalFormatter
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.model.stakekit.RewardBlockType
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
|
|
@ -38,6 +39,7 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val stakingEntryInfoProvider: Provider<StakingEntryInfo?>,
|
||||
private val pendingBalancesProvider: Provider<List<BalanceItem>>,
|
||||
private val stakingAvailabilityProvider: Provider<StakingAvailability>,
|
||||
private val symbol: String,
|
||||
private val decimals: Int,
|
||||
|
|
@ -145,6 +147,8 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
val stakingEntryInfo = stakingEntryInfoProvider.invoke()
|
||||
val stakingAvailability = stakingAvailabilityProvider.invoke()
|
||||
val iconState = state.tokenInfoBlockState.iconState
|
||||
val pendingBalances = pendingBalancesProvider.invoke()
|
||||
val fiatRate = status.value.fiatRate
|
||||
|
||||
return when {
|
||||
stakingAvailability == StakingAvailability.TemporaryUnavailable -> {
|
||||
|
|
@ -153,14 +157,24 @@ internal class TokenDetailsLoadedBalanceConverter(
|
|||
stakingAvailability == StakingAvailability.Unavailable -> {
|
||||
null
|
||||
}
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null -> {
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null && pendingBalances.isEmpty() -> {
|
||||
getStakeAvailableState(stakingEntryInfo, iconState)
|
||||
}
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo != null && pendingBalances.isNotEmpty() -> {
|
||||
val pendingBalancesCryptoAmount = pendingBalances.sumOf { it.amount }
|
||||
|
||||
val stakingFiatAmount = fiatRate?.multiply(pendingBalancesCryptoAmount)
|
||||
getStakedState(
|
||||
status = status,
|
||||
stakingCryptoAmount = pendingBalancesCryptoAmount,
|
||||
stakingFiatAmount = stakingFiatAmount,
|
||||
stakingRewardAmount = null,
|
||||
)
|
||||
}
|
||||
stakingCryptoAmount.isNullOrZero() && stakingEntryInfo == null -> {
|
||||
null
|
||||
}
|
||||
else -> {
|
||||
val fiatRate = status.value.fiatRate
|
||||
val stakingRewardAmount = yieldBalance?.getRewardStakingBalance()?.let { fiatRate?.multiply(it) }
|
||||
val stakingFiatAmount = stakingCryptoAmount?.let { fiatRate?.multiply(it) }
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import com.tangem.domain.common.CardTypesResolver
|
|||
import com.tangem.domain.staking.GetStakingIntegrationIdUseCase
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
|
|
@ -52,6 +53,7 @@ internal class TokenDetailsStateFactory(
|
|||
private val stakingEntryInfoProvider: Provider<StakingEntryInfo?>,
|
||||
private val stakingAvailabilityProvider: Provider<StakingAvailability>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||
private val pendingBalancesProvider: Provider<List<BalanceItem>>,
|
||||
private val clickIntents: TokenDetailsClickIntents,
|
||||
private val networkHasDerivationUseCase: NetworkHasDerivationUseCase,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
|
|
@ -82,6 +84,7 @@ internal class TokenDetailsStateFactory(
|
|||
appCurrencyProvider = appCurrencyProvider,
|
||||
stakingEntryInfoProvider = stakingEntryInfoProvider,
|
||||
stakingAvailabilityProvider = stakingAvailabilityProvider,
|
||||
pendingBalancesProvider = pendingBalancesProvider,
|
||||
symbol = symbol,
|
||||
decimals = decimals,
|
||||
clickIntents = clickIntents,
|
||||
|
|
|
|||
|
|
@ -32,12 +32,14 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase
|
||||
import com.tangem.domain.staking.GetStakingPendingTransactionsUseCase
|
||||
import com.tangem.domain.staking.GetStakingAvailabilityUseCase
|
||||
import com.tangem.domain.staking.GetStakingEntryInfoUseCase
|
||||
import com.tangem.domain.staking.GetStakingIntegrationIdUseCase
|
||||
import com.tangem.domain.staking.GetYieldUseCase
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction.TransactionInfo
|
||||
|
|
@ -119,6 +121,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val swapTransactionStatusStore: SwapTransactionStatusStore,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val associateAssetUseCase: AssociateAssetUseCase,
|
||||
private val getStakingPendingTransactionsUseCase: GetStakingPendingTransactionsUseCase,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
private val vibratorHapticManager: VibratorHapticManager,
|
||||
|
|
@ -148,6 +151,8 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val warningsJobHolder = JobHolder()
|
||||
private val swapTxJobHolder = JobHolder()
|
||||
private val selectedAppCurrencyFlow: StateFlow<AppCurrency> = createSelectedAppCurrencyFlow()
|
||||
private val stakingPendingBalances: List<BalanceItem>
|
||||
get() = getStakingPendingTransactionsUseCase(userWalletId).getOrElse { emptyList() }
|
||||
|
||||
private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||
private var stakingEntryInfo: StakingEntryInfo? = null
|
||||
|
|
@ -160,6 +165,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
stakingEntryInfoProvider = Provider { stakingEntryInfo },
|
||||
stakingAvailabilityProvider = Provider { stakingAvailability },
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
pendingBalancesProvider = Provider { stakingPendingBalances },
|
||||
clickIntents = this,
|
||||
networkHasDerivationUseCase = networkHasDerivationUseCase,
|
||||
getUserWalletUseCase = getUserWalletUseCase,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue