Updated on 2026-08-14
This commit is contained in:
parent
ede2066c64
commit
4a726e436c
31 changed files with 421 additions and 37 deletions
|
|
@ -22,7 +22,7 @@ internal sealed class InnerYieldBalanceState {
|
|||
|
||||
@Immutable
|
||||
internal data class BalanceState(
|
||||
val id: String,
|
||||
val groupId: String,
|
||||
val title: TextReference,
|
||||
val type: BalanceType,
|
||||
val subtitle: TextReference?,
|
||||
|
|
@ -34,4 +34,5 @@ internal data class BalanceState(
|
|||
val rawCurrencyId: String?,
|
||||
val validator: Yield.Validator?,
|
||||
val pendingActions: ImmutableList<PendingAction>,
|
||||
val isPending: Boolean,
|
||||
)
|
||||
|
|
@ -98,6 +98,7 @@ internal sealed class StakingStates {
|
|||
val transactionDoneState: TransactionDoneState,
|
||||
val isApprovalNeeded: Boolean,
|
||||
val reduceAmountBy: BigDecimal?,
|
||||
val balanceState: BalanceState?,
|
||||
) : ConfirmationState()
|
||||
|
||||
data class Empty(
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ internal class BalanceItemConverter(
|
|||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val yield: Yield,
|
||||
) : Converter<BalanceItem, BalanceState?> {
|
||||
|
||||
override fun convert(value: BalanceItem): BalanceState? {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val appCurrency = appCurrencyProvider()
|
||||
|
|
@ -37,7 +38,7 @@ internal class BalanceItemConverter(
|
|||
val title = value.type.getTitle(validator?.name)
|
||||
return title?.let {
|
||||
BalanceState(
|
||||
id = value.id,
|
||||
groupId = value.groupId,
|
||||
validator = validator,
|
||||
title = title,
|
||||
subtitle = getSubtitle(value),
|
||||
|
|
@ -59,7 +60,8 @@ internal class BalanceItemConverter(
|
|||
),
|
||||
rawCurrencyId = value.rawCurrencyId,
|
||||
pendingActions = value.pendingActions.toPersistentList(),
|
||||
isClickable = value.type.isClickable(),
|
||||
isClickable = value.type.isClickable() && !value.isPending,
|
||||
isPending = value.isPending,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ internal class RewardsValidatorStateConverter(
|
|||
)
|
||||
|
||||
return BalanceState(
|
||||
id = balance.id,
|
||||
groupId = balance.groupId,
|
||||
validator = this,
|
||||
title = stringReference(this.name),
|
||||
subtitle = null,
|
||||
|
|
@ -90,6 +90,7 @@ internal class RewardsValidatorStateConverter(
|
|||
pendingActions = balance.pendingActions.toPersistentList(),
|
||||
isClickable = true,
|
||||
type = balance.type,
|
||||
isPending = balance.isPending,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ import kotlinx.collections.immutable.toPersistentList
|
|||
internal class YieldBalancesConverter(
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val balancesToShowProvider: Provider<List<BalanceItem>>,
|
||||
private val yield: Yield,
|
||||
) : Converter<Unit, InnerYieldBalanceState> {
|
||||
|
||||
|
|
@ -32,6 +33,8 @@ internal class YieldBalancesConverter(
|
|||
val cryptoRewardsValue = yieldBalance.getRewardStakingBalance()
|
||||
val fiatRewardsValue = cryptoCurrencyStatus.value.fiatRate?.times(cryptoRewardsValue)
|
||||
|
||||
val balanceToShowItems = balancesToShowProvider()
|
||||
|
||||
InnerYieldBalanceState.Data(
|
||||
rewardsCrypto = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = cryptoRewardsValue,
|
||||
|
|
@ -43,7 +46,7 @@ internal class YieldBalancesConverter(
|
|||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
),
|
||||
rewardBlockType = getRewardBlockType(),
|
||||
balance = yieldBalance.balance.items.mapBalances(),
|
||||
balance = balanceToShowItems.mapBalances(),
|
||||
)
|
||||
} else {
|
||||
InnerYieldBalanceState.Empty
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import com.tangem.blockchain.common.TransactionData
|
|||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.staking.GetConstructedStakingTransactionUseCase
|
||||
import com.tangem.domain.staking.GetStakingTransactionUseCase
|
||||
import com.tangem.domain.staking.SaveUnsubmittedHashUseCase
|
||||
import com.tangem.domain.staking.SubmitHashUseCase
|
||||
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
|
||||
|
|
@ -45,6 +45,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
private val submitHashUseCase: SubmitHashUseCase,
|
||||
private val saveUnsubmittedHashUseCase: SaveUnsubmittedHashUseCase,
|
||||
private val savePendingTransactionUseCase: SavePendingTransactionUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
@Assisted private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
@Assisted private val userWallet: UserWallet,
|
||||
|
|
@ -89,6 +90,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
|
||||
sendStakingTransaction(
|
||||
fullTransactionsData = fullTransactionsData,
|
||||
balanceState = confirmationState.balanceState,
|
||||
onSendSuccess = onSendSuccess,
|
||||
onSendError = onSendError,
|
||||
)
|
||||
|
|
@ -191,6 +193,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
|
||||
private suspend fun sendStakingTransaction(
|
||||
fullTransactionsData: List<FullTransactionData>,
|
||||
balanceState: BalanceState?,
|
||||
onSendSuccess: (txUrl: String) -> Unit,
|
||||
onSendError: (SendTransactionError?) -> Unit,
|
||||
) {
|
||||
|
|
@ -206,6 +209,11 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
submitHash(
|
||||
transactionIds = fullTransactionsData.map { it.stakeKitTransaction.id },
|
||||
transactionHashes = transactionHashes,
|
||||
groupId = balanceState?.groupId,
|
||||
validator = balanceState?.validator,
|
||||
amount = balanceState?.cryptoDecimal,
|
||||
balanceType = balanceState?.type,
|
||||
rawCurrencyId = balanceState?.rawCurrencyId,
|
||||
)
|
||||
val txUrl = getExplorerTransactionUrlUseCase(
|
||||
txHash = transactionHashes.last(),
|
||||
|
|
@ -218,13 +226,27 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun submitHash(transactionIds: List<String>, transactionHashes: List<String>) {
|
||||
private suspend fun submitHash(
|
||||
transactionIds: List<String>,
|
||||
transactionHashes: List<String>,
|
||||
groupId: String?,
|
||||
validator: Yield.Validator?,
|
||||
amount: BigDecimal?,
|
||||
balanceType: BalanceType?,
|
||||
rawCurrencyId: String?,
|
||||
) {
|
||||
transactionIds
|
||||
.zip(transactionHashes)
|
||||
.forEach { (transactionId, transactionHash) ->
|
||||
submitHashUseCase.submitHash(
|
||||
transactionId = transactionId,
|
||||
transactionHash = transactionHash,
|
||||
submitHashUseCase(
|
||||
SubmitHashData(
|
||||
transactionId = transactionId,
|
||||
transactionHash = transactionHash,
|
||||
validator = validator,
|
||||
amount = amount,
|
||||
balanceType = balanceType,
|
||||
rawCurrencyId = rawCurrencyId,
|
||||
),
|
||||
)
|
||||
.onLeft {
|
||||
analyticsEventHandler.send(
|
||||
|
|
@ -236,6 +258,15 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
)
|
||||
}.onRight {
|
||||
Timber.d("Successful hash submission")
|
||||
savePendingTransactionUseCase.invoke(
|
||||
PendingTransaction(
|
||||
groupId = groupId,
|
||||
type = balanceType,
|
||||
amount = amount,
|
||||
rawCurrencyId = rawCurrencyId,
|
||||
validator = validator,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,5 +93,6 @@ internal object ConfirmationStatePreviewData {
|
|||
isApprovalNeeded = false,
|
||||
reduceAmountBy = null,
|
||||
pendingActions = null,
|
||||
balanceState = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -68,7 +68,7 @@ internal object InitialStakingStatePreview {
|
|||
rewardBlockType = RewardBlockType.RewardUnavailable,
|
||||
balance = persistentListOf(
|
||||
BalanceState(
|
||||
id = "id",
|
||||
groupId = "groupId",
|
||||
title = stringReference("Binance"),
|
||||
cryptoValue = "100",
|
||||
cryptoAmount = stringReference("100 SOL"),
|
||||
|
|
@ -91,6 +91,7 @@ internal object InitialStakingStatePreview {
|
|||
isClickable = true,
|
||||
type = BalanceType.STAKED,
|
||||
subtitle = null,
|
||||
isPending = false,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ internal object StakingClickIntentsStub : StakingClickIntents {
|
|||
actionTypeToOverwrite: StakingActionCommonType?,
|
||||
pendingAction: PendingAction?,
|
||||
pendingActions: ImmutableList<PendingAction>?,
|
||||
) {
|
||||
}
|
||||
balanceState: BalanceState?,
|
||||
) { }
|
||||
|
||||
override fun onActionClick() {}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ internal class SetConfirmationStateLoadingTransformer(
|
|||
pendingActions = possibleConfirmationState?.pendingActions,
|
||||
isApprovalNeeded = false,
|
||||
reduceAmountBy = null,
|
||||
balanceState = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig
|
|||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.core.serialization.SerializedBigDecimal
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
|
@ -37,6 +38,7 @@ internal class SetInitialDataStateTransformer(
|
|||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val userWalletProvider: Provider<UserWallet>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val balancesToShowProvider: Provider<List<BalanceItem>>,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
|
||||
|
|
@ -59,6 +61,7 @@ internal class SetInitialDataStateTransformer(
|
|||
YieldBalancesConverter(
|
||||
cryptoCurrencyStatusProvider,
|
||||
appCurrencyProvider,
|
||||
balancesToShowProvider,
|
||||
yield,
|
||||
)
|
||||
}
|
||||
|
|
@ -228,6 +231,7 @@ internal class SetInitialDataStateTransformer(
|
|||
pendingActions = null,
|
||||
isApprovalNeeded = isApprovalNeeded,
|
||||
reduceAmountBy = null,
|
||||
balanceState = null,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ internal fun StakingInitialInfoContent(
|
|||
if (state.showBanner) {
|
||||
item(key = BANNER_BLOCK_KEY) {
|
||||
Column(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
modifier = Modifier.animateItem(),
|
||||
) {
|
||||
BannerBlock(onClick = clickIntents::onInitialInfoBannerClick)
|
||||
SpacerH12()
|
||||
|
|
@ -105,7 +105,7 @@ internal fun StakingInitialInfoContent(
|
|||
|
||||
if (state.yieldBalance is InnerYieldBalanceState.Data) {
|
||||
item(key = STAKING_REWARD_BLOCK_KEY) {
|
||||
Column(modifier = Modifier.animateItemPlacement()) {
|
||||
Column(modifier = Modifier.animateItem()) {
|
||||
StakingRewardBlock(
|
||||
rewardCrypto = state.yieldBalance.rewardsCrypto,
|
||||
rewardFiat = state.yieldBalance.rewardsFiat,
|
||||
|
|
@ -138,7 +138,6 @@ internal fun StakingInitialInfoContent(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
private fun LazyListScope.activeStakingBlock(
|
||||
state: StakingStates.InitialInfoState.Data,
|
||||
clickIntents: StakingClickIntents,
|
||||
|
|
@ -175,7 +174,7 @@ private fun LazyListScope.activeStakingBlock(
|
|||
onClick = clickIntents::onActiveStake,
|
||||
onAnalytic = clickIntents::onActiveStakeAnalytic,
|
||||
modifier = Modifier
|
||||
.animateItemPlacement()
|
||||
.animateItem()
|
||||
.then(
|
||||
if (state.yieldBalance.balance.last() == balance) {
|
||||
Modifier.clip(CornersToRound.BOTTOM_2.getShape())
|
||||
|
|
@ -279,6 +278,7 @@ private fun ActiveStakingBlock(
|
|||
imageUrl = balance.getImage(),
|
||||
iconRes = icon,
|
||||
iconTint = iconTint,
|
||||
subtitleEndIconRes = R.drawable.ic_staking_pending_transaction.takeIf { balance.isPending },
|
||||
onImageError = { ValidatorImagePlaceholder() },
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.action)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
|
|||
actionTypeToOverwrite: StakingActionCommonType? = null,
|
||||
pendingAction: PendingAction? = null,
|
||||
pendingActions: ImmutableList<PendingAction>? = null,
|
||||
balanceState: BalanceState? = null,
|
||||
)
|
||||
|
||||
fun onActionClick()
|
||||
|
|
|
|||
|
|
@ -23,9 +23,11 @@ import com.tangem.domain.feedback.GetCardInfoUseCase
|
|||
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
|
||||
import com.tangem.domain.feedback.models.BlockchainErrorInfo
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
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
|
||||
|
|
@ -98,6 +100,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
private val getCurrencyCheckUseCase: GetCurrencyCheckUseCase,
|
||||
private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase,
|
||||
private val isAnyTokenStakedUseCase: IsAnyTokenStakedUseCase,
|
||||
private val invalidatePendingTransactionsUseCase: InvalidatePendingTransactionsUseCase,
|
||||
private val stakingTransactionLoader: StakingTransactionSender.Factory,
|
||||
private val stakingFeeTransactionLoader: StakingFeeTransactionLoader.Factory,
|
||||
private val stakingBalanceUpdater: StakingBalanceUpdater.Factory,
|
||||
|
|
@ -131,6 +134,13 @@ internal class StakingViewModel @Inject constructor(
|
|||
private var userWallet: UserWallet by Delegates.notNull()
|
||||
private var appCurrency: AppCurrency by Delegates.notNull()
|
||||
|
||||
private val balancesToShow: List<BalanceItem>
|
||||
get() {
|
||||
return cryptoCurrencyStatus.value.yieldBalance?.let {
|
||||
invalidatePendingTransactionsUseCase(it).getOrElse { emptyList() }
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
private var isInitialInfoAnalyticSent: Boolean = false
|
||||
|
||||
private val balanceUpdater by lazy(LazyThreadSafetyMode.NONE) {
|
||||
|
|
@ -200,6 +210,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
actionTypeToOverwrite: StakingActionCommonType?,
|
||||
pendingAction: PendingAction?,
|
||||
pendingActions: ImmutableList<PendingAction>?,
|
||||
balanceState: BalanceState?,
|
||||
) {
|
||||
if (actionTypeToOverwrite != null) {
|
||||
stateController.update(SetActionToExecuteTransformer(actionTypeToOverwrite, pendingAction, pendingActions))
|
||||
|
|
@ -208,10 +219,12 @@ internal class StakingViewModel @Inject constructor(
|
|||
when {
|
||||
isInitState() -> {
|
||||
stateController.update(SetConfirmationStateLoadingTransformer(yield, appCurrency))
|
||||
stateController.update(SetBalanceStateTransformer(balanceState))
|
||||
onRefreshSwipe(isRefreshing = false)
|
||||
}
|
||||
isAssentState() -> {
|
||||
getFee(pendingAction, pendingActions)
|
||||
stateController.update(SetBalanceStateTransformer(balanceState))
|
||||
val amountState = value.amountState as? AmountState.Data
|
||||
if (amountState?.amountTextField?.isWarning == true) {
|
||||
stateController.update(
|
||||
|
|
@ -413,6 +426,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
onNextClick(
|
||||
actionTypeToOverwrite = StakingActionCommonType.PENDING_OTHER,
|
||||
pendingAction = action,
|
||||
balanceState = activeStake,
|
||||
)
|
||||
stateController.update(DismissBottomSheetStateTransformer)
|
||||
},
|
||||
|
|
@ -428,6 +442,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
actionTypeToOverwrite = null,
|
||||
pendingAction = activeStake.pendingActions.firstOrNull(),
|
||||
pendingActions = activeStake.pendingActions.takeIf { isAllWithdrawActions },
|
||||
balanceState = activeStake,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -749,6 +764,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
userWalletProvider = Provider { userWallet },
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
balancesToShowProvider = Provider { balancesToShow },
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue