Updated on 2026-08-14
This commit is contained in:
commit
652159dbce
316 changed files with 6242 additions and 3084 deletions
|
|
@ -32,10 +32,10 @@ internal class StakingAnalyticSender(
|
|||
|
||||
fun confirmationScreen(value: StakingUiState) {
|
||||
val confirmationState = value.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val validatorState = confirmationState?.validatorState as? ValidatorState.Content
|
||||
val validatorState = value.validatorState as? StakingStates.ValidatorState.Data
|
||||
val validatorName = validatorState?.chosenValidator?.name ?: return
|
||||
|
||||
if (confirmationState.innerState == InnerConfirmationStakingState.COMPLETED) return
|
||||
if (confirmationState?.innerState == InnerConfirmationStakingState.COMPLETED) return
|
||||
|
||||
analyticsEventHandler.send(
|
||||
StakingAnalyticsEvent.ConfirmationScreenOpened(
|
||||
|
|
@ -53,6 +53,7 @@ internal class StakingAnalyticSender(
|
|||
StakingStep.Amount -> StakeScreenSource.Amount
|
||||
StakingStep.Confirmation -> StakeScreenSource.Confirmation
|
||||
StakingStep.Validators,
|
||||
StakingStep.RestakeValidator,
|
||||
StakingStep.RewardsValidators,
|
||||
-> StakeScreenSource.Validators
|
||||
},
|
||||
|
|
@ -75,8 +76,7 @@ internal class StakingAnalyticSender(
|
|||
}
|
||||
|
||||
fun sendTransactionStakingAnalytics(value: StakingUiState) {
|
||||
val confirmationState = value.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val validatorState = confirmationState?.validatorState as? ValidatorState.Content
|
||||
val validatorState = value.validatorState as? StakingStates.ValidatorState.Data
|
||||
val validatorName = validatorState?.chosenValidator?.name ?: return
|
||||
|
||||
analyticsEventHandler.send(
|
||||
|
|
@ -98,8 +98,7 @@ internal class StakingAnalyticSender(
|
|||
}
|
||||
|
||||
fun sendTransactionStakingClickedAnalytics(value: StakingUiState) {
|
||||
val confirmationState = value.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val validatorState = confirmationState?.validatorState as? ValidatorState.Content
|
||||
val validatorState = value.validatorState as? StakingStates.ValidatorState.Data
|
||||
val validatorName = validatorState?.chosenValidator?.name ?: return
|
||||
|
||||
analyticsEventHandler.send(
|
||||
|
|
@ -114,11 +113,9 @@ internal class StakingAnalyticSender(
|
|||
val confirmationState = value.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
|
||||
return when (value.actionType) {
|
||||
StakingActionCommonType.ENTER -> StakingActionType.STAKE
|
||||
StakingActionCommonType.EXIT -> StakingActionType.UNSTAKE
|
||||
StakingActionCommonType.PENDING_REWARDS,
|
||||
StakingActionCommonType.PENDING_OTHER,
|
||||
-> confirmationState?.pendingAction?.type ?: StakingActionType.UNKNOWN
|
||||
StakingActionCommonType.Enter -> StakingActionType.STAKE
|
||||
StakingActionCommonType.Exit -> StakingActionType.UNSTAKE
|
||||
is StakingActionCommonType.Pending -> confirmationState?.pendingAction?.type ?: StakingActionType.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +45,12 @@ internal class StakingStateController @Inject constructor(
|
|||
mutableUiState.update(function = titleTransformer::transform)
|
||||
}
|
||||
|
||||
fun updateAll(vararg transformer: Transformer<StakingUiState>) {
|
||||
transformer.forEach { mutableUiState.update(function = it::transform) }
|
||||
mutableUiState.update(function = buttonsTransformer::transform)
|
||||
mutableUiState.update(function = titleTransformer::transform)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
mutableUiState.update { getInitialState() }
|
||||
mutableUiState.update(function = buttonsTransformer::transform)
|
||||
|
|
@ -72,12 +78,13 @@ internal class StakingStateController @Inject constructor(
|
|||
currentStep = StakingStep.InitialInfo,
|
||||
initialInfoState = StakingStates.InitialInfoState.Empty(),
|
||||
amountState = AmountState.Empty(),
|
||||
validatorState = StakingStates.ValidatorState.Empty(),
|
||||
rewardsValidatorsState = StakingStates.RewardsValidatorsState.Empty(),
|
||||
confirmationState = StakingStates.ConfirmationState.Empty(),
|
||||
isBalanceHidden = false,
|
||||
event = consumedEvent(),
|
||||
bottomSheetConfig = null,
|
||||
actionType = StakingActionCommonType.ENTER,
|
||||
actionType = StakingActionCommonType.Enter,
|
||||
buttonsState = NavigationButtonsState.Empty,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,14 @@ internal class StakingStateRouter(
|
|||
fun onNextClick() {
|
||||
when (stateController.value.currentStep) {
|
||||
StakingStep.InitialInfo -> when (stateController.value.actionType) {
|
||||
StakingActionCommonType.ENTER -> showAmount()
|
||||
StakingActionCommonType.PENDING_OTHER,
|
||||
StakingActionCommonType.EXIT,
|
||||
StakingActionCommonType.Enter -> showAmount()
|
||||
StakingActionCommonType.Pending.Other,
|
||||
StakingActionCommonType.Exit,
|
||||
StakingActionCommonType.Pending.Rewards,
|
||||
-> showConfirmation()
|
||||
StakingActionCommonType.PENDING_REWARDS -> showRewardsValidators()
|
||||
StakingActionCommonType.Pending.Restake -> showRestakeValidators()
|
||||
}
|
||||
StakingStep.RestakeValidator,
|
||||
StakingStep.RewardsValidators,
|
||||
StakingStep.Validators,
|
||||
StakingStep.Amount,
|
||||
|
|
@ -41,25 +43,31 @@ internal class StakingStateRouter(
|
|||
val uiState = stateController.uiState.value
|
||||
when (uiState.currentStep) {
|
||||
StakingStep.InitialInfo -> onBackClick()
|
||||
StakingStep.Amount -> showInitial()
|
||||
StakingStep.RestakeValidator,
|
||||
StakingStep.RewardsValidators,
|
||||
StakingStep.Amount,
|
||||
-> showInitial()
|
||||
StakingStep.Confirmation -> {
|
||||
if (uiState.actionType != StakingActionCommonType.ENTER) {
|
||||
if (uiState.actionType != StakingActionCommonType.Enter) {
|
||||
showInitial()
|
||||
} else {
|
||||
showAmount()
|
||||
}
|
||||
}
|
||||
StakingStep.Validators -> showConfirmation()
|
||||
StakingStep.RewardsValidators -> showInitial()
|
||||
}
|
||||
}
|
||||
|
||||
fun showValidators() {
|
||||
stateController.update { it.copy(currentStep = StakingStep.Validators) }
|
||||
}
|
||||
|
||||
private fun showInitial() {
|
||||
analyticSender.initialInfoScreen(stateController.value)
|
||||
stateController.update { it.copy(currentStep = StakingStep.InitialInfo) }
|
||||
}
|
||||
|
||||
private fun showRewardsValidators() {
|
||||
fun showRewardsValidators() {
|
||||
analyticsEventsHandler.send(StakingAnalyticsEvent.RewardScreenOpened)
|
||||
stateController.update { it.copy(currentStep = StakingStep.RewardsValidators) }
|
||||
}
|
||||
|
|
@ -69,8 +77,8 @@ internal class StakingStateRouter(
|
|||
stateController.update { it.copy(currentStep = StakingStep.Amount) }
|
||||
}
|
||||
|
||||
fun showValidators() {
|
||||
stateController.update { it.copy(currentStep = StakingStep.Validators) }
|
||||
private fun showRestakeValidators() {
|
||||
stateController.update { it.copy(currentStep = StakingStep.RestakeValidator) }
|
||||
}
|
||||
|
||||
private fun showConfirmation() {
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ 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.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType
|
||||
import com.tangem.features.staking.impl.presentation.state.events.StakingEvent
|
||||
|
|
@ -34,6 +34,7 @@ internal data class StakingUiState(
|
|||
val amountState: AmountState,
|
||||
val rewardsValidatorsState: StakingStates.RewardsValidatorsState,
|
||||
val confirmationState: StakingStates.ConfirmationState,
|
||||
val validatorState: StakingStates.ValidatorState,
|
||||
val isBalanceHidden: Boolean,
|
||||
val bottomSheetConfig: TangemBottomSheetConfig?,
|
||||
val actionType: StakingActionCommonType,
|
||||
|
|
@ -45,10 +46,12 @@ internal data class StakingUiState(
|
|||
initialInfoState: StakingStates.InitialInfoState = this.initialInfoState,
|
||||
amountState: AmountState = this.amountState,
|
||||
confirmationState: StakingStates.ConfirmationState = this.confirmationState,
|
||||
validatorState: StakingStates.ValidatorState = this.validatorState,
|
||||
): StakingUiState = copy(
|
||||
initialInfoState = initialInfoState,
|
||||
amountState = amountState,
|
||||
confirmationState = confirmationState,
|
||||
validatorState = validatorState,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -85,13 +88,30 @@ internal sealed class StakingStates {
|
|||
) : RewardsValidatorsState()
|
||||
}
|
||||
|
||||
sealed class ValidatorState : StakingStates() {
|
||||
abstract val isClickable: Boolean
|
||||
|
||||
data class Data(
|
||||
override val isPrimaryButtonEnabled: Boolean,
|
||||
override val isClickable: Boolean,
|
||||
val isVisibleOnConfirmation: Boolean,
|
||||
val chosenValidator: Yield.Validator,
|
||||
val activeValidator: Yield.Validator?,
|
||||
val availableValidators: List<Yield.Validator>,
|
||||
) : ValidatorState()
|
||||
|
||||
data class Empty(
|
||||
override val isClickable: Boolean = false,
|
||||
override val isPrimaryButtonEnabled: Boolean = false,
|
||||
) : ValidatorState()
|
||||
}
|
||||
|
||||
/** Confirmation state */
|
||||
sealed class ConfirmationState : StakingStates() {
|
||||
data class Data(
|
||||
override val isPrimaryButtonEnabled: Boolean,
|
||||
val innerState: InnerConfirmationStakingState,
|
||||
val feeState: FeeState,
|
||||
val validatorState: ValidatorState,
|
||||
val pendingAction: PendingAction?,
|
||||
val pendingActions: ImmutableList<PendingAction>?,
|
||||
val notifications: ImmutableList<NotificationUM>,
|
||||
|
|
@ -99,7 +119,6 @@ internal sealed class StakingStates {
|
|||
val transactionDoneState: TransactionDoneState,
|
||||
val isApprovalNeeded: Boolean,
|
||||
val reduceAmountBy: BigDecimal?,
|
||||
val possiblePendingTransaction: PendingTransaction?,
|
||||
) : ConfirmationState()
|
||||
|
||||
data class Empty(
|
||||
|
|
@ -112,6 +131,7 @@ enum class StakingStep {
|
|||
InitialInfo,
|
||||
RewardsValidators,
|
||||
Amount,
|
||||
RestakeValidator,
|
||||
Confirmation,
|
||||
Validators,
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
package com.tangem.features.staking.impl.presentation.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
|
||||
@Immutable
|
||||
internal sealed class ValidatorState {
|
||||
|
||||
abstract val isClickable: Boolean
|
||||
|
||||
data class Content(
|
||||
override val isClickable: Boolean,
|
||||
val chosenValidator: Yield.Validator,
|
||||
val availableValidators: List<Yield.Validator>,
|
||||
) : ValidatorState()
|
||||
|
||||
data object Loading : ValidatorState() {
|
||||
override val isClickable: Boolean
|
||||
get() = false
|
||||
}
|
||||
|
||||
data object Error : ValidatorState() {
|
||||
override val isClickable: Boolean
|
||||
get() = false
|
||||
}
|
||||
|
||||
fun copySealed(isClickable: Boolean): ValidatorState {
|
||||
return if (this is Content) {
|
||||
copy(isClickable = isClickable)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.converters
|
||||
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
|
|
@ -47,10 +49,7 @@ internal class BalanceItemConverter(
|
|||
cryptoValue = cryptoAmount.parseBigDecimal(cryptoCurrency.decimals),
|
||||
cryptoDecimal = cryptoAmount,
|
||||
cryptoAmount = stringReference(
|
||||
BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = cryptoAmount,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
),
|
||||
cryptoAmount.format { crypto(cryptoCurrency) },
|
||||
),
|
||||
fiatAmount = stringReference(
|
||||
BigDecimalFormatter.formatFiatAmount(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.converters
|
||||
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
|
|
@ -65,10 +67,9 @@ internal class RewardsValidatorStateConverter(
|
|||
val appCurrency = appCurrencyProvider()
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val cryptoAmount = stringReference(
|
||||
BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = cryptoValue,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
),
|
||||
cryptoValue.format {
|
||||
crypto(cryptoCurrency)
|
||||
},
|
||||
)
|
||||
val fiatAmount = stringReference(
|
||||
BigDecimalFormatter.formatFiatAmount(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.converters
|
||||
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.stakekit.*
|
||||
|
|
@ -41,10 +43,7 @@ internal class YieldBalancesConverter(
|
|||
}
|
||||
val (type, isActionable) = getRewardBlockType()
|
||||
InnerYieldBalanceState.Data(
|
||||
rewardsCrypto = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = cryptoRewardsValue,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
),
|
||||
rewardsCrypto = cryptoRewardsValue.format { crypto(cryptoCurrency) },
|
||||
rewardsFiat = BigDecimalFormatter.formatFiatAmount(
|
||||
fiatAmount = fiatRewardsValue,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.helpers
|
||||
|
||||
import com.tangem.domain.staking.FetchStakingYieldBalanceUseCase
|
||||
import com.tangem.domain.staking.FetchActionsUseCase
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus
|
||||
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
|
||||
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -20,16 +23,21 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
|
|||
private val stakingYieldBalanceUseCase: FetchStakingYieldBalanceUseCase,
|
||||
private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val getTxHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
private val fetchActionsUseCase: FetchActionsUseCase,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
@Assisted private val userWallet: UserWallet,
|
||||
@Assisted private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
@Assisted private val yield: Yield,
|
||||
) {
|
||||
fun scheduleUpdates() {
|
||||
fun fullUpdate() {
|
||||
coroutineScope.launch {
|
||||
listOf(
|
||||
// we should update network to find pending tx after 1 sec
|
||||
async {
|
||||
fetchPendingTransactionsUseCase(userWallet.walletId, setOf(cryptoCurrencyStatus.currency.network))
|
||||
fetchPendingTransactionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
networks = setOf(cryptoCurrencyStatus.currency.network),
|
||||
)
|
||||
},
|
||||
// we should update tx history and network for new balances
|
||||
async {
|
||||
|
|
@ -41,11 +49,14 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
|
|||
async {
|
||||
updateNetworkStatuses()
|
||||
},
|
||||
async {
|
||||
updateProcessingActions()
|
||||
},
|
||||
).awaitAll()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun instantUpdate() {
|
||||
suspend fun partialUpdate() {
|
||||
coroutineScope {
|
||||
listOf(
|
||||
async {
|
||||
|
|
@ -54,6 +65,19 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
|
|||
async {
|
||||
updateNetworkStatuses(delay = 0)
|
||||
},
|
||||
async {
|
||||
updateProcessingActions()
|
||||
},
|
||||
).awaitAll()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun initialUpdate() {
|
||||
coroutineScope {
|
||||
listOf(
|
||||
async {
|
||||
updateStakeBalance()
|
||||
},
|
||||
).awaitAll()
|
||||
}
|
||||
}
|
||||
|
|
@ -91,9 +115,22 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun updateProcessingActions() {
|
||||
fetchActionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
networkType = yield.token.network,
|
||||
stakingActionStatus = StakingActionStatus.PROCESSING,
|
||||
)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(cryptoCurrencyStatus: CryptoCurrencyStatus, userWallet: UserWallet): StakingBalanceUpdater
|
||||
fun create(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
userWallet: UserWallet,
|
||||
yield: Yield,
|
||||
): StakingBalanceUpdater
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -22,8 +22,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.ValidatorState
|
||||
import com.tangem.features.staking.impl.presentation.state.utils.isSolanaWithdraw
|
||||
import com.tangem.features.staking.impl.presentation.state.utils.isComposePendingActions
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -48,8 +47,6 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor(
|
|||
) {
|
||||
|
||||
suspend fun getFee(
|
||||
pendingAction: PendingAction?,
|
||||
pendingActions: ImmutableList<PendingAction>?,
|
||||
onStakingFee: (Fee) -> Unit,
|
||||
onStakingFeeError: (StakingError) -> Unit,
|
||||
onApprovalFee: (TransactionFee) -> Unit,
|
||||
|
|
@ -57,17 +54,20 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor(
|
|||
) {
|
||||
val state = stateController.value
|
||||
val confirmationState = state.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
?: error("No confirmation state")
|
||||
val validatorState = confirmationState.validatorState as? ValidatorState.Content
|
||||
?: error("Illegal state")
|
||||
val validatorState = state.validatorState as? StakingStates.ValidatorState.Data
|
||||
?: error("No validator provided")
|
||||
|
||||
val amount = (state.amountState as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
|
||||
?: error("No amount provided")
|
||||
|
||||
val pendingAction = confirmationState.pendingAction
|
||||
val pendingActions = confirmationState.pendingActions
|
||||
|
||||
val validatorAddress = validatorState.chosenValidator.address
|
||||
|
||||
val approval = stakingApproval as? StakingApproval.Needed
|
||||
if (approval != null && state.actionType == StakingActionCommonType.ENTER) {
|
||||
if (approval != null && state.actionType == StakingActionCommonType.Enter) {
|
||||
val allowance = getAllowanceUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
|
|
@ -114,7 +114,7 @@ internal class StakingFeeTransactionLoader @AssistedInject constructor(
|
|||
val sourceAddress = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
|
||||
?: error("No available address")
|
||||
|
||||
val gasEstimate = if (isSolanaWithdraw(cryptoCurrencyStatus.currency.network.id.value, pendingActions)) {
|
||||
val gasEstimate = if (isComposePendingActions(cryptoCurrencyStatus.currency.network.id.value, pendingActions)) {
|
||||
val result = coroutineScope {
|
||||
pendingActions?.map { action ->
|
||||
async {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.tangem.blockchain.common.TransactionData
|
|||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
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.*
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
|
|
@ -21,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.isSolanaWithdraw
|
||||
import com.tangem.features.staking.impl.presentation.state.utils.isComposePendingActions
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
|
|
@ -31,7 +30,6 @@ 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(
|
||||
|
|
@ -43,7 +41,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
private val submitHashUseCase: SubmitHashUseCase,
|
||||
private val saveUnsubmittedHashUseCase: SaveUnsubmittedHashUseCase,
|
||||
private val savePendingTransactionUseCase: SavePendingTransactionUseCase,
|
||||
@Assisted private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
@Assisted private val userWallet: UserWallet,
|
||||
@Assisted private val yield: Yield,
|
||||
|
|
@ -51,7 +48,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
) {
|
||||
|
||||
private val balanceUpdater: StakingBalanceUpdater
|
||||
get() = stakingBalanceUpdater.create(cryptoCurrencyStatus, userWallet)
|
||||
get() = stakingBalanceUpdater.create(cryptoCurrencyStatus, userWallet, yield)
|
||||
|
||||
suspend fun constructAndSendTransactions(
|
||||
onConstructSuccess: (List<StakingTransaction>) -> Unit,
|
||||
|
|
@ -89,26 +86,21 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
|
||||
sendStakingTransaction(
|
||||
fullTransactionsData = fullTransactionsData,
|
||||
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,
|
||||
onConstructError: (StakingError) -> Unit,
|
||||
) = coroutineScope {
|
||||
val isAllWithdrawAction = isSolanaWithdraw(
|
||||
val isComposePendingActions = isComposePendingActions(
|
||||
cryptoCurrencyStatus.currency.network.id.value,
|
||||
confirmationState.pendingActions,
|
||||
)
|
||||
if (isAllWithdrawAction) {
|
||||
if (isComposePendingActions) {
|
||||
confirmationState.pendingActions?.map { action ->
|
||||
async {
|
||||
getStakingTransaction(
|
||||
|
|
@ -169,7 +161,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
action: PendingAction? = confirmationState.pendingAction,
|
||||
onConstructError: (StakingError) -> Unit,
|
||||
): List<StakingTransaction> {
|
||||
val validatorState = confirmationState.validatorState as? ValidatorState.Content
|
||||
val validatorState = state.validatorState as? StakingStates.ValidatorState.Data
|
||||
?: error("No validator provided")
|
||||
val fee = (confirmationState.feeState as? FeeState.Content)?.fee
|
||||
?: error("No fee provided")
|
||||
|
|
@ -202,7 +194,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
|
||||
private suspend fun sendStakingTransaction(
|
||||
fullTransactionsData: List<FullTransactionData>,
|
||||
possiblePendingTransaction: PendingTransaction,
|
||||
onSendSuccess: (txUrl: String) -> Unit,
|
||||
onSendError: (SendTransactionError?) -> Unit,
|
||||
) {
|
||||
|
|
@ -218,24 +209,19 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
submitHash(
|
||||
transactions = fullTransactionsData.map { it.stakeKitTransaction },
|
||||
transactionHashes = transactionHashes,
|
||||
pendingTransaction = possiblePendingTransaction,
|
||||
)
|
||||
val txUrl = getExplorerTransactionUrlUseCase(
|
||||
txHash = transactionHashes.last(),
|
||||
networkId = cryptoCurrencyStatus.currency.network.id,
|
||||
).getOrElse { "" }
|
||||
|
||||
balanceUpdater.scheduleUpdates()
|
||||
balanceUpdater.fullUpdate()
|
||||
onSendSuccess(txUrl)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun submitHash(
|
||||
transactions: List<StakingTransaction>,
|
||||
transactionHashes: List<String>,
|
||||
pendingTransaction: PendingTransaction,
|
||||
) {
|
||||
private suspend fun submitHash(transactions: List<StakingTransaction>, transactionHashes: List<String>) {
|
||||
transactions
|
||||
.zip(transactionHashes)
|
||||
.forEach { (transaction, transactionHash) ->
|
||||
|
|
@ -243,7 +229,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
SubmitHashData(
|
||||
transactionId = transaction.id,
|
||||
transactionHash = transactionHash,
|
||||
pendingTransaction = pendingTransaction,
|
||||
),
|
||||
)
|
||||
.onLeft {
|
||||
|
|
@ -253,9 +238,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
)
|
||||
}.onRight {
|
||||
Timber.d("Successful hash submission")
|
||||
if (transaction.type != StakingTransactionType.FREEZE_ENERGY) {
|
||||
savePendingTransactionUseCase.invoke(userWallet.walletId, pendingTransaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -263,7 +245,7 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
private fun getAmount(amountState: AmountState.Data, fee: Fee, reduceAmountBy: BigDecimal?): BigDecimal {
|
||||
val amountValue = amountState.amountTextField.cryptoAmount.value ?: error("No amount value")
|
||||
val feeValue = fee.amount.value ?: error("No fee value")
|
||||
val isEnterAction = stateController.value.actionType == StakingActionCommonType.ENTER
|
||||
val isEnterAction = stateController.value.actionType == StakingActionCommonType.Enter
|
||||
|
||||
return checkAndCalculateSubtractedAmount(
|
||||
isAmountSubtractAvailable = isAmountSubtractAvailable && isEnterAction,
|
||||
|
|
@ -274,23 +256,6 @@ internal class StakingTransactionSender @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun composeStakeTransaction(confirmationState: StakingStates.ConfirmationState.Data): PendingTransaction {
|
||||
val state = stateController.value
|
||||
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
val token = yield.getCurrentToken(cryptoCurrencyStatus.currency.id.rawCurrencyId)
|
||||
|
||||
return PendingTransaction(
|
||||
groupId = UUID.randomUUID().toString(),
|
||||
token = token,
|
||||
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 = yieldBalance?.getBalancesUniqueId() ?: 0,
|
||||
)
|
||||
}
|
||||
|
||||
private data class FullTransactionData(
|
||||
val stakeKitTransaction: StakingTransaction,
|
||||
val tangemTransaction: TransactionData.Compiled,
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.Yield.Validator.ValidatorStatus
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -16,48 +14,6 @@ import java.math.BigDecimal
|
|||
|
||||
internal object ConfirmationStatePreviewData {
|
||||
|
||||
private val validatorList = listOf(
|
||||
Yield.Validator(
|
||||
address = "0xa6e768fef2d1af36c0cfdb276422e7881a83e951",
|
||||
status = ValidatorStatus.ACTIVE,
|
||||
name = "Luganodes",
|
||||
image = "https://assets.stakek.it/validators/luganodes.png",
|
||||
apr = BigDecimal("0.054823398040640445"),
|
||||
commission = 0.1,
|
||||
stakedBalance = "355544384.45009977",
|
||||
website = "https://luganodes.com/",
|
||||
votingPower = 0.09778360195377911,
|
||||
preferred = true,
|
||||
isStrategicPartner = false,
|
||||
),
|
||||
Yield.Validator(
|
||||
address = "0x35b1ca0f398905cf752e6fe122b51c88022fca32",
|
||||
status = ValidatorStatus.ACTIVE,
|
||||
name = "InfStones",
|
||||
image = "https://assets.stakek.it/validators/infstones.png",
|
||||
apr = BigDecimal("0.057786472172836965"),
|
||||
commission = 0.05,
|
||||
stakedBalance = "12495684.05643019",
|
||||
website = "https://infstones.com/",
|
||||
votingPower = 0.0034366257754399774,
|
||||
preferred = true,
|
||||
isStrategicPartner = false,
|
||||
),
|
||||
Yield.Validator(
|
||||
address = "0xd14a87025109013b0a2354a775cb335f926af65a",
|
||||
status = ValidatorStatus.ACTIVE,
|
||||
name = "Kiln",
|
||||
image = "https://assets.stakek.it/validators/kiln.png",
|
||||
apr = BigDecimal("0.057786472172836965"),
|
||||
commission = 0.05,
|
||||
stakedBalance = "85400369.96393165",
|
||||
website = "https://infstones.com/",
|
||||
votingPower = 0.023487238579718264,
|
||||
preferred = true,
|
||||
isStrategicPartner = true,
|
||||
),
|
||||
)
|
||||
|
||||
private val fee = Fee.Common(
|
||||
amount = Amount(
|
||||
currencySymbol = "MATIC",
|
||||
|
|
@ -77,11 +33,6 @@ internal object ConfirmationStatePreviewData {
|
|||
isFeeApproximate = false,
|
||||
isFeeConvertibleToFiat = true,
|
||||
),
|
||||
validatorState = ValidatorState.Content(
|
||||
isClickable = true,
|
||||
chosenValidator = validatorList[0],
|
||||
availableValidators = validatorList,
|
||||
),
|
||||
footerText = stringReference("You stake \$715.11 and will be receiving ~\$35 monthly"),
|
||||
notifications = persistentListOf(
|
||||
StakingNotification.Info.EarnRewards(
|
||||
|
|
@ -96,6 +47,5 @@ internal object ConfirmationStatePreviewData {
|
|||
isApprovalNeeded = false,
|
||||
reduceAmountBy = null,
|
||||
pendingActions = null,
|
||||
possiblePendingTransaction = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.previewdata
|
||||
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.Yield.Validator.ValidatorStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal object ValidatorStatePreviewData {
|
||||
|
||||
private val validatorList = listOf(
|
||||
Yield.Validator(
|
||||
address = "0xa6e768fef2d1af36c0cfdb276422e7881a83e951",
|
||||
status = ValidatorStatus.ACTIVE,
|
||||
name = "Luganodes",
|
||||
image = "https://assets.stakek.it/validators/luganodes.png",
|
||||
apr = BigDecimal("0.054823398040640445"),
|
||||
commission = 0.1,
|
||||
stakedBalance = "355544384.45009977",
|
||||
website = "https://luganodes.com/",
|
||||
votingPower = 0.09778360195377911,
|
||||
preferred = true,
|
||||
isStrategicPartner = false,
|
||||
),
|
||||
Yield.Validator(
|
||||
address = "0x35b1ca0f398905cf752e6fe122b51c88022fca32",
|
||||
status = ValidatorStatus.ACTIVE,
|
||||
name = "InfStones",
|
||||
image = "https://assets.stakek.it/validators/infstones.png",
|
||||
apr = BigDecimal("0.057786472172836965"),
|
||||
commission = 0.05,
|
||||
stakedBalance = "12495684.05643019",
|
||||
website = "https://infstones.com/",
|
||||
votingPower = 0.0034366257754399774,
|
||||
preferred = true,
|
||||
isStrategicPartner = true,
|
||||
),
|
||||
Yield.Validator(
|
||||
address = "0xd14a87025109013b0a2354a775cb335f926af65a",
|
||||
status = ValidatorStatus.ACTIVE,
|
||||
name = "Kiln",
|
||||
image = "https://assets.stakek.it/validators/kiln.png",
|
||||
apr = BigDecimal("0.057786472172836965"),
|
||||
commission = 0.05,
|
||||
stakedBalance = "85400369.96393165",
|
||||
website = "https://infstones.com/",
|
||||
votingPower = 0.023487238579718264,
|
||||
preferred = true,
|
||||
isStrategicPartner = false,
|
||||
),
|
||||
)
|
||||
|
||||
val validatorState = StakingStates.ValidatorState.Data(
|
||||
availableValidators = validatorList,
|
||||
chosenValidator = validatorList.first(),
|
||||
isPrimaryButtonEnabled = true,
|
||||
activeValidator = null,
|
||||
isClickable = true,
|
||||
isVisibleOnConfirmation = true,
|
||||
)
|
||||
}
|
||||
|
|
@ -2,14 +2,11 @@ package com.tangem.features.staking.impl.presentation.state.stub
|
|||
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
||||
import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
|
|
@ -17,13 +14,7 @@ internal object StakingClickIntentsStub : StakingClickIntents {
|
|||
|
||||
override fun onBackClick() {}
|
||||
|
||||
override fun onNextClick(
|
||||
actionTypeToOverwrite: StakingActionCommonType?,
|
||||
pendingAction: PendingAction?,
|
||||
pendingActions: ImmutableList<PendingAction>?,
|
||||
balanceState: BalanceState?,
|
||||
) {
|
||||
}
|
||||
override fun onNextClick(balanceState: BalanceState?) {}
|
||||
|
||||
override fun onActionClick() {}
|
||||
|
||||
|
|
@ -67,8 +58,7 @@ internal object StakingClickIntentsStub : StakingClickIntents {
|
|||
|
||||
override fun onActiveStake(activeStake: BalanceState) {}
|
||||
|
||||
override fun getFee(pendingAction: PendingAction?, pendingActions: ImmutableList<PendingAction>?) {
|
||||
}
|
||||
override fun getFee() {}
|
||||
|
||||
override fun onAmountReduceByClick(
|
||||
reduceAmountBy: BigDecimal,
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceType
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isTron
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class ActionTypeActiveStakeTransformer(
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val activeStake: BalanceState,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val isTronStakedBalance = isTronStakedBalance(
|
||||
networkId = cryptoCurrencyStatus.currency.network.id,
|
||||
activeStake = activeStake,
|
||||
)
|
||||
val actionType = if (activeStake.pendingActions.isEmpty() || isTronStakedBalance) {
|
||||
StakingActionCommonType.EXIT
|
||||
} else {
|
||||
StakingActionCommonType.PENDING_OTHER
|
||||
}
|
||||
|
||||
return prevState.copy(actionType = actionType)
|
||||
}
|
||||
|
||||
private fun isTronStakedBalance(networkId: Network.ID, activeStake: BalanceState): Boolean {
|
||||
return isTron(networkId.value) && activeStake.type == BalanceType.STAKED
|
||||
}
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ internal class AddStakingNotificationsTransformer(
|
|||
val feeValue = feeState?.fee?.amount?.value.orZero()
|
||||
val reduceAmountBy = confirmationState.reduceAmountBy.orZero()
|
||||
|
||||
val isEnterAction = prevState.actionType == StakingActionCommonType.ENTER
|
||||
val isEnterAction = prevState.actionType == StakingActionCommonType.Enter
|
||||
val isFeeCoverage = checkFeeCoverage(
|
||||
amountValue = amountValue,
|
||||
feeValue = feeValue,
|
||||
|
|
@ -93,12 +93,7 @@ internal class AddStakingNotificationsTransformer(
|
|||
prevState = prevState,
|
||||
feeError = feeError,
|
||||
sendingAmount = sendingAmount,
|
||||
onReload = {
|
||||
prevState.clickIntents.getFee(
|
||||
confirmationState.pendingAction,
|
||||
confirmationState.pendingActions,
|
||||
)
|
||||
},
|
||||
onReload = prevState.clickIntents::getFee,
|
||||
feeValue = feeValue,
|
||||
)
|
||||
// warnings
|
||||
|
|
@ -228,7 +223,7 @@ internal class AddStakingNotificationsTransformer(
|
|||
|
||||
val showNotification = sendingAmount + feeAmount > balance
|
||||
if (showNotification) {
|
||||
val notification = if (actionType == StakingActionCommonType.ENTER) {
|
||||
val notification = if (actionType == StakingActionCommonType.Enter) {
|
||||
NotificationUM.Error.TotalExceedsBalance
|
||||
} else {
|
||||
with(cryptoCurrencyStatus.currency) {
|
||||
|
|
@ -249,9 +244,9 @@ internal class AddStakingNotificationsTransformer(
|
|||
|
||||
private fun MutableList<NotificationUM>.addInfoNotifications(prevState: StakingUiState) {
|
||||
when (prevState.actionType) {
|
||||
StakingActionCommonType.EXIT -> addExitInfoNotifications()
|
||||
StakingActionCommonType.ENTER -> addEnterInfoNotifications()
|
||||
else -> addPendingInfoNotifications(prevState)
|
||||
StakingActionCommonType.Enter -> addEnterInfoNotifications()
|
||||
StakingActionCommonType.Exit -> addExitInfoNotifications()
|
||||
is StakingActionCommonType.Pending -> addPendingInfoNotifications(prevState)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -312,6 +307,10 @@ internal class AddStakingNotificationsTransformer(
|
|||
resourceReference(R.string.staking_revote) to
|
||||
resourceReference(R.string.staking_notifications_revote_tron_text)
|
||||
}
|
||||
StakingActionType.RESTAKE -> {
|
||||
resourceReference(R.string.staking_restake) to
|
||||
resourceReference(R.string.staking_notification_restake_text)
|
||||
}
|
||||
else -> null to null
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
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.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal class SetActionToExecuteTransformer(
|
||||
private val actionTypeToOverwrite: StakingActionCommonType,
|
||||
private val pendingAction: PendingAction?,
|
||||
private val pendingActions: ImmutableList<PendingAction>?,
|
||||
) : Transformer<StakingUiState> {
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val confirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
return prevState.copy(
|
||||
actionType = actionTypeToOverwrite,
|
||||
confirmationState = confirmationState?.copy(
|
||||
pendingAction = pendingAction,
|
||||
pendingActions = pendingActions,
|
||||
) ?: StakingStates.ConfirmationState.Empty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -109,6 +109,7 @@ internal class SetButtonsStateTransformer(
|
|||
StakingStep.Confirmation -> getConfirmationButtonText()
|
||||
StakingStep.Validators -> resourceReference(R.string.common_continue)
|
||||
StakingStep.Amount,
|
||||
StakingStep.RestakeValidator,
|
||||
StakingStep.RewardsValidators,
|
||||
-> resourceReference(R.string.common_next)
|
||||
}
|
||||
|
|
@ -120,17 +121,15 @@ internal class SetButtonsStateTransformer(
|
|||
resourceReference(R.string.common_close)
|
||||
} else {
|
||||
when (actionType) {
|
||||
StakingActionCommonType.ENTER -> {
|
||||
StakingActionCommonType.Enter -> {
|
||||
if (confirmationState.isApprovalNeeded) {
|
||||
resourceReference(R.string.give_permission_title)
|
||||
} else {
|
||||
resourceReference(R.string.common_stake)
|
||||
}
|
||||
}
|
||||
StakingActionCommonType.EXIT -> resourceReference(R.string.common_unstake)
|
||||
StakingActionCommonType.PENDING_OTHER,
|
||||
StakingActionCommonType.PENDING_REWARDS,
|
||||
-> confirmationState.pendingAction?.type.getPendingActionTitle()
|
||||
StakingActionCommonType.Exit -> resourceReference(R.string.common_unstake)
|
||||
is StakingActionCommonType.Pending -> confirmationState.pendingAction?.type.getPendingActionTitle()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -140,18 +139,11 @@ internal class SetButtonsStateTransformer(
|
|||
|
||||
private fun StakingUiState.onPrimaryClick() {
|
||||
when (currentStep) {
|
||||
StakingStep.InitialInfo -> clickIntents.onEnterClick()
|
||||
StakingStep.Validators -> {
|
||||
if (confirmationState is StakingStates.ConfirmationState.Data) {
|
||||
clickIntents.onNextClick(
|
||||
pendingActions = confirmationState.pendingActions,
|
||||
pendingAction = confirmationState.pendingAction,
|
||||
)
|
||||
} else {
|
||||
clickIntents.onNextClick()
|
||||
}
|
||||
}
|
||||
StakingStep.Amount -> clickIntents.onNextClick()
|
||||
StakingStep.InitialInfo -> clickIntents.onNextClick()
|
||||
StakingStep.Validators,
|
||||
StakingStep.RestakeValidator,
|
||||
-> clickIntents.onNextClick()
|
||||
StakingStep.Amount -> clickIntents.onEnterClick()
|
||||
StakingStep.Confirmation -> onConfirmationClick()
|
||||
StakingStep.RewardsValidators -> Unit
|
||||
}
|
||||
|
|
@ -162,7 +154,7 @@ internal class SetButtonsStateTransformer(
|
|||
if (confirmationState.innerState == InnerConfirmationStakingState.COMPLETED) {
|
||||
clickIntents.onNextClick()
|
||||
} else {
|
||||
val isEnterAction = actionType == StakingActionCommonType.ENTER
|
||||
val isEnterAction = actionType == StakingActionCommonType.Enter
|
||||
val isApproveNeeded = confirmationState.isApprovalNeeded
|
||||
|
||||
if (isEnterAction && isApproveNeeded) {
|
||||
|
|
@ -179,6 +171,7 @@ internal class SetButtonsStateTransformer(
|
|||
private fun StakingStep.isPrevButtonVisible(): Boolean = when (this) {
|
||||
StakingStep.InitialInfo,
|
||||
StakingStep.RewardsValidators,
|
||||
StakingStep.RestakeValidator,
|
||||
StakingStep.Confirmation,
|
||||
StakingStep.Validators,
|
||||
-> false
|
||||
|
|
@ -192,7 +185,9 @@ internal class SetButtonsStateTransformer(
|
|||
StakingStep.Amount -> amountState.isPrimaryButtonEnabled
|
||||
StakingStep.Confirmation -> confirmationState.isPrimaryButtonEnabled
|
||||
StakingStep.RewardsValidators -> rewardsValidatorsState.isPrimaryButtonEnabled
|
||||
StakingStep.Validators -> true
|
||||
StakingStep.RestakeValidator,
|
||||
StakingStep.Validators,
|
||||
-> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,22 +2,17 @@ package com.tangem.features.staking.impl.presentation.state.transformers
|
|||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.FeeState
|
||||
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.utils.Provider
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal class SetConfirmationStateAssentTransformer(
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||
private val fee: Fee,
|
||||
private val action: PendingAction?,
|
||||
private val actions: ImmutableList<PendingAction>?,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
|
|
@ -30,7 +25,6 @@ internal class SetConfirmationStateAssentTransformer(
|
|||
if (this is StakingStates.ConfirmationState.Data) {
|
||||
val isFeeConvertibleToFiat = feeCryptoCurrencyStatus?.currency?.network?.hasFiatFeeRate == true
|
||||
return copy(
|
||||
innerState = InnerConfirmationStakingState.ASSENT,
|
||||
feeState = FeeState.Content(
|
||||
fee = fee,
|
||||
rate = feeCryptoCurrencyStatus?.value?.fiatRate,
|
||||
|
|
@ -38,11 +32,7 @@ internal class SetConfirmationStateAssentTransformer(
|
|||
appCurrency = appCurrencyProvider(),
|
||||
isFeeApproximate = false,
|
||||
),
|
||||
validatorState = validatorState.copySealed(isClickable = true),
|
||||
pendingAction = action,
|
||||
pendingActions = actions,
|
||||
isPrimaryButtonEnabled = true,
|
||||
isApprovalNeeded = false,
|
||||
)
|
||||
} else {
|
||||
return this
|
||||
|
|
|
|||
|
|
@ -16,11 +16,10 @@ internal class SetConfirmationStateCompletedTransformer(
|
|||
}
|
||||
|
||||
private fun StakingStates.ConfirmationState.copyWrapped(): StakingStates.ConfirmationState {
|
||||
if (this is StakingStates.ConfirmationState.Data) {
|
||||
return copy(
|
||||
return if (this is StakingStates.ConfirmationState.Data) {
|
||||
copy(
|
||||
isPrimaryButtonEnabled = true,
|
||||
innerState = InnerConfirmationStakingState.COMPLETED,
|
||||
validatorState = validatorState.copySealed(isClickable = false),
|
||||
footerText = TextReference.EMPTY,
|
||||
notifications = persistentListOf(),
|
||||
transactionDoneState = TransactionDoneState.Content(
|
||||
|
|
@ -29,7 +28,7 @@ internal class SetConfirmationStateCompletedTransformer(
|
|||
),
|
||||
)
|
||||
} else {
|
||||
return this
|
||||
this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal object SetConfirmationStateEmptyTransformer : Transformer<StakingUiState> {
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
return prevState.copy(
|
||||
actionType = StakingActionCommonType.Enter,
|
||||
validatorState = StakingStates.ValidatorState.Empty(),
|
||||
confirmationState = StakingStates.ConfirmationState.Empty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ internal class SetConfirmationStateInProgressTransformer : Transformer<StakingUi
|
|||
copy(
|
||||
isPrimaryButtonEnabled = false,
|
||||
innerState = InnerConfirmationStakingState.IN_PROGRESS,
|
||||
validatorState = validatorState.copySealed(isClickable = false),
|
||||
footerText = TextReference.EMPTY,
|
||||
)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.staking.model.StakingApproval
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
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.isTronStakedBalance
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal class SetConfirmationStateInitTransformer(
|
||||
private val isEnter: Boolean,
|
||||
private val stakingApproval: StakingApproval,
|
||||
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)
|
||||
|
||||
private val isTronStakedBalance
|
||||
get() = isTronStakedBalance(networkId, pendingAction)
|
||||
|
||||
private val isExit: Boolean
|
||||
get() = pendingAction == null && pendingActions?.isEmpty() == true || isTronStakedBalance
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val actionType = when {
|
||||
isEnter -> StakingActionCommonType.Enter
|
||||
isExit -> StakingActionCommonType.Exit
|
||||
else -> when (pendingAction?.type) {
|
||||
StakingActionType.STAKE -> StakingActionCommonType.Enter
|
||||
StakingActionType.UNSTAKE -> StakingActionCommonType.Exit
|
||||
StakingActionType.CLAIM_REWARDS,
|
||||
StakingActionType.RESTAKE_REWARDS,
|
||||
-> StakingActionCommonType.Pending.Rewards
|
||||
StakingActionType.VOTE_LOCKED,
|
||||
StakingActionType.RESTAKE,
|
||||
-> StakingActionCommonType.Pending.Restake
|
||||
else -> StakingActionCommonType.Pending.Other
|
||||
}
|
||||
}
|
||||
|
||||
return prevState.copy(
|
||||
actionType = actionType,
|
||||
confirmationState = StakingStates.ConfirmationState.Data(
|
||||
isPrimaryButtonEnabled = false,
|
||||
innerState = InnerConfirmationStakingState.ASSENT,
|
||||
feeState = FeeState.Loading,
|
||||
notifications = persistentListOf(),
|
||||
footerText = TextReference.EMPTY,
|
||||
transactionDoneState = TransactionDoneState.Empty,
|
||||
isApprovalNeeded = stakingApproval is StakingApproval.Needed,
|
||||
reduceAmountBy = null,
|
||||
pendingAction = pendingAction,
|
||||
pendingActions = pendingActions.takeIf { isComposePendingActions },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ import com.tangem.features.staking.impl.R
|
|||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.features.staking.impl.presentation.state.utils.getRewardScheduleText
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal class SetConfirmationStateLoadingTransformer(
|
||||
private val yield: Yield,
|
||||
|
|
@ -22,39 +21,21 @@ 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 ?: filteredValidators[0]
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = StakingStates.ConfirmationState.Data(
|
||||
confirmationState = possibleConfirmationState?.copy(
|
||||
isPrimaryButtonEnabled = false,
|
||||
innerState = InnerConfirmationStakingState.ASSENT,
|
||||
feeState = FeeState.Loading,
|
||||
validatorState = ValidatorState.Content(
|
||||
isClickable = true,
|
||||
chosenValidator = chosenValidator,
|
||||
availableValidators = filteredValidators,
|
||||
),
|
||||
notifications = persistentListOf(),
|
||||
footerText = getFooter(prevState),
|
||||
transactionDoneState = TransactionDoneState.Empty,
|
||||
pendingAction = possibleConfirmationState?.pendingAction,
|
||||
pendingActions = possibleConfirmationState?.pendingActions,
|
||||
isApprovalNeeded = false,
|
||||
reduceAmountBy = null,
|
||||
possiblePendingTransaction = null,
|
||||
),
|
||||
) ?: prevState.confirmationState,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getFooter(state: StakingUiState): TextReference {
|
||||
val amountState = state.amountState as? AmountState.Data
|
||||
|
||||
val isEnterAction = state.actionType == StakingActionCommonType.ENTER
|
||||
val isEnterAction = state.actionType == StakingActionCommonType.Enter
|
||||
|
||||
val amountDecimal = amountState?.amountTextField?.fiatAmount?.value
|
||||
val amountValue = BigDecimalFormatter.formatFiatAmount(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ internal object SetConfirmationStateResetAssentTransformer : Transformer<Staking
|
|||
confirmationState.copy(
|
||||
isPrimaryButtonEnabled = true,
|
||||
innerState = InnerConfirmationStakingState.ASSENT,
|
||||
validatorState = confirmationState.validatorState.copySealed(isClickable = true),
|
||||
)
|
||||
} else {
|
||||
confirmationState
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ import com.tangem.common.ui.amountScreen.models.AmountState
|
|||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.list.RoundedListWithDividersItemData
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.percent
|
||||
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.staking.model.stakekit.BalanceItem
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
|
|
@ -25,7 +27,6 @@ import com.tangem.utils.Provider
|
|||
import com.tangem.utils.isNullOrZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -33,7 +34,6 @@ import java.math.BigDecimal
|
|||
internal class SetInitialDataStateTransformer(
|
||||
private val clickIntents: StakingClickIntents,
|
||||
private val yield: Yield,
|
||||
private val isApprovalNeeded: Boolean,
|
||||
private val isAnyTokenStaked: Boolean,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val userWalletProvider: Provider<UserWallet>,
|
||||
|
|
@ -76,7 +76,7 @@ internal class SetInitialDataStateTransformer(
|
|||
currentStep = StakingStep.InitialInfo,
|
||||
initialInfoState = createInitialInfoState(),
|
||||
amountState = createInitialAmountState(),
|
||||
confirmationState = createInitialConfirmationState(),
|
||||
confirmationState = StakingStates.ConfirmationState.Empty(),
|
||||
rewardsValidatorsState = rewardsValidatorStateConverter.convert(Unit),
|
||||
bottomSheetConfig = null,
|
||||
)
|
||||
|
|
@ -87,7 +87,7 @@ internal class SetInitialDataStateTransformer(
|
|||
return StakingStates.InitialInfoState.Data(
|
||||
isPrimaryButtonEnabled = !cryptoCurrencyStatusProvider().value.amount.isNullOrZero(),
|
||||
showBanner = !isAnyTokenStaked && yieldBalance == InnerYieldBalanceState.Empty,
|
||||
aprRange = getAprRange(yield.validators),
|
||||
aprRange = getAprRange(yield.preferredValidators),
|
||||
infoItems = getInfoItems(),
|
||||
onInfoClick = clickIntents::onInfoClick,
|
||||
yieldBalance = yieldBalance,
|
||||
|
|
@ -113,7 +113,7 @@ internal class SetInitialDataStateTransformer(
|
|||
}
|
||||
|
||||
private fun createAnnualPercentageRateItem(): RoundedListWithDividersItemData {
|
||||
val validators = yield.validators
|
||||
val validators = yield.preferredValidators
|
||||
return RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_annual_percentage_rate,
|
||||
startText = TextReference.Res(R.string.staking_details_annual_percentage_rate),
|
||||
|
|
@ -128,11 +128,7 @@ internal class SetInitialDataStateTransformer(
|
|||
id = R.string.staking_details_available,
|
||||
startText = TextReference.Res(R.string.staking_details_available),
|
||||
endText = TextReference.Str(
|
||||
value = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = cryptoCurrencyStatus.value.amount,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency.symbol,
|
||||
decimals = cryptoCurrencyStatus.currency.decimals,
|
||||
),
|
||||
value = cryptoCurrencyStatus.value.amount.format { crypto(cryptoCurrencyStatus.currency) },
|
||||
),
|
||||
isEndTextHideable = true,
|
||||
)
|
||||
|
|
@ -158,11 +154,7 @@ internal class SetInitialDataStateTransformer(
|
|||
val minimumCryptoAmount = yield.args.enter.args[Yield.Args.ArgType.AMOUNT]?.minimum ?: return null
|
||||
if (!isPolkadot(cryptoCurrencyStatus.currency.network.id.value)) return null
|
||||
|
||||
val formattedAmount = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = minimumCryptoAmount,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency.symbol,
|
||||
decimals = cryptoCurrencyStatus.currency.decimals,
|
||||
)
|
||||
val formattedAmount = minimumCryptoAmount.format { crypto(cryptoCurrencyStatus.currency) }
|
||||
|
||||
return RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_minimum_requirement,
|
||||
|
|
@ -218,23 +210,6 @@ internal class SetInitialDataStateTransformer(
|
|||
return amountStateConverter.convert("")
|
||||
}
|
||||
|
||||
private fun createInitialConfirmationState(): StakingStates.ConfirmationState {
|
||||
return StakingStates.ConfirmationState.Data(
|
||||
isPrimaryButtonEnabled = false,
|
||||
innerState = InnerConfirmationStakingState.ASSENT,
|
||||
feeState = FeeState.Loading,
|
||||
validatorState = ValidatorState.Loading,
|
||||
notifications = persistentListOf(),
|
||||
footerText = TextReference.EMPTY,
|
||||
transactionDoneState = TransactionDoneState.Empty,
|
||||
pendingAction = null,
|
||||
pendingActions = null,
|
||||
isApprovalNeeded = isApprovalNeeded,
|
||||
reduceAmountBy = null,
|
||||
possiblePendingTransaction = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getAprRange(validators: List<Yield.Validator>): TextReference {
|
||||
val aprValues = validators
|
||||
.filter { it.preferred }
|
||||
|
|
@ -243,14 +218,8 @@ internal class SetInitialDataStateTransformer(
|
|||
val minApr = aprValues.min()
|
||||
val maxApr = aprValues.max()
|
||||
|
||||
val formattedMinApr = BigDecimalFormatter.formatPercent(
|
||||
percent = minApr,
|
||||
useAbsoluteValue = true,
|
||||
).remove("%")
|
||||
val formattedMaxApr = BigDecimalFormatter.formatPercent(
|
||||
percent = maxApr,
|
||||
useAbsoluteValue = true,
|
||||
)
|
||||
val formattedMinApr = minApr.format { percent() }.remove("%")
|
||||
val formattedMaxApr = maxApr.format { percent() }
|
||||
|
||||
if (maxApr - minApr < EQUALITY_THRESHOLD) {
|
||||
return stringReference("$formattedMinApr%")
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.domain.staking.model.PendingTransaction
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SetPossiblePendingTransactionTransformer(
|
||||
private val yield: Yield,
|
||||
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 yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
|
||||
val balancesId = yieldBalance?.getBalancesUniqueId() ?: 0
|
||||
val token = yield.getCurrentToken(cryptoCurrencyStatus.currency.id.rawCurrencyId)
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = possibleConfirmationState.copy(
|
||||
possiblePendingTransaction = PendingTransaction(
|
||||
groupId = balanceState.groupId,
|
||||
token = token,
|
||||
type = balanceState.type,
|
||||
amount = balanceState.cryptoDecimal,
|
||||
rawCurrencyId = balanceState.rawCurrencyId,
|
||||
validator = balanceState.validator,
|
||||
balancesId = balancesId,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,9 @@ internal object SetTitleTransformer : Transformer<StakingUiState> {
|
|||
|
||||
val title = when (currentStep) {
|
||||
StakingStep.Amount -> resourceReference(R.string.send_amount_label)
|
||||
StakingStep.Validators -> resourceReference(R.string.staking_validators)
|
||||
StakingStep.RestakeValidator,
|
||||
StakingStep.Validators,
|
||||
-> resourceReference(R.string.staking_validators)
|
||||
StakingStep.RewardsValidators -> resourceReference(R.string.common_claim_rewards)
|
||||
StakingStep.InitialInfo -> resourceReference(
|
||||
R.string.staking_title_stake,
|
||||
|
|
@ -29,11 +31,11 @@ internal object SetTitleTransformer : Transformer<StakingUiState> {
|
|||
|
||||
StakingStep.Confirmation -> {
|
||||
when (actionType) {
|
||||
StakingActionCommonType.ENTER -> resourceReference(
|
||||
StakingActionCommonType.Enter -> resourceReference(
|
||||
R.string.staking_title_stake,
|
||||
wrappedList(prevState.cryptoCurrencyName),
|
||||
)
|
||||
StakingActionCommonType.EXIT -> resourceReference(
|
||||
StakingActionCommonType.Exit -> resourceReference(
|
||||
R.string.staking_title_unstake,
|
||||
wrappedList(prevState.cryptoCurrencyName),
|
||||
)
|
||||
|
|
@ -51,7 +53,7 @@ internal object SetTitleTransformer : Transformer<StakingUiState> {
|
|||
}
|
||||
}
|
||||
|
||||
val subtitle = if (currentStep == StakingStep.Confirmation && actionType == StakingActionCommonType.ENTER) {
|
||||
val subtitle = if (currentStep == StakingStep.Confirmation && actionType == StakingActionCommonType.Enter) {
|
||||
stringReference(prevState.walletName)
|
||||
} else {
|
||||
null
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.isNullOrEmpty
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.utils.parseBigDecimal
|
||||
import com.tangem.domain.staking.model.stakekit.AddressArgument
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
|
|
@ -55,11 +56,9 @@ internal class AmountRequirementStateTransformer(
|
|||
isRequirementError -> resourceReference(
|
||||
R.string.staking_amount_requirement_error,
|
||||
wrappedList(
|
||||
BigDecimalFormatter.formatCryptoAmount(
|
||||
amountRequirements.minimum,
|
||||
cryptoCurrencyStatus.currency.symbol,
|
||||
cryptoCurrencyStatus.currency.decimals,
|
||||
),
|
||||
amountRequirements.minimum.format {
|
||||
crypto(cryptoCurrencyStatus.currency)
|
||||
},
|
||||
),
|
||||
)
|
||||
isIntegerOnlyError -> resourceReference(
|
||||
|
|
@ -102,7 +101,7 @@ 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 isEnter = actionType == StakingActionCommonType.Enter
|
||||
val isTron = isTron(cryptoCurrencyStatus.currency.network.id.value)
|
||||
|
||||
val isIntegerOnly = cryptoAmountValue.isZero() || cryptoAmountValue.remainder(BigDecimal.ONE).isZero()
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ internal class SetConfirmationStateAssentApprovalTransformer(
|
|||
appCurrency = appCurrencyProvider(),
|
||||
isFeeApproximate = false,
|
||||
),
|
||||
validatorState = validatorState.copySealed(isClickable = true),
|
||||
isPrimaryButtonEnabled = true,
|
||||
isApprovalNeeded = true,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import com.tangem.common.ui.bottomsheet.permission.state.*
|
|||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
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.format
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -12,7 +14,6 @@ import com.tangem.features.staking.impl.R
|
|||
import com.tangem.features.staking.impl.presentation.state.FeeState
|
||||
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.Provider
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
|
|
@ -28,17 +29,15 @@ internal class ShowApprovalBottomSheetTransformer(
|
|||
|
||||
val amountState = prevState.amountState as? AmountState.Data ?: return prevState
|
||||
val confirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState
|
||||
val validatorState = confirmationState.validatorState as? ValidatorState.Content ?: return prevState
|
||||
val validatorState = prevState.validatorState as? StakingStates.ValidatorState.Data ?: return prevState
|
||||
val feeState = confirmationState.feeState as? FeeState.Content ?: return prevState
|
||||
val fee = feeState.fee ?: return prevState
|
||||
|
||||
val walletAddress = cryptoCurrencyValue.networkAddress?.defaultAddress?.value.orEmpty()
|
||||
val validatorAddress = validatorState.chosenValidator.address
|
||||
val feeCryptoValue = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = fee.amount.value,
|
||||
cryptoCurrency = fee.amount.currencySymbol,
|
||||
decimals = fee.amount.decimals,
|
||||
)
|
||||
val feeCryptoValue = fee.amount.value.format {
|
||||
crypto(fee.amount.currencySymbol, fee.amount.decimals)
|
||||
}
|
||||
val feeFiatValue = BigDecimalFormatter.formatFiatAmount(
|
||||
fiatAmount = feeCryptoCurrencyStatus?.value?.fiatRate?.multiply(fee.amount.value),
|
||||
fiatCurrencyCode = appCurrencyProvider().code,
|
||||
|
|
|
|||
|
|
@ -1,29 +1,45 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers.validator
|
||||
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStep
|
||||
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 class ValidatorSelectChangeTransformer(
|
||||
private val yield: Yield,
|
||||
private val selectedValidator: Yield.Validator?,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val confirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState
|
||||
selectedValidator ?: return prevState
|
||||
val validatorState = (confirmationState.validatorState as? ValidatorState.Content)?.copy(
|
||||
chosenValidator = selectedValidator,
|
||||
) ?: ValidatorState.Content(
|
||||
isClickable = false,
|
||||
availableValidators = emptyList(),
|
||||
chosenValidator = selectedValidator,
|
||||
)
|
||||
val validatorState = prevState.validatorState as? StakingStates.ValidatorState.Data
|
||||
val confirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
|
||||
val isRestake = prevState.actionType == StakingActionCommonType.Pending.Restake
|
||||
val isEnter = prevState.actionType == StakingActionCommonType.Enter
|
||||
val isFromInfoScreen = prevState.currentStep == StakingStep.InitialInfo
|
||||
val isVoteLocked = confirmationState?.pendingAction?.type == StakingActionType.VOTE_LOCKED
|
||||
|
||||
val activeValidator = selectedValidator.takeIf { isFromInfoScreen && isRestake }
|
||||
?: validatorState?.activeValidator
|
||||
val filteredValidators = yield.preferredValidators.filterNot { it == activeValidator }
|
||||
|
||||
val selectedValidator = if (isRestake && isFromInfoScreen) {
|
||||
filteredValidators.firstOrNull()
|
||||
} else {
|
||||
selectedValidator
|
||||
}
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = confirmationState.copy(
|
||||
validatorState = validatorState,
|
||||
validatorState = StakingStates.ValidatorState.Data(
|
||||
chosenValidator = selectedValidator ?: yield.preferredValidators.first(),
|
||||
availableValidators = filteredValidators,
|
||||
isPrimaryButtonEnabled = true,
|
||||
isClickable = true,
|
||||
activeValidator = activeValidator,
|
||||
isVisibleOnConfirmation = isEnter || isRestake || isVoteLocked,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,12 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isBSC
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isSolana
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isTron
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
internal fun StakingActionType?.getPendingActionTitle(): TextReference = when (this) {
|
||||
|
|
@ -29,8 +33,35 @@ internal fun StakingActionType?.getPendingActionTitle(): TextReference = when (t
|
|||
null -> TextReference.EMPTY
|
||||
}
|
||||
|
||||
internal fun isSolanaWithdraw(networkId: String, pendingActions: ImmutableList<PendingAction>?): Boolean {
|
||||
val isSolana = isSolana(networkId)
|
||||
val isWithdraw = pendingActions?.all { it.type == StakingActionType.WITHDRAW } == true
|
||||
return isSolana && isWithdraw && !pendingActions.isNullOrEmpty()
|
||||
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 isBscRestake = isBSC(networkId) && activeStake.pendingActions.any {
|
||||
it.type == StakingActionType.RESTAKE
|
||||
}
|
||||
|
||||
return isSingleAction && !isBscRestake || isComposePendingActions
|
||||
}
|
||||
|
||||
internal fun withStubUnstakeAction(networkId: String, activeStake: BalanceState) = if (isBSC(networkId)) {
|
||||
activeStake.pendingActions.plus(
|
||||
PendingAction(
|
||||
type = StakingActionType.UNSTAKE,
|
||||
passthrough = "",
|
||||
args = null,
|
||||
),
|
||||
).toPersistentList()
|
||||
} else {
|
||||
activeStake.pendingActions
|
||||
}
|
||||
|
||||
internal fun isTronStakedBalance(networkId: String, pendingAction: PendingAction?): Boolean {
|
||||
return isTron(networkId) && pendingAction?.type == StakingActionType.REVOTE
|
||||
}
|
||||
|
||||
internal fun isComposePendingActions(networkId: String, pendingActions: ImmutableList<PendingAction>?): Boolean {
|
||||
return when {
|
||||
isSolana(networkId) -> pendingActions?.any { it.type == StakingActionType.WITHDRAW } == true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
|
@ -12,8 +12,9 @@ import androidx.compose.ui.Modifier
|
|||
import com.tangem.core.ui.components.inputrow.InputRowImageInfo
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.percent
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
|
|
@ -41,10 +42,7 @@ internal fun StakingClaimRewardsValidatorContent(
|
|||
annotatedReference {
|
||||
appendSpace()
|
||||
appendColored(
|
||||
text = BigDecimalFormatter.formatPercent(
|
||||
percent = item.validator?.apr.orZero(),
|
||||
useAbsoluteValue = true,
|
||||
),
|
||||
text = item.validator?.apr.orZero().format { percent() },
|
||||
color = TangemTheme.colors.text.accent,
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ 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.StakingNotification
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.TransactionDoneState
|
||||
import com.tangem.features.staking.impl.presentation.state.previewdata.ConfirmationStatePreviewData
|
||||
import com.tangem.features.staking.impl.presentation.state.previewdata.ValidatorStatePreviewData
|
||||
import com.tangem.features.staking.impl.presentation.state.stub.StakingClickIntentsStub
|
||||
import com.tangem.features.staking.impl.presentation.ui.block.NotificationsBlock
|
||||
import com.tangem.features.staking.impl.presentation.ui.block.StakingFeeBlock
|
||||
|
|
@ -36,12 +36,12 @@ import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickInten
|
|||
internal fun StakingConfirmationContent(
|
||||
amountState: AmountState,
|
||||
state: StakingStates.ConfirmationState,
|
||||
validatorState: StakingStates.ValidatorState,
|
||||
clickIntents: StakingClickIntents,
|
||||
type: StakingActionCommonType,
|
||||
) {
|
||||
if (state !is StakingStates.ConfirmationState.Data) return
|
||||
val isEnterAction = type == StakingActionCommonType.ENTER
|
||||
val showValidatorBlock = isEnterAction || state.pendingAction?.type == StakingActionType.VOTE_LOCKED
|
||||
val isEnterAction = type == StakingActionCommonType.Enter
|
||||
val isTransactionSent = state.innerState == InnerConfirmationStakingState.COMPLETED
|
||||
val isTransactionInProgress = state.notifications.any { it is StakingNotification.Warning.TransactionInProgress }
|
||||
Column(
|
||||
|
|
@ -67,13 +67,11 @@ internal fun StakingConfirmationContent(
|
|||
isEditingDisabled = !isEnterAction && state.innerState != InnerConfirmationStakingState.COMPLETED,
|
||||
onClick = clickIntents::onPrevClick,
|
||||
)
|
||||
if (showValidatorBlock) {
|
||||
ValidatorBlock(
|
||||
validatorState = state.validatorState,
|
||||
isClickable = !isTransactionInProgress,
|
||||
onClick = clickIntents::openValidators,
|
||||
)
|
||||
}
|
||||
ValidatorBlock(
|
||||
validatorState = validatorState,
|
||||
isClickable = !isTransactionInProgress,
|
||||
onClick = clickIntents::openValidators,
|
||||
)
|
||||
StakingFeeBlock(feeState = state.feeState, isTransactionSent = isTransactionSent)
|
||||
NotificationsBlock(notifications = state.notifications)
|
||||
}
|
||||
|
|
@ -88,8 +86,9 @@ private fun Preview_StakingConfirmationContent() {
|
|||
StakingConfirmationContent(
|
||||
amountState = AmountStatePreviewData.amountState,
|
||||
state = ConfirmationStatePreviewData.assentStakingState,
|
||||
validatorState = ValidatorStatePreviewData.validatorState,
|
||||
clickIntents = StakingClickIntentsStub,
|
||||
type = StakingActionCommonType.ENTER,
|
||||
type = StakingActionCommonType.Enter,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,11 +41,12 @@ import com.tangem.core.ui.components.inputrow.InputRowImageInfo
|
|||
import com.tangem.core.ui.components.list.roundedListWithDividersItems
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.percent
|
||||
import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.staking.model.stakekit.BalanceType
|
||||
import com.tangem.domain.staking.model.stakekit.RewardBlockType
|
||||
import com.tangem.features.staking.impl.R
|
||||
|
|
@ -311,10 +312,7 @@ private fun BalanceState.getAprText() = combinedReference(
|
|||
annotatedReference {
|
||||
appendSpace()
|
||||
appendColored(
|
||||
text = BigDecimalFormatter.formatPercent(
|
||||
percent = validator?.apr.orZero(),
|
||||
useAbsoluteValue = true,
|
||||
),
|
||||
text = validator?.apr.orZero().format { percent() },
|
||||
color = TangemTheme.colors.text.accent,
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.tangem.features.staking.impl.presentation.ui
|
|||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -89,15 +88,12 @@ private fun StakingAppBar(uiState: StakingUiState) {
|
|||
val (backIcon, click) = when (uiState.currentStep) {
|
||||
StakingStep.Amount,
|
||||
StakingStep.Confirmation,
|
||||
-> {
|
||||
R.drawable.ic_close_24 to uiState.clickIntents::onBackClick
|
||||
}
|
||||
-> R.drawable.ic_close_24 to uiState.clickIntents::onBackClick
|
||||
StakingStep.Validators,
|
||||
StakingStep.RewardsValidators,
|
||||
StakingStep.RestakeValidator,
|
||||
StakingStep.InitialInfo,
|
||||
-> {
|
||||
R.drawable.ic_back_24 to uiState.clickIntents::onPrevClick
|
||||
}
|
||||
-> R.drawable.ic_back_24 to uiState.clickIntents::onPrevClick
|
||||
}
|
||||
AppBarWithBackButtonAndIcon(
|
||||
text = uiState.title.resolveReference(),
|
||||
|
|
@ -109,7 +105,6 @@ private fun StakingAppBar(uiState: StakingUiState) {
|
|||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = Modifier) {
|
||||
val currentScreen = uiState.currentStep
|
||||
|
|
@ -172,17 +167,16 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M
|
|||
StakingStep.Confirmation -> StakingConfirmationContent(
|
||||
amountState = uiState.amountState,
|
||||
state = uiState.confirmationState,
|
||||
validatorState = uiState.validatorState,
|
||||
clickIntents = uiState.clickIntents,
|
||||
type = uiState.actionType,
|
||||
)
|
||||
StakingStep.Validators -> {
|
||||
val confirmState = uiState.confirmationState
|
||||
if (confirmState !is StakingStates.ConfirmationState.Data) return@AnimatedContent
|
||||
StakingValidatorListContent(
|
||||
state = confirmState.validatorState,
|
||||
clickIntents = uiState.clickIntents,
|
||||
)
|
||||
}
|
||||
StakingStep.RestakeValidator,
|
||||
StakingStep.Validators,
|
||||
-> StakingValidatorListContent(
|
||||
state = uiState.validatorState,
|
||||
clickIntents = uiState.clickIntents,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,12 +24,13 @@ 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.*
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.percent
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.ValidatorState
|
||||
import com.tangem.features.staking.impl.presentation.state.previewdata.ConfirmationStatePreviewData
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.previewdata.ValidatorStatePreviewData
|
||||
import com.tangem.features.staking.impl.presentation.state.stub.StakingClickIntentsStub
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
import com.tangem.utils.extensions.orZero
|
||||
|
|
@ -39,7 +40,7 @@ import com.tangem.utils.extensions.orZero
|
|||
*/
|
||||
@Composable
|
||||
internal fun StakingValidatorListContent(
|
||||
state: ValidatorState,
|
||||
state: StakingStates.ValidatorState,
|
||||
clickIntents: StakingClickIntents,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -51,7 +52,7 @@ internal fun StakingValidatorListContent(
|
|||
.background(TangemTheme.colors.background.secondary)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
if (state is ValidatorState.Content) {
|
||||
if (state is StakingStates.ValidatorState.Data) {
|
||||
val validators = state.availableValidators
|
||||
items(
|
||||
count = validators.size,
|
||||
|
|
@ -67,7 +68,7 @@ internal fun StakingValidatorListContent(
|
|||
annotatedReference {
|
||||
appendSpace()
|
||||
appendColored(
|
||||
text = BigDecimalFormatter.formatPercent(item.apr.orZero(), true),
|
||||
text = item.apr.orZero().format { percent() },
|
||||
color = TangemTheme.colors.text.accent,
|
||||
)
|
||||
},
|
||||
|
|
@ -131,7 +132,7 @@ private fun RowScope.ValidatorLabel(isStrategicPartner: Boolean) {
|
|||
@Composable
|
||||
private fun StakingValidatorListContent_Preview(
|
||||
@PreviewParameter(StakingValidatorListContentPreviewProvider::class)
|
||||
data: ValidatorState,
|
||||
data: StakingStates.ValidatorState,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
StakingValidatorListContent(
|
||||
|
|
@ -141,8 +142,8 @@ private fun StakingValidatorListContent_Preview(
|
|||
}
|
||||
}
|
||||
|
||||
private class StakingValidatorListContentPreviewProvider : PreviewParameterProvider<ValidatorState> {
|
||||
override val values: Sequence<ValidatorState>
|
||||
get() = sequenceOf(ConfirmationStatePreviewData.assentStakingState.validatorState)
|
||||
private class StakingValidatorListContentPreviewProvider : PreviewParameterProvider<StakingStates.ValidatorState> {
|
||||
override val values: Sequence<StakingStates.ValidatorState>
|
||||
get() = sequenceOf(ValidatorStatePreviewData.validatorState)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -21,6 +21,9 @@ import com.tangem.common.ui.amountScreen.utils.getFiatReference
|
|||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.rows.SelectorRowItem
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
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.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
|
|
@ -56,12 +59,12 @@ internal fun StakingFeeBlock(feeState: FeeState, isTransactionSent: Boolean) {
|
|||
titleRes = R.string.common_fee_selector_option_market,
|
||||
iconRes = R.drawable.ic_bird_24,
|
||||
preDot = stringReference(
|
||||
BigDecimalFormatter.formatCryptoFeeAmount(
|
||||
cryptoAmount = feeAmount?.value,
|
||||
cryptoCurrency = feeAmount?.currencySymbol.orEmpty(),
|
||||
decimals = feeAmount?.decimals ?: 0,
|
||||
canBeLower = feeState.isFeeApproximate,
|
||||
),
|
||||
feeAmount?.value.format {
|
||||
crypto(
|
||||
symbol = feeAmount?.currencySymbol.orEmpty(),
|
||||
decimals = feeAmount?.decimals ?: 0,
|
||||
).fee(canBeLower = feeState.isFeeApproximate)
|
||||
},
|
||||
),
|
||||
postDot = if (feeState.isFeeConvertibleToFiat) {
|
||||
getFiatReference(feeAmount?.value, feeState.rate, feeState.appCurrency)
|
||||
|
|
|
|||
|
|
@ -12,42 +12,44 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import com.tangem.core.ui.components.inputrow.InputRowImageInfo
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.percent
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.ValidatorState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.ui.ValidatorImagePlaceholder
|
||||
import com.tangem.utils.extensions.orZero
|
||||
|
||||
@Composable
|
||||
internal fun ValidatorBlock(validatorState: ValidatorState, isClickable: Boolean, onClick: () -> Unit) {
|
||||
internal fun ValidatorBlock(validatorState: StakingStates.ValidatorState, isClickable: Boolean, onClick: () -> Unit) {
|
||||
val state = validatorState as? StakingStates.ValidatorState.Data ?: return
|
||||
if (!state.isVisibleOnConfirmation) return
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable(
|
||||
enabled = validatorState.isClickable && isClickable,
|
||||
enabled = state.isClickable && isClickable,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(),
|
||||
onClick = onClick,
|
||||
),
|
||||
) {
|
||||
if (validatorState is ValidatorState.Content) {
|
||||
InputRowImageInfo(
|
||||
title = resourceReference(R.string.staking_validator),
|
||||
subtitle = stringReference(validatorState.chosenValidator.name),
|
||||
infoTitle = annotatedReference {
|
||||
append(resourceReference(R.string.staking_details_apr).resolveReference())
|
||||
appendSpace()
|
||||
appendColored(
|
||||
text = BigDecimalFormatter.formatPercent(validatorState.chosenValidator.apr.orZero(), true),
|
||||
color = TangemTheme.colors.text.accent,
|
||||
)
|
||||
},
|
||||
imageUrl = validatorState.chosenValidator.image.orEmpty(),
|
||||
onImageError = { ValidatorImagePlaceholder() },
|
||||
)
|
||||
}
|
||||
InputRowImageInfo(
|
||||
title = resourceReference(R.string.staking_validator),
|
||||
subtitle = stringReference(state.chosenValidator.name),
|
||||
infoTitle = annotatedReference {
|
||||
append(resourceReference(R.string.staking_details_apr).resolveReference())
|
||||
appendSpace()
|
||||
appendColored(
|
||||
text = state.chosenValidator.apr.orZero().format { percent() },
|
||||
color = TangemTheme.colors.text.accent,
|
||||
)
|
||||
},
|
||||
imageUrl = state.chosenValidator.image.orEmpty(),
|
||||
onImageError = { ValidatorImagePlaceholder() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,13 +3,10 @@ package com.tangem.features.staking.impl.presentation.viewmodel
|
|||
import com.tangem.common.ui.amountScreen.AmountScreenClickIntents
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.domain.staking.model.stakekit.PendingAction
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
||||
import com.tangem.features.staking.impl.presentation.state.bottomsheet.InfoType
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
|
|
@ -17,12 +14,7 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
|
|||
|
||||
fun onBackClick()
|
||||
|
||||
fun onNextClick(
|
||||
actionTypeToOverwrite: StakingActionCommonType? = null,
|
||||
pendingAction: PendingAction? = null,
|
||||
pendingActions: ImmutableList<PendingAction>? = null,
|
||||
balanceState: BalanceState? = null,
|
||||
)
|
||||
fun onNextClick(balanceState: BalanceState? = null)
|
||||
|
||||
fun onActionClick()
|
||||
|
||||
|
|
@ -36,9 +28,9 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
|
|||
|
||||
fun onEnterClick()
|
||||
|
||||
fun getFee(pendingAction: PendingAction?, pendingActions: ImmutableList<PendingAction>?)
|
||||
fun getFee()
|
||||
|
||||
override fun onAmountNext() = onNextClick(actionTypeToOverwrite = null)
|
||||
override fun onAmountNext() = onNextClick()
|
||||
|
||||
fun openValidators()
|
||||
|
||||
|
|
|
|||
|
|
@ -26,14 +26,13 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
|
|||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
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.*
|
||||
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
|
||||
import com.tangem.domain.tokens.*
|
||||
|
|
@ -65,12 +64,14 @@ import com.tangem.features.staking.impl.presentation.state.transformers.*
|
|||
import com.tangem.features.staking.impl.presentation.state.transformers.amount.*
|
||||
import com.tangem.features.staking.impl.presentation.state.transformers.approval.*
|
||||
import com.tangem.features.staking.impl.presentation.state.transformers.validator.ValidatorSelectChangeTransformer
|
||||
import com.tangem.features.staking.impl.presentation.state.utils.isSolanaWithdraw
|
||||
import com.tangem.features.staking.impl.presentation.state.utils.isSingleAction
|
||||
import com.tangem.features.staking.impl.presentation.state.utils.withStubUnstakeAction
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.*
|
||||
import com.tangem.utils.extensions.isSingleItem
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -108,6 +109,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
private val stakingBalanceUpdater: StakingBalanceUpdater.Factory,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val getActionsUseCase: GetActionsUseCase,
|
||||
private val paramsInterceptorHolder: ParamsInterceptorHolder,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
|
|
@ -132,6 +134,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
?: error("This screen can't be opened without `Yield`")
|
||||
|
||||
private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
private var processingActions: List<StakingAction> = emptyList()
|
||||
private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||
|
||||
private var innerRouter: InnerStakingRouter by Delegates.notNull()
|
||||
|
|
@ -142,9 +145,8 @@ internal class StakingViewModel @Inject constructor(
|
|||
get() {
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
return invalidatePendingTransactionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
balanceItems = yieldBalance?.balance?.items ?: emptyList(),
|
||||
balancesId = yieldBalance?.getBalancesUniqueId() ?: 0,
|
||||
processingActions = processingActions,
|
||||
).getOrElse { emptyList() }
|
||||
}
|
||||
|
||||
|
|
@ -154,6 +156,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
stakingBalanceUpdater.create(
|
||||
cryptoCurrencyStatus,
|
||||
userWallet,
|
||||
yield,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -188,6 +191,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
|
||||
private var stakingApproval: StakingApproval = StakingApproval.Empty
|
||||
private var isAmountSubtractAvailable: Boolean = false
|
||||
private var isAnyTokenStaked: Boolean = false
|
||||
private val allowanceTaskScheduler = SingleTaskScheduler<BigDecimal>()
|
||||
|
||||
private val transactionsInProgress: CopyOnWriteArrayList<StakingTransaction> = CopyOnWriteArrayList()
|
||||
|
|
@ -195,6 +199,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
private var approvalJobHolder: JobHolder = JobHolder()
|
||||
private var feeJobHolder: JobHolder = JobHolder()
|
||||
private var sendTransactionJobHolder = JobHolder()
|
||||
private var stepChangesJobHolder = JobHolder()
|
||||
|
||||
init {
|
||||
subscribeOnSelectedAppCurrency()
|
||||
|
|
@ -208,76 +213,18 @@ internal class StakingViewModel @Inject constructor(
|
|||
approvalJobHolder.cancel()
|
||||
feeJobHolder.cancel()
|
||||
sendTransactionJobHolder.cancel()
|
||||
stepChangesJobHolder.cancel()
|
||||
}
|
||||
|
||||
override fun onBackClick() {
|
||||
stakingStateRouter.onBackClick()
|
||||
}
|
||||
|
||||
override fun onNextClick(
|
||||
actionTypeToOverwrite: StakingActionCommonType?,
|
||||
pendingAction: PendingAction?,
|
||||
pendingActions: ImmutableList<PendingAction>?,
|
||||
balanceState: BalanceState?,
|
||||
) {
|
||||
if (actionTypeToOverwrite != null) {
|
||||
stateController.update(SetActionToExecuteTransformer(actionTypeToOverwrite, pendingAction, pendingActions))
|
||||
}
|
||||
override fun onNextClick(balanceState: BalanceState?) {
|
||||
stakingStateRouter.onNextClick()
|
||||
when {
|
||||
isInitState() -> {
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stateController.update(
|
||||
SetConfirmationStateLoadingTransformer(
|
||||
yield = yield,
|
||||
appCurrency = appCurrency,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
),
|
||||
)
|
||||
if (balanceState != null) {
|
||||
stateController.update(
|
||||
SetPossiblePendingTransactionTransformer(yield, 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)
|
||||
if (balanceState != null) {
|
||||
stateController.update(
|
||||
SetPossiblePendingTransactionTransformer(yield, balanceState, cryptoCurrencyStatus),
|
||||
)
|
||||
}
|
||||
val amountState = value.amountState as? AmountState.Data
|
||||
if (amountState?.amountTextField?.isWarning == true) {
|
||||
stateController.update(
|
||||
AmountRoundToIntegerTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActionClick() {
|
||||
handleOnNextConfirmationClick()
|
||||
}
|
||||
|
||||
override fun getFee(pendingAction: PendingAction?, pendingActions: ImmutableList<PendingAction>?) {
|
||||
override fun getFee() {
|
||||
stateController.update(
|
||||
SetConfirmationStateLoadingTransformer(
|
||||
yield = yield,
|
||||
|
|
@ -287,16 +234,12 @@ internal class StakingViewModel @Inject constructor(
|
|||
)
|
||||
viewModelScope.launch {
|
||||
feeLoader.getFee(
|
||||
pendingAction = pendingAction,
|
||||
pendingActions = pendingActions,
|
||||
onStakingFee = { gasEstimate ->
|
||||
stateController.update(
|
||||
SetConfirmationStateAssentTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
fee = gasEstimate,
|
||||
action = pendingAction,
|
||||
actions = pendingActions,
|
||||
),
|
||||
)
|
||||
updateNotifications()
|
||||
|
|
@ -324,7 +267,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
}.saveIn(feeJobHolder)
|
||||
}
|
||||
|
||||
private fun handleOnNextConfirmationClick() {
|
||||
override fun onActionClick() {
|
||||
if (isAssentState()) {
|
||||
viewModelScope.launch {
|
||||
stakingAnalyticSender.sendTransactionStakingClickedAnalytics(value)
|
||||
|
|
@ -343,7 +286,6 @@ internal class StakingViewModel @Inject constructor(
|
|||
stateController.update(SetConfirmationStateCompletedTransformer(txUrl))
|
||||
},
|
||||
onSendError = { error ->
|
||||
Timber.e(error.toString())
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.TransactionError)
|
||||
stakingEventFactory.createSendTransactionErrorAlert(error)
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
|
|
@ -383,7 +325,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
override fun onRefreshSwipe(isRefreshing: Boolean) {
|
||||
stateController.update(SetInitialLoadingStateTransformer(isRefreshing))
|
||||
coroutineScope.launch {
|
||||
balanceUpdater.instantUpdate()
|
||||
balanceUpdater.partialUpdate()
|
||||
}.invokeOnCompletion {
|
||||
stateController.update(SetInitialLoadingStateTransformer(false))
|
||||
}
|
||||
|
|
@ -403,28 +345,23 @@ internal class StakingViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onEnterClick() {
|
||||
onAmountValueChange("") // reset amount state
|
||||
|
||||
// TODO refactor in [REDACTED_JIRA]
|
||||
val confirmationState = value.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val filteredValidator = yield.validators.filter { it.preferred }
|
||||
if (filteredValidator.isEmpty()) {
|
||||
if (yield.preferredValidators.isEmpty()) {
|
||||
stateController.updateEvent(
|
||||
StakingEvent.ShowAlert(StakingAlertUM.NoAvailableValidators),
|
||||
)
|
||||
} else {
|
||||
stateController.update {
|
||||
value.copy(
|
||||
confirmationState = confirmationState?.copy(
|
||||
validatorState = ValidatorState.Content(
|
||||
isClickable = true,
|
||||
chosenValidator = filteredValidator[0],
|
||||
availableValidators = filteredValidator,
|
||||
),
|
||||
) as StakingStates.ConfirmationState,
|
||||
)
|
||||
}
|
||||
onNextClick(StakingActionCommonType.ENTER)
|
||||
stateController.updateAll(
|
||||
SetConfirmationStateInitTransformer(
|
||||
isEnter = true,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
stakingApproval = stakingApproval,
|
||||
),
|
||||
ValidatorSelectChangeTransformer(
|
||||
selectedValidator = null,
|
||||
yield = yield,
|
||||
),
|
||||
)
|
||||
onNextClick()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -463,7 +400,12 @@ internal class StakingViewModel @Inject constructor(
|
|||
validator = validator.name,
|
||||
),
|
||||
)
|
||||
stateController.update(ValidatorSelectChangeTransformer(validator))
|
||||
stateController.update(
|
||||
ValidatorSelectChangeTransformer(
|
||||
selectedValidator = validator,
|
||||
yield = yield,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun openRewardsValidators() {
|
||||
|
|
@ -479,50 +421,38 @@ internal class StakingViewModel @Inject constructor(
|
|||
source = StakeScreenSource.Info,
|
||||
),
|
||||
)
|
||||
onNextClick(actionTypeToOverwrite = StakingActionCommonType.PENDING_REWARDS)
|
||||
stateController.update {
|
||||
value.copy(actionType = StakingActionCommonType.Pending.Rewards)
|
||||
}
|
||||
stakingStateRouter.showRewardsValidators()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActiveStake(activeStake: BalanceState) {
|
||||
val isAllWithdrawActions = isSolanaWithdraw(
|
||||
cryptoCurrencyStatus.currency.network.id.value,
|
||||
activeStake.pendingActions,
|
||||
)
|
||||
val isMultiplePendingActions = activeStake.pendingActions.size > 1
|
||||
if (isMultiplePendingActions && !isAllWithdrawActions) {
|
||||
val networkId = cryptoCurrencyStatus.currency.network.id.value
|
||||
if (isSingleAction(networkId, activeStake)) {
|
||||
prepareForConfirmation(
|
||||
pendingActions = activeStake.pendingActions,
|
||||
validator = activeStake.validator,
|
||||
amountValue = activeStake.cryptoValue,
|
||||
)
|
||||
onNextClick(balanceState = activeStake)
|
||||
} else {
|
||||
stateController.update(
|
||||
ShowActionSelectorBottomSheetTransformer(
|
||||
pendingActions = activeStake.pendingActions,
|
||||
pendingActions = withStubUnstakeAction(networkId, activeStake),
|
||||
onActionSelect = { action ->
|
||||
stateController.update(ValidatorSelectChangeTransformer(activeStake.validator))
|
||||
stateController.update(
|
||||
AmountChangeStateTransformer(
|
||||
cryptoCurrencyStatus,
|
||||
activeStake.cryptoValue,
|
||||
yield,
|
||||
),
|
||||
)
|
||||
onNextClick(
|
||||
actionTypeToOverwrite = StakingActionCommonType.PENDING_OTHER,
|
||||
prepareForConfirmation(
|
||||
pendingAction = action,
|
||||
balanceState = activeStake,
|
||||
validator = activeStake.validator,
|
||||
amountValue = activeStake.cryptoValue,
|
||||
)
|
||||
stateController.update(DismissBottomSheetStateTransformer)
|
||||
onNextClick(balanceState = activeStake)
|
||||
},
|
||||
onDismiss = { stateController.update(DismissBottomSheetStateTransformer) },
|
||||
),
|
||||
)
|
||||
} else {
|
||||
stateController.update(ActionTypeActiveStakeTransformer(cryptoCurrencyStatus, activeStake))
|
||||
stateController.update(ValidatorSelectChangeTransformer(activeStake.validator))
|
||||
stateController.update(AmountChangeStateTransformer(cryptoCurrencyStatus, activeStake.cryptoValue, yield))
|
||||
|
||||
onNextClick(
|
||||
actionTypeToOverwrite = null,
|
||||
pendingAction = activeStake.pendingActions.firstOrNull(),
|
||||
pendingActions = activeStake.pendingActions.takeIf { isAllWithdrawActions },
|
||||
balanceState = activeStake,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -617,7 +547,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
stakingAnalyticSender.sendTransactionApprovalAnalytics(tokenCryptoCurrency)
|
||||
stateController.update(SetApprovalInProgressTransformer)
|
||||
stateController.update(DismissBottomSheetStateTransformer)
|
||||
awaitForAllowance(confirmationState.pendingAction)
|
||||
awaitForAllowance()
|
||||
},
|
||||
)
|
||||
}.saveIn(approvalJobHolder)
|
||||
|
|
@ -703,7 +633,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
stateController.update(DismissStakingNotificationsStateTransformer(notification))
|
||||
}
|
||||
|
||||
private fun awaitForAllowance(pendingAction: PendingAction?) {
|
||||
private fun awaitForAllowance() {
|
||||
val approval = stakingApproval as? StakingApproval.Needed ?: return
|
||||
allowanceTaskScheduler.scheduleTask(
|
||||
scope = viewModelScope,
|
||||
|
|
@ -722,7 +652,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
val amount = (value.amountState as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
|
||||
?: error("No amount provided")
|
||||
if (allowance >= amount) {
|
||||
getFee(pendingAction = pendingAction, pendingActions = null)
|
||||
getFee()
|
||||
allowanceTaskScheduler.cancelTask()
|
||||
}
|
||||
},
|
||||
|
|
@ -760,7 +690,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
val cardInfo = getCardInfoUseCase(userWallet.scanResponse).getOrElse { error("CardInfo must be not null") }
|
||||
val amountState = uiState.value.amountState as? AmountState.Data
|
||||
val confirmationState = uiState.value.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val validatorState = confirmationState?.validatorState as? ValidatorState.Content
|
||||
val validatorState = uiState.value.validatorState as? StakingStates.ValidatorState.Data
|
||||
val feeState = confirmationState?.feeState as? FeeState.Content
|
||||
|
||||
val validator = validatorState?.chosenValidator
|
||||
|
|
@ -802,13 +732,16 @@ internal class StakingViewModel @Inject constructor(
|
|||
stakingApproval = isApproveNeededUseCase(cryptoCurrencyStatus.currency).getOrElse { StakingApproval.Empty }
|
||||
}
|
||||
|
||||
private suspend fun setupIsAnyTokenStaked() {
|
||||
isAnyTokenStaked = isAnyTokenStakedUseCase(userWalletId).getOrNull() ?: false
|
||||
}
|
||||
|
||||
private fun subscribeOnCurrencyStatusUpdates() {
|
||||
getUserWalletUseCase(userWalletId).fold(
|
||||
ifRight = { wallet ->
|
||||
userWallet = wallet
|
||||
},
|
||||
ifLeft = {
|
||||
Timber.e(it.toString())
|
||||
stakingEventFactory.createGenericErrorAlert(it.toString())
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
},
|
||||
|
|
@ -820,6 +753,8 @@ internal class StakingViewModel @Inject constructor(
|
|||
.onEach { maybeStatus ->
|
||||
maybeStatus.fold(
|
||||
ifRight = { status ->
|
||||
if (status.value !is CryptoCurrencyStatus.Loaded) return@fold
|
||||
|
||||
if (!isInitialInfoAnalyticSent) {
|
||||
isInitialInfoAnalyticSent = true
|
||||
val balances = status.value.yieldBalance as? YieldBalance.Data
|
||||
|
|
@ -836,26 +771,14 @@ internal class StakingViewModel @Inject constructor(
|
|||
feeCryptoCurrencyStatus =
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, status).getOrNull()
|
||||
cryptoCurrencyStatus = status
|
||||
val isAnyTokenStaked = isAnyTokenStakedUseCase(userWalletId).getOrNull() ?: false
|
||||
|
||||
setupApprovalNeeded()
|
||||
setupIsAnyTokenStaked()
|
||||
checkIfSubtractAvailable()
|
||||
|
||||
stateController.update(
|
||||
transformer = SetInitialDataStateTransformer(
|
||||
clickIntents = this@StakingViewModel,
|
||||
yield = yield,
|
||||
isAnyTokenStaked = isAnyTokenStaked,
|
||||
isApprovalNeeded = stakingApproval is StakingApproval.Needed,
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
userWalletProvider = Provider { userWallet },
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
balancesToShowProvider = Provider { balancesToShow },
|
||||
),
|
||||
)
|
||||
subscribeOnActionsUpdates()
|
||||
subscribeOnStepChanges()
|
||||
},
|
||||
ifLeft = { error ->
|
||||
Timber.e(error.toString())
|
||||
stakingEventFactory.createGenericErrorAlert(error.toString())
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
},
|
||||
|
|
@ -887,6 +810,93 @@ internal class StakingViewModel @Inject constructor(
|
|||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnStepChanges() {
|
||||
uiState
|
||||
.distinctUntilChangedBy { it.currentStep }
|
||||
.onEach {
|
||||
when {
|
||||
isInitState() -> {
|
||||
updateInitialData()
|
||||
balanceUpdater.initialUpdate()
|
||||
}
|
||||
isAssentState() -> {
|
||||
getFee()
|
||||
val amountState = value.amountState as? AmountState.Data
|
||||
if (amountState?.amountTextField?.isWarning == true) {
|
||||
stateController.update(
|
||||
AmountRoundToIntegerTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(viewModelScope)
|
||||
.saveIn(stepChangesJobHolder)
|
||||
}
|
||||
|
||||
private fun subscribeOnActionsUpdates() {
|
||||
getActionsUseCase(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = cryptoCurrencyId,
|
||||
)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach { result ->
|
||||
result.getOrNull()?.let { actions ->
|
||||
processingActions = actions
|
||||
if (isInitState()) {
|
||||
updateInitialData()
|
||||
}
|
||||
}
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun updateInitialData() {
|
||||
stateController.updateAll(
|
||||
SetInitialDataStateTransformer(
|
||||
clickIntents = this@StakingViewModel,
|
||||
yield = yield,
|
||||
isAnyTokenStaked = isAnyTokenStaked,
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
userWalletProvider = Provider { userWallet },
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
balancesToShowProvider = Provider { balancesToShow },
|
||||
),
|
||||
SetConfirmationStateEmptyTransformer,
|
||||
)
|
||||
}
|
||||
|
||||
private fun prepareForConfirmation(
|
||||
pendingActions: ImmutableList<PendingAction> = persistentListOf(),
|
||||
pendingAction: PendingAction? = pendingActions.firstOrNull(),
|
||||
validator: Yield.Validator?,
|
||||
amountValue: String,
|
||||
) {
|
||||
stateController.updateAll(
|
||||
SetConfirmationStateInitTransformer(
|
||||
isEnter = false,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
stakingApproval = stakingApproval,
|
||||
pendingActions = pendingActions,
|
||||
pendingAction = pendingAction,
|
||||
),
|
||||
ValidatorSelectChangeTransformer(
|
||||
selectedValidator = validator,
|
||||
yield = yield,
|
||||
),
|
||||
AmountChangeStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
value = amountValue,
|
||||
yield = yield,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun isAssentState(): Boolean {
|
||||
return value.currentStep == StakingStep.Confirmation &&
|
||||
(value.confirmationState as? StakingStates.ConfirmationState.Data)?.innerState ==
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue