Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-13 13:02:12 +03:00
commit 3946e4ebca
359 changed files with 8459 additions and 3324 deletions

View file

@ -52,7 +52,9 @@ internal class StakingAnalyticSender(
source = when (value.currentStep) {
StakingStep.InitialInfo -> StakeScreenSource.Info
StakingStep.Amount -> StakeScreenSource.Amount
StakingStep.Confirmation -> StakeScreenSource.Confirmation
StakingStep.Success,
StakingStep.Confirmation,
-> StakeScreenSource.Confirmation
StakingStep.Validators,
StakingStep.RestakeValidator,
StakingStep.RewardsValidators,

View file

@ -87,7 +87,7 @@ internal class StakingStateController @Inject constructor(
cryptoCurrencyBlockchainId = "",
currentStep = StakingStep.InitialInfo,
initialInfoState = StakingStates.InitialInfoState.Empty(),
amountState = AmountState.Empty(isRedesignEnabled = false),
amountState = AmountState.Empty,
validatorState = StakingStates.ValidatorState.Empty(),
rewardsValidatorsState = StakingStates.RewardsValidatorsState.Empty(),
confirmationState = StakingStates.ConfirmationState.Empty(),

View file

@ -42,6 +42,7 @@ internal class StakingStateRouter(
StakingStep.Amount,
-> showConfirmation()
StakingStep.Confirmation -> showInitial()
StakingStep.Success -> appRouter.pop()
}
}
@ -65,6 +66,7 @@ internal class StakingStateRouter(
}
}
StakingStep.Validators -> showConfirmation()
StakingStep.Success -> appRouter.pop()
}
}

View file

@ -138,4 +138,5 @@ enum class StakingStep {
RestakeValidator,
Confirmation,
Validators,
Success,
}

View file

