Updated on 2026-08-14

This commit is contained in:
Tangem 2024-07-17 18:35:19 +05:00
commit 3c8fb7f668
25 changed files with 195 additions and 97 deletions

View file

@ -86,6 +86,12 @@
<string name="common_copy_address">Скопировать адрес</string>
<string name="common_create">Создать</string>
<string name="common_custom">Свое</string>
<plurals name="common_days">
<item quantity="one">%d день</item>
<item quantity="few">%d дня</item>
<item quantity="many">%d дней</item>
<item quantity="other">%d дней</item>
</plurals>
<string name="common_delete">Удалить</string>
<string name="common_disabled">Отключено</string>
<string name="common_done">Готово</string>

View file

@ -88,6 +88,10 @@
<string name="common_copy_address">Copy address</string>
<string name="common_create">Create</string>
<string name="common_custom">Custom</string>
<plurals name="common_days">
<item quantity="one">%d day</item>
<item quantity="other">%d days</item>
</plurals>
<string name="common_delete">Delete</string>
<string name="common_disabled">Disabled</string>
<string name="common_done">Done</string>

View file

@ -12,9 +12,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.tangem.core.ui.components.CircleShimmer
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.GRAY_SCALE_ALPHA
import com.tangem.core.ui.utils.GrayscaleColorFilter
import com.tangem.core.ui.utils.NORMAL_ALPHA
import com.tangem.core.ui.utils.getGreyScaleColorFilter
/**
* Cryptocurrency icon with network badge
@ -76,11 +74,7 @@ private fun BoxScope.ContentIconContainer(
) {
val networkBadgeOffset = TangemTheme.dimens.spacing4
val (alpha, colorFilter) = remember(icon.isGrayscale) {
if (icon.isGrayscale) {
GRAY_SCALE_ALPHA to GrayscaleColorFilter
} else {
NORMAL_ALPHA to null
}
getGreyScaleColorFilter(icon.isGrayscale)
}
ContentIcon(

View file

@ -20,6 +20,7 @@ internal fun InputRowImageBase(
modifier: Modifier = Modifier,
subtitleColor: Color = TangemTheme.colors.text.primary1,
captionColor: Color = TangemTheme.colors.text.tertiary,
isGrayscaleImage: Boolean = false,
extraContent: @Composable RowScope.() -> Unit = {},
) {
Row(
@ -29,6 +30,7 @@ internal fun InputRowImageBase(
) {
InputRowAsyncImage(
imageUrl = imageUrl,
isGrayscale = isGrayscaleImage,
modifier = Modifier
.size(TangemTheme.dimens.spacing36),
)

View file

@ -32,6 +32,7 @@ import com.tangem.core.ui.res.TangemThemePreview
* @param imageUrl icon to load
* @param subtitleColor subtitle text color
* @param captionColor caption text color
* @param isGrayscaleImage whether to display grayscale image
*/
@Suppress("LongParameterList")
@Composable
@ -45,6 +46,7 @@ fun InputRowImageInfo(
title: TextReference? = null,
subtitleColor: Color = TangemTheme.colors.text.primary1,
captionColor: Color = TangemTheme.colors.text.tertiary,
isGrayscaleImage: Boolean = false,
) {
Column(
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing6),
@ -64,6 +66,7 @@ fun InputRowImageInfo(
imageUrl = imageUrl,
subtitleColor = subtitleColor,
captionColor = captionColor,
isGrayscaleImage = isGrayscaleImage,
) {
Column(
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),

View file

@ -10,17 +10,22 @@ import coil.compose.SubcomposeAsyncImage
import coil.request.ImageRequest
import com.tangem.core.ui.components.currency.icon.LoadingIcon
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.getGreyScaleColorFilter
/**
* Loads image by url for icon in the input row
*
* @param imageUrl url of the image
* @param modifier modifier
* @param isGrayscale whether to apply grayscale filter
*/
@Composable
internal fun InputRowAsyncImage(imageUrl: String, modifier: Modifier = Modifier) {
internal fun InputRowAsyncImage(imageUrl: String, modifier: Modifier = Modifier, isGrayscale: Boolean = false) {
val (alpha, colorFilter) = getGreyScaleColorFilter(isGrayscale = isGrayscale)
SubcomposeAsyncImage(
modifier = modifier,
colorFilter = colorFilter,
alpha = alpha,
model = ImageRequest.Builder(context = LocalContext.current)
.data(imageUrl)
.crossfade(enable = true)

View file

@ -2,6 +2,7 @@ package com.tangem.core.ui.extensions
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.FontWeight
@ -55,4 +56,8 @@ fun AnnotatedString.Builder.appendMarkdown(markdownText: String, node: ASTNode):
return this
}
fun AnnotatedString.Builder.appendSpace() = append(" ")
fun AnnotatedString.Builder.appendSpace() = append(" ")
fun AnnotatedString.Builder.appendColored(text: String, color: Color) = withStyle(SpanStyle(color = color)) {
append(text)
}

View file

@ -9,6 +9,7 @@ import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.AnnotatedString.Builder
import androidx.compose.ui.text.buildAnnotatedString
import org.intellij.markdown.MarkdownElementTypes
import kotlin.contracts.ExperimentalContracts
@ -100,6 +101,17 @@ fun annotatedReference(value: AnnotatedString): TextReference {
return TextReference.Annotated(value)
}
/**
* Creates a [TextReference] using an annotated string value.
*
* @param onAnnotated The annotated string builder.
* @return A [TextReference] representing the provided annotated string value.
*/
@Composable
inline fun annotatedReference(onAnnotated: Builder.() -> Unit): TextReference {
return TextReference.Annotated(buildAnnotatedString { onAnnotated() })
}
/**
* Creates a [TextReference] using a plural string resource ID with count and optional format arguments.
*

View file

@ -8,4 +8,13 @@ const val GRAY_SCALE_ALPHA = 0.4f
const val NORMAL_ALPHA = 1f
val GrayscaleColorFilter: ColorFilter
get() = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(GRAY_SCALE_SATURATION) })
get() = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(GRAY_SCALE_SATURATION) })
/**
* Returns alpha and color filter for grayscale if [isGrayscale] is true
*/
fun getGreyScaleColorFilter(isGrayscale: Boolean): Pair<Float, ColorFilter?> = if (isGrayscale) {
GRAY_SCALE_ALPHA to GrayscaleColorFilter
} else {
NORMAL_ALPHA to null
}

View file

@ -9,7 +9,7 @@ import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.details.entity.UserWalletListUM.UserWalletUM
import com.tangem.features.details.impl.R
import com.tangem.utils.Strings.STARS
import com.tangem.utils.StringsSigns.STARS
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList

View file

@ -3,6 +3,7 @@ package com.tangem.features.staking.impl.presentation.state
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.staking.model.Yield
import kotlinx.collections.immutable.ImmutableList
import java.math.BigDecimal
sealed class InnerYieldBalanceState {
data class Data(
@ -19,14 +20,17 @@ data class BalanceGroupedState(
val items: ImmutableList<BalanceState>,
val footer: TextReference?,
val title: TextReference,
val type: BalanceGroupType,
)
data class BalanceState(
val validator: Yield.Validator,
val cryptoValue: String,
val cryptoDecimal: BigDecimal,
val cryptoAmount: TextReference,
val fiatAmount: TextReference,
val rawCurrencyId: String?,
val unbondingPeriod: TextReference,
)
enum class BalanceGroupType {

View file

@ -44,6 +44,7 @@ internal class StakingStateController @Inject constructor() {
isBalanceHidden = false,
event = consumedEvent(),
bottomSheetConfig = null,
routeType = RouteType.STAKE,
)
}
}

View file

@ -13,8 +13,12 @@ internal class StakingStateRouter(
}
fun onNextClick() {
when (stateController.uiState.value.currentStep) {
StakingStep.InitialInfo -> showAmount()
when (stateController.value.currentStep) {
StakingStep.InitialInfo -> when (stateController.value.routeType) {
RouteType.STAKE -> showAmount()
RouteType.UNSTAKE -> showConfirmation()
RouteType.CLAIM -> showRewardsValidators()
}
StakingStep.RewardsValidators,
StakingStep.Validators,
StakingStep.Amount,

View file

@ -23,6 +23,7 @@ internal data class StakingUiState(
val confirmationState: StakingStates.ConfirmationState,
val isBalanceHidden: Boolean,
val bottomSheetConfig: TangemBottomSheetConfig?,
val routeType: RouteType,
val event: StateEvent<StakingEvent>,
) {
@ -55,6 +56,7 @@ internal sealed class StakingStates {
val rewardSchedule: String,
val onInfoClick: (InfoType) -> Unit,
val yieldBalance: InnerYieldBalanceState,
val isStakeMoreAvailable: Boolean,
) : InitialInfoState()
data class Empty(
@ -98,4 +100,10 @@ enum class StakingStep {
Amount,
Confirmation,
Validators,
}
enum class RouteType {
STAKE,
UNSTAKE,
CLAIM,
}

View file

@ -1,7 +1,6 @@
package com.tangem.features.staking.impl.presentation.state.converters
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.appcurrency.model.AppCurrency
@ -10,12 +9,12 @@ import com.tangem.domain.staking.model.BalanceType
import com.tangem.domain.staking.model.Yield
import com.tangem.domain.staking.model.YieldBalance
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.presentation.state.BalanceState
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.utils.Provider
import com.tangem.utils.StringsSigns.PLUS
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.addOrReplace
import kotlinx.collections.immutable.toPersistentList
import java.math.BigDecimal
@ -33,13 +32,6 @@ internal class RewardsValidatorStateConverter(
StakingStates.RewardsValidatorsState.Data(
isPrimaryButtonEnabled = true,
rewards = balances
// todo remove when real data is available
.addOrReplace(
item = balances.first().copy(
type = BalanceType.REWARDS,
),
predicate = { true },
)
.filter { it.type == BalanceType.REWARDS }
.mapRewardBalances(cryptoCurrencyStatus)
.toPersistentList(),
@ -56,11 +48,16 @@ internal class RewardsValidatorStateConverter(
}
val cryptoValue = balance.amount.times(balance.pricePerShare)
val fiatValue = cryptoCurrencyStatus.value.fiatRate?.times(cryptoValue)
val unbondingPeriod = yield.metadata.cooldownPeriod.days
validator?.toBalanceState(
cryptoCurrencyStatus = cryptoCurrencyStatus,
cryptoValue = cryptoValue,
fiatValue = fiatValue,
unbondingPeriod = pluralReference(
id = R.plurals.common_days,
count = unbondingPeriod,
formatArgs = wrappedList(unbondingPeriod),
),
)
}
@ -68,6 +65,7 @@ internal class RewardsValidatorStateConverter(
cryptoCurrencyStatus: CryptoCurrencyStatus,
cryptoValue: BigDecimal,
fiatValue: BigDecimal?,
unbondingPeriod: TextReference,
): BalanceState {
val appCurrency = appCurrencyProvider()
val cryptoCurrency = cryptoCurrencyStatus.currency
@ -92,9 +90,11 @@ internal class RewardsValidatorStateConverter(
return BalanceState(
validator = this,
cryptoValue = cryptoValue.parseBigDecimal(cryptoCurrency.decimals),
cryptoDecimal = cryptoValue,
cryptoAmount = cryptoAmount,
fiatAmount = fiatAmount,
rawCurrencyId = cryptoCurrency.id.rawCurrencyId,
unbondingPeriod = unbondingPeriod,
)
}
}

View file

@ -1,7 +1,9 @@
package com.tangem.features.staking.impl.presentation.state.converters
import com.tangem.core.ui.extensions.pluralReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.appcurrency.model.AppCurrency
@ -14,7 +16,6 @@ import com.tangem.features.staking.impl.presentation.state.BalanceState
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.addOrReplace
import com.tangem.utils.isNullOrZero
import kotlinx.collections.immutable.toPersistentList
@ -54,13 +55,7 @@ internal class YieldBalancesConverter(
}
private fun getGroupedBalance(balance: YieldBalanceItem) = balance.items
// todo remove when real data is available
.addOrReplace(
item = balance.items.first().copy(
type = BalanceType.REWARDS,
),
predicate = { true },
)
.sortedBy { it.type }
.groupBy { it.type.toGroup() }
.mapNotNull { item ->
val (title, footer) = getGroupTitle(item.key)
@ -69,6 +64,7 @@ internal class YieldBalancesConverter(
items = item.value.mapBalances().toPersistentList(),
footer = footer,
title = it,
type = item.key,
)
}
}
@ -83,11 +79,12 @@ internal class YieldBalancesConverter(
}
val cryptoAmount = balance.amount * balance.pricePerShare
val fiatAmount = cryptoCurrencyStatus.value.fiatRate?.times(cryptoAmount)
val unbondingPeriod = yield.metadata.cooldownPeriod.days
validator?.let {
BalanceState(
validator = validator,
cryptoValue = cryptoAmount.parseBigDecimal(cryptoCurrency.decimals),
cryptoDecimal = cryptoAmount,
cryptoAmount = stringReference(
BigDecimalFormatter.formatCryptoAmount(
cryptoAmount = cryptoAmount,
@ -102,6 +99,11 @@ internal class YieldBalancesConverter(
),
),
rawCurrencyId = balance.rawCurrencyId,
unbondingPeriod = pluralReference(
id = R.plurals.common_days,
count = unbondingPeriod,
formatArgs = wrappedList(unbondingPeriod),
),
)
}
}

View file

@ -2,10 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.previewdata
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.staking.model.Yield
import com.tangem.features.staking.impl.presentation.state.BalanceGroupedState
import com.tangem.features.staking.impl.presentation.state.BalanceState
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.state.*
import kotlinx.collections.immutable.persistentListOf
internal object InitialStakingStatePreview {
@ -21,6 +18,7 @@ internal object InitialStakingStatePreview {
rewardSchedule = "Block",
onInfoClick = {},
yieldBalance = InnerYieldBalanceState.Empty,
isStakeMoreAvailable = true,
)
val stateWithYield = defaultState.copy(
@ -32,10 +30,12 @@ internal object InitialStakingStatePreview {
BalanceGroupedState(
title = stringReference("Staked"),
footer = null,
type = BalanceGroupType.ACTIVE,
items = persistentListOf(
BalanceState(
cryptoValue = "100",
cryptoAmount = stringReference("100 SOL"),
cryptoDecimal = "100".toBigDecimal(),
fiatAmount = stringReference("100 $"),
rawCurrencyId = null,
validator = Yield.Validator(
@ -50,6 +50,7 @@ internal object InitialStakingStatePreview {
votingPower = null,
preferred = false,
),
unbondingPeriod = stringReference("3 days"),
),
),
),

View file

@ -31,6 +31,7 @@ import java.math.BigDecimal
internal class SetInitialDataStateTransformer(
private val clickIntents: StakingClickIntents,
private val yield: Yield,
private val isStakeMoreAvailable: Boolean,
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
private val userWalletProvider: Provider<UserWallet>,
private val appCurrencyProvider: Provider<AppCurrency>,
@ -92,6 +93,7 @@ internal class SetInitialDataStateTransformer(
rewardSchedule = yield.metadata.rewardSchedule,
onInfoClick = clickIntents::onInfoClick,
yieldBalance = yieldBalancesConverter.convert(Unit),
isStakeMoreAvailable = isStakeMoreAvailable,
)
}

View file

@ -18,21 +18,23 @@ import com.tangem.core.ui.components.SpacerHMax
import com.tangem.core.ui.components.transactions.TransactionDoneTitle
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.state.RouteType
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.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
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.presentation.state.TransactionDoneState
@Composable
internal fun StakingConfirmationContent(
amountState: AmountState,
state: StakingStates.ConfirmationState,
clickIntents: StakingClickIntents,
type: RouteType,
) {
if (state !is StakingStates.ConfirmationState.Data) return
@ -60,7 +62,9 @@ internal fun StakingConfirmationContent(
isEditingDisabled = true,
onClick = {},
)
ValidatorBlock(validatorState = state.validatorState, onClick = clickIntents::openValidators)
if (type == RouteType.STAKE) {
ValidatorBlock(validatorState = state.validatorState, onClick = clickIntents::openValidators)
}
StakingFeeBlock(feeState = state.feeState)
NotificationsBlock(notifications = state.notifications)
SpacerHMax()
@ -89,6 +93,7 @@ private fun Preview_StakingConfirmationContent() {
amountState = AmountStatePreviewData.amountState,
state = ConfirmationStatePreviewData.assentStakingState,
clickIntents = StakingClickIntentsStub,
type = RouteType.STAKE,
)
}
}

View file

@ -21,7 +21,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
@ -35,9 +34,7 @@ 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.BalanceGroupedState
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.state.*
import com.tangem.features.staking.impl.presentation.state.previewdata.InitialStakingStatePreview
import com.tangem.features.staking.impl.presentation.state.stub.StakingClickIntentsStub
import com.tangem.features.staking.impl.presentation.state.transformers.InfoType
@ -77,7 +74,7 @@ internal fun StakingInitialInfoContent(state: StakingStates.InitialInfoState, cl
}
AnimatedContent(targetState = state.yieldBalance, label = "Rewards block visibility animation") {
if (it is InnerYieldBalanceState.Data) {
ActiveStakingBlock(it.balance)
ActiveStakingBlock(it.balance, clickIntents::onActiveStake)
}
}
}
@ -204,17 +201,15 @@ private fun StakingRewardBlock(
onRewardsClick: () -> Unit,
) {
val (text, textColor) = if (isRewardsToClaim) {
annotatedReference(
buildAnnotatedString {
append(PLUS)
appendSpace()
append(rewardFiat)
appendSpace()
append(DOT)
appendSpace()
append(rewardCrypto)
},
) to TangemTheme.colors.text.primary1
annotatedReference {
append(PLUS)
appendSpace()
append(rewardFiat)
appendSpace()
append(DOT)
appendSpace()
append(rewardCrypto)
} to TangemTheme.colors.text.primary1
} else {
resourceReference(R.string.staking_details_no_rewards_to_claim) to TangemTheme.colors.text.tertiary
}
@ -235,7 +230,7 @@ private fun StakingRewardBlock(
}
@Composable
private fun ActiveStakingBlock(groups: List<BalanceGroupedState>) {
private fun ActiveStakingBlock(groups: List<BalanceGroupedState>, onClick: (BalanceState) -> Unit) {
Column(
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
@ -253,15 +248,43 @@ private fun ActiveStakingBlock(groups: List<BalanceGroupedState>) {
) {
group.items.forEachIndexed { index, balance ->
key(balance.validator.address) {
val caption = combinedReference(
if (group.type == BalanceGroupType.UNSTAKED) {
resourceReference(R.string.staking_details_unbonding_period)
annotatedReference {
appendSpace()
appendColored(
text = balance.unbondingPeriod.resolveReference(),
color = TangemTheme.colors.text.accent,
)
}
} else {
resourceReference(R.string.app_name)
annotatedReference {
appendSpace()
appendColored(
text = BigDecimalFormatter.formatPercent(
percent = balance.validator.apr.orZero(),
useAbsoluteValue = true,
),
color = TangemTheme.colors.text.accent,
)
}
},
)
InputRowImageInfo(
title = group.title.takeIf { index == 0 },
subtitle = stringReference(balance.validator.name),
caption = stringReference(
BigDecimalFormatter.formatPercent(balance.validator.apr.orZero(), true),
),
caption = caption,
isGrayscaleImage = group.type == BalanceGroupType.UNSTAKED,
infoTitle = balance.fiatAmount,
infoSubtitle = balance.cryptoAmount,
imageUrl = balance.validator.image.orEmpty(),
modifier = Modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(),
onClick = { onClick(balance) },
),
)
}
}

View file

@ -126,7 +126,12 @@ private fun getButtonData(currentState: StakingUiState): Pair<Int, (() -> Unit)?
StakingStep.InitialInfo -> {
val initialState = currentState.initialInfoState as? StakingStates.InitialInfoState.Data
if (initialState?.yieldBalance is InnerYieldBalanceState.Data) {
R.string.staking_stake_more to currentState.clickIntents::onNextClick
val click = if (initialState.isStakeMoreAvailable) {
currentState.clickIntents::onNextClick
} else {
null
}
R.string.staking_stake_more to click
} else {
R.string.common_next to currentState.clickIntents::onNextClick
}

View file

@ -155,6 +155,7 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M
amountState = uiState.amountState,
state = uiState.confirmationState,
clickIntents = uiState.clickIntents,
type = uiState.routeType,
)
StakingStep.Validators -> {
val confirmState = uiState.confirmationState

View file

@ -14,10 +14,7 @@ import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.staking.GetStakingTransactionUseCase
import com.tangem.domain.staking.EstimateGasUseCase
import com.tangem.domain.staking.SaveUnsubmittedHashUseCase
import com.tangem.domain.staking.SubmitHashUseCase
import com.tangem.domain.staking.*
import com.tangem.domain.staking.model.Yield
import com.tangem.domain.staking.model.action.StakingActionCommonType
import com.tangem.domain.staking.model.transaction.ActionParams
@ -32,10 +29,6 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.staking.impl.navigation.InnerStakingRouter
import com.tangem.features.staking.impl.presentation.state.*
import com.tangem.features.staking.impl.presentation.state.StakingStateController
import com.tangem.features.staking.impl.presentation.state.StakingStateRouter
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.transformers.*
import com.tangem.features.staking.impl.presentation.state.transformers.amount.AmountChangeStateTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.amount.AmountCurrencyChangeStateTransformer
@ -66,6 +59,7 @@ internal class StakingViewModel @Inject constructor(
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val saveUnsubmittedHashUseCase: SaveUnsubmittedHashUseCase,
private val submitHashUseCase: SubmitHashUseCase,
private val isStakeMoreAvailableUseCase: IsStakeMoreAvailableUseCase,
savedStateHandle: SavedStateHandle,
) : ViewModel(), DefaultLifecycleObserver, StakingClickIntents {
@ -117,15 +111,26 @@ internal class StakingViewModel @Inject constructor(
viewModelScope.launch {
stateController.update(SetConfirmationStateInProgressTransformer())
val confirmationState =
value.confirmationState as? StakingStates.ConfirmationState.Data ?: error("No confirmation state")
val validatorState = confirmationState.validatorState as? ValidatorState.Content
?: error("No validator provided")
val actionType = when (value.routeType) {
RouteType.STAKE -> StakingActionCommonType.ENTER
RouteType.UNSTAKE -> StakingActionCommonType.EXIT
RouteType.CLAIM -> TODO()
}
val stakingTransaction = getStakingTransactionUseCase(
params = ActionParams(
actionCommonType = StakingActionCommonType.ENTER,
actionCommonType = actionType,
integrationId = yield.id,
amount = (value.amountState as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
?: error("No amount provided"),
address = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
?: error("No available address"),
validatorAddress = yield.validators.getOrNull(0)?.address ?: error("No available validator"),
validatorAddress = validatorState.chosenValidator.address,
token = yield.token,
),
).getOrElse {
@ -151,9 +156,15 @@ internal class StakingViewModel @Inject constructor(
),
)
val actionType = when (value.routeType) {
RouteType.STAKE -> StakingActionCommonType.ENTER
RouteType.UNSTAKE -> StakingActionCommonType.EXIT
RouteType.CLAIM -> TODO()
}
val stakingGasEstimate = estimateGasUseCase(
params = ActionParams(
actionCommonType = StakingActionCommonType.ENTER,
actionCommonType = actionType,
integrationId = yield.id,
amount = (value.amountState as? AmountState.Data)?.amountTextField?.cryptoAmount?.value
?: error("No amount provided"),
@ -209,16 +220,19 @@ internal class StakingViewModel @Inject constructor(
}
override fun openRewardsValidators() {
stakingStateRouter.showRewardsValidators()
stateController.update { it.copy(routeType = RouteType.CLAIM) }
onNextClick()
}
override fun selectRewardValidator(rewardValue: String) {
stateController.update(AmountChangeStateTransformer(cryptoCurrencyStatus, rewardValue))
stakingStateRouter.onNextClick()
onNextClick()
}
override fun onActiveStake(activeStake: BalanceState) {
// todo unstake
stateController.update { it.copy(routeType = RouteType.UNSTAKE) }
stateController.update(AmountChangeStateTransformer(cryptoCurrencyStatus, activeStake.cryptoValue))
onNextClick()
}
override fun onExploreClick() {
@ -253,10 +267,14 @@ internal class StakingViewModel @Inject constructor(
getCryptoCurrencyStatusSyncUseCase(userWalletId, cryptoCurrencyId).fold(
ifRight = {
cryptoCurrencyStatus = it
val networkId = cryptoCurrencyStatus.currency.network.id
val isStakeMoreAvailable = isStakeMoreAvailableUseCase(networkId)
stateController.update(
transformer = SetInitialDataStateTransformer(
clickIntents = this@StakingViewModel,
yield = yield,
isStakeMoreAvailable = isStakeMoreAvailable.getOrElse { false },
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
userWalletProvider = Provider { userWallet },
appCurrencyProvider = Provider { appCurrency },

View file

@ -18,9 +18,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.utils.GRAY_SCALE_ALPHA
import com.tangem.core.ui.utils.GrayscaleColorFilter
import com.tangem.core.ui.utils.NORMAL_ALPHA
import com.tangem.core.ui.utils.getGreyScaleColorFilter
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState
import com.tangem.features.tokendetails.impl.R
@ -46,11 +44,7 @@ internal fun TokenInfoBlock(state: TokenInfoBlockState, modifier: Modifier = Mod
}
val (alpha, colorFilter) = remember(state.iconState.isGrayscale) {
if (state.iconState.isGrayscale) {
GRAY_SCALE_ALPHA to GrayscaleColorFilter
} else {
NORMAL_ALPHA to null
}
getGreyScaleColorFilter(state.iconState.isGrayscale)
}
CurrencyIcon(
modifier = Modifier

View file

@ -19,9 +19,7 @@ import com.tangem.core.ui.components.SecondaryButton
import com.tangem.core.ui.components.SpacerW8
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.utils.GRAY_SCALE_ALPHA
import com.tangem.core.ui.utils.GrayscaleColorFilter
import com.tangem.core.ui.utils.NORMAL_ALPHA
import com.tangem.core.ui.utils.getGreyScaleColorFilter
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingAvailableBlock
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingErrorBlock
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData.stakingLoadingBlock
@ -74,11 +72,7 @@ private fun StakingAvailableContent(state: StakingBlockUM.StakeAvailable, modifi
) {
Row {
val (alpha, colorFilter) = remember(state.iconState.isGrayscale) {
if (state.iconState.isGrayscale) {
GRAY_SCALE_ALPHA to GrayscaleColorFilter
} else {
NORMAL_ALPHA to null
}
getGreyScaleColorFilter(state.iconState.isGrayscale)
}
CurrencyIcon(
modifier = Modifier
@ -138,11 +132,7 @@ private fun StakingLoading(iconState: IconState, modifier: Modifier = Modifier)
) {
Row {
val (alpha, colorFilter) = remember(iconState.isGrayscale) {
if (iconState.isGrayscale) {
GRAY_SCALE_ALPHA to GrayscaleColorFilter
} else {
NORMAL_ALPHA to null
}
getGreyScaleColorFilter(iconState.isGrayscale)
}
CurrencyIcon(
modifier = Modifier