@ -46,10 +46,11 @@ internal class SetAmountDataTransformer(
return prevState.copy(
amountState = AmountStateConverter(
clickIntents = clickIntents,
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
appCurrencyProvider = appCurrencyProvider,
iconStateConverter = iconStateConverter,
maxEnterAmount = maxEnterAmount,
appCurrency = appCurrencyProvider(),
cryptoCurrencyStatus = cryptoCurrencyStatusProvider(),
isBalanceHidden = false,
).convert(
AmountParameters(
title = title,

View file

@ -25,7 +25,6 @@ internal class SetButtonsStateTransformer(
val buttonsState = if (prevState.isButtonsVisible()) {
NavigationButtonsState.Data(
primaryButton = getPrimaryButton(prevState),
prevButton = getPrevButton(prevState),
extraButtons = getExtraButtons(prevState).takeIf { txUrl != null },
txUrl = txUrl,
onTextClick = urlOpener::openUrl,
@ -64,18 +63,6 @@ internal class SetButtonsStateTransformer(
)
}
private fun getPrevButton(prevState: StakingUiState): NavigationButton? {
return NavigationButton(
textReference = TextReference.EMPTY,
iconRes = R.drawable.ic_back_24,
isSecondary = true,
isIconVisible = true,
shouldShowProgress = false,
isEnabled = true,
onClick = prevState.clickIntents::onPrevClick,
).takeIf { prevState.currentStep.isPrevButtonVisible() }
}
private fun getExtraButtons(prevState: StakingUiState): Pair<NavigationButton, NavigationButton> {
return NavigationButton(
textReference = resourceReference(R.string.common_explore),
@ -111,7 +98,7 @@ internal class SetButtonsStateTransformer(
resourceReference(R.string.common_stake)
}
}
StakingStep.Success -> resourceReference(R.string.common_close)
StakingStep.Confirmation -> getConfirmationButtonText()
StakingStep.Validators -> resourceReference(R.string.common_continue)
StakingStep.Amount,
@ -125,21 +112,17 @@ internal class SetButtonsStateTransformer(
val confirmationState = confirmationState as? StakingStates.ConfirmationState.Data
val amountState = amountState as? AmountState.Data
return if (confirmationState != null && amountState != null) {
if (confirmationState.innerState == InnerConfirmationStakingState.COMPLETED) {
resourceReference(R.string.common_close)
} else {
when (actionType) {
is StakingActionCommonType.Enter -> {
val amount = amountState.amountTextField.cryptoAmount.value.orZero()
if (confirmationState.isApprovalNeeded && confirmationState.allowance < amount) {
resourceReference(R.string.give_permission_title)
} else {
resourceReference(R.string.common_stake)
}
when (actionType) {
is StakingActionCommonType.Enter -> {
val amount = amountState.amountTextField.cryptoAmount.value.orZero()
if (confirmationState.isApprovalNeeded && confirmationState.allowance < amount) {
resourceReference(R.string.give_permission_title)
} else {
resourceReference(R.string.common_stake)
}
is StakingActionCommonType.Exit -> resourceReference(R.string.common_unstake)
is StakingActionCommonType.Pending -> confirmationState.pendingAction?.type.getPendingActionTitle()
}
is StakingActionCommonType.Exit -> resourceReference(R.string.common_unstake)
is StakingActionCommonType.Pending -> confirmationState.pendingAction?.type.getPendingActionTitle()
}
} else {
resourceReference(R.string.common_close)
@ -155,6 +138,7 @@ internal class SetButtonsStateTransformer(
StakingStep.Amount -> clickIntents.onAmountEnterClick()
StakingStep.Confirmation -> onConfirmationClick()
StakingStep.RewardsValidators -> Unit
StakingStep.Success -> clickIntents.onBackClick()
}
}
@ -178,17 +162,6 @@ internal class SetButtonsStateTransformer(
}
}
private fun StakingStep.isPrevButtonVisible(): Boolean = when (this) {
StakingStep.InitialInfo,
StakingStep.RewardsValidators,
StakingStep.RestakeValidator,
StakingStep.Confirmation,
StakingStep.Validators,
-> false
StakingStep.Amount,
-> true
}
private fun StakingUiState.isPrimaryButtonDisabled(): Boolean {
val initialState = initialInfoState as? StakingStates.InitialInfoState.Data
val hasNotStaking = initialState?.yieldBalance == InnerYieldBalanceState.Empty
@ -205,6 +178,7 @@ internal class SetButtonsStateTransformer(
StakingStep.RewardsValidators -> rewardsValidatorsState.isPrimaryButtonEnabled
StakingStep.RestakeValidator,
StakingStep.Validators,
StakingStep.Success,
-> true
}
}

View file

@ -4,6 +4,7 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.currency.CryptoCurrencyStatus
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.StakingStep
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.features.staking.impl.presentation.state.TransactionDoneState
import com.tangem.utils.transformer.Transformer
@ -17,6 +18,7 @@ internal class SetConfirmationStateCompletedTransformer(
override fun transform(prevState: StakingUiState): StakingUiState {
return prevState.copy(
confirmationState = prevState.confirmationState.copyWrapped(),
currentStep = StakingStep.Success,
)
}

View file

@ -229,10 +229,11 @@ internal class SetInitialDataStateTransformer(
)
return AmountStateConverter(
clickIntents = clickIntents,
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
appCurrencyProvider = appCurrencyProvider,
cryptoCurrencyStatus = cryptoCurrencyStatus,
appCurrency = appCurrencyProvider(),
iconStateConverter = iconStateConverter,
maxEnterAmount = maxEnterAmount,
isBalanceHidden = false,
).convert(
AmountParameters(
title = stringReference(userWalletProvider().name),

View file

@ -1,5 +1,6 @@
package com.tangem.features.staking.impl.presentation.state.transformers
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.stringReference
@ -28,7 +29,7 @@ internal object SetTitleTransformer : Transformer<StakingUiState> {
R.string.staking_title_stake,
wrappedList(prevState.cryptoCurrencyName),
)
StakingStep.Success -> TextReference.EMPTY
StakingStep.Confirmation -> {
when (actionType) {
is StakingActionCommonType.Enter -> resourceReference(

View file

@ -14,12 +14,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.preview.AmountStatePreviewData
import com.tangem.common.ui.amountScreen.ui.AmountBlock
import com.tangem.common.ui.amountScreen.ui.AmountBlockV2
import com.tangem.core.ui.components.transactions.TransactionDoneTitle
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.presentation.model.StakingClickIntents
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
@ -30,7 +31,6 @@ import com.tangem.features.staking.impl.presentation.state.stub.StakingClickInte
import com.tangem.features.staking.impl.presentation.ui.block.NotificationsBlock
import com.tangem.features.staking.impl.presentation.ui.block.StakingFeeBlock
import com.tangem.features.staking.impl.presentation.ui.block.ValidatorBlock
import com.tangem.features.staking.impl.presentation.model.StakingClickIntents
@Suppress("LongParameterList")
@Composable
@ -60,7 +60,7 @@ internal fun StakingConfirmationContent(
subtitle = resourceReference(R.string.staking_transaction_in_progress_text),
)
}
AmountBlock(
AmountBlockV2(
amountState = amountState,
isClickDisabled = !state.isAmountEditable || isTransactionSent || isTransactionInProgress,
isEditingDisabled = !state.isAmountEditable && state.innerState != InnerConfirmationStakingState.COMPLETED,

View file

@ -94,6 +94,7 @@ private fun StakingAppBar(uiState: StakingUiState) {
val (backIcon, click) = when (uiState.currentStep) {
StakingStep.Amount,
StakingStep.Confirmation,
StakingStep.Success,
-> R.drawable.ic_close_24 to uiState.clickIntents::onBackClick
StakingStep.Validators,
StakingStep.RewardsValidators,
@ -111,6 +112,7 @@ private fun StakingAppBar(uiState: StakingUiState) {
)
}
@Suppress("LongMethod")
@Composable
private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = Modifier) {
val currentScreen = uiState.currentStep
@ -139,10 +141,10 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M
contentAlignment = Alignment.TopCenter,
label = "Staking Screen Navigation",
transitionSpec = {
val direction = if (initialState.ordinal < targetState.ordinal) {
AnimatedContentTransitionScope.SlideDirection.Start
} else {
AnimatedContentTransitionScope.SlideDirection.End
val direction = when {
targetState == StakingStep.Success -> AnimatedContentTransitionScope.SlideDirection.Up
initialState.ordinal < targetState.ordinal -> AnimatedContentTransitionScope.SlideDirection.Start
else -> AnimatedContentTransitionScope.SlideDirection.End
}
slideIntoContainer(towards = direction, animationSpec = tween())
@ -166,7 +168,6 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M
}
StakingStep.Amount -> AmountScreenContent(
amountState = uiState.amountState,
isBalanceHidden = uiState.isBalanceHidden,
clickIntents = uiState.clickIntents,
modifier = Modifier.background(TangemTheme.colors.background.secondary),
)
@ -176,6 +177,12 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M
validatorState = uiState.validatorState,
clickIntents = uiState.clickIntents,
)
StakingStep.Success -> StakingSuccessContent(
amountState = uiState.amountState,
state = uiState.confirmationState,
validatorState = uiState.validatorState,
clickIntents = uiState.clickIntents,
)
StakingStep.RestakeValidator,
StakingStep.Validators,
-> StakingValidatorListContent(

View file

@ -0,0 +1,86 @@
package com.tangem.features.staking.impl.presentation.ui
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.preview.AmountStatePreviewData
import com.tangem.common.ui.amountScreen.ui.AmountBlock
import com.tangem.core.ui.components.transactions.TransactionDoneTitle
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.presentation.model.StakingClickIntents
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.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
import com.tangem.features.staking.impl.presentation.ui.block.ValidatorBlock
@Suppress("LongParameterList")
@Composable
internal fun StakingSuccessContent(
amountState: AmountState,
state: StakingStates.ConfirmationState,
validatorState: StakingStates.ValidatorState,
clickIntents: StakingClickIntents,
) {
if (state !is StakingStates.ConfirmationState.Data) return
val isTransactionSent = state.innerState == InnerConfirmationStakingState.COMPLETED
val isTransactionInProgress = state.notifications.any { it is StakingNotification.Warning.TransactionInProgress }
Column(
modifier = Modifier
.background(TangemTheme.colors.background.secondary)
.padding(horizontal = TangemTheme.dimens.spacing16)
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
) {
TransactionDoneTitle(
title = resourceReference(R.string.common_in_progress),
subtitle = resourceReference(R.string.staking_transaction_in_progress_text),
)
AmountBlock(
amountState = amountState,
isClickDisabled = !state.isAmountEditable || isTransactionSent || isTransactionInProgress,
isEditingDisabled = !state.isAmountEditable && state.innerState != InnerConfirmationStakingState.COMPLETED,
onClick = clickIntents::onPrevClick,
)
ValidatorBlock(
validatorState = validatorState,
isClickable = !isTransactionInProgress,
onClick = clickIntents::openValidators,
)
StakingFeeBlock(feeState = state.feeState)
NotificationsBlock(notifications = state.notifications)
Spacer(Modifier)
}
}
@Preview(widthDp = 360, showBackground = true)
@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun Preview_StakingConfirmationContent() {
TangemThemePreview {
Column(Modifier.background(TangemTheme.colors.background.primary)) {
StakingConfirmationContent(
amountState = AmountStatePreviewData.amountState,
state = ConfirmationStatePreviewData.assentStakingState,
validatorState = ValidatorStatePreviewData.validatorState,
clickIntents = StakingClickIntentsStub,
)
}
}
}