Updated on 2026-08-14
This commit is contained in:
parent
67016b4755
commit
57eb2359a7
19 changed files with 291 additions and 125 deletions
|
|
@ -3,6 +3,7 @@ package com.tangem.features.staking.impl.presentation.state
|
|||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
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.domain.staking.model.stakekit.PendingAction
|
||||
|
|
@ -47,6 +48,14 @@ internal sealed class StakingStates {
|
|||
sealed class InitialInfoState : StakingStates() {
|
||||
data class Data(
|
||||
override val isPrimaryButtonEnabled: Boolean,
|
||||
val infoItems: ImmutableList<RoundedListWithDividersItemData>,
|
||||
val aprRange: TextReference,
|
||||
val onInfoClick: (InfoType) -> Unit,
|
||||
val yieldBalance: InnerYieldBalanceState,
|
||||
val isStakeMoreAvailable: Boolean,
|
||||
) : InitialInfoState()
|
||||
|
||||
data class InitialInfoItems(
|
||||
val available: String,
|
||||
val onStake: String,
|
||||
val aprRange: TextReference,
|
||||
|
|
@ -55,10 +64,7 @@ internal sealed class StakingStates {
|
|||
val rewardClaiming: String,
|
||||
val warmupPeriod: String,
|
||||
val rewardSchedule: String,
|
||||
val onInfoClick: (InfoType) -> Unit,
|
||||
val yieldBalance: InnerYieldBalanceState,
|
||||
val isStakeMoreAvailable: Boolean,
|
||||
) : InitialInfoState()
|
||||
)
|
||||
|
||||
data class Empty(
|
||||
override val isPrimaryButtonEnabled: Boolean = false,
|
||||
|
|
|
|||
|
|
@ -1,21 +1,59 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.previewdata
|
||||
|
||||
import com.tangem.core.ui.components.list.RoundedListWithDividersItemData
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal object InitialStakingStatePreview {
|
||||
val defaultState = StakingStates.InitialInfoState.Data(
|
||||
isPrimaryButtonEnabled = true,
|
||||
available = "15 SOL",
|
||||
onStake = "0 SOL",
|
||||
aprRange = stringReference("2.54-5.12%"),
|
||||
unbondingPeriod = "3d",
|
||||
minimumRequirement = "12 SOL",
|
||||
rewardClaiming = "Auto",
|
||||
warmupPeriod = "Days",
|
||||
rewardSchedule = "Block",
|
||||
infoItems = persistentListOf(
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_available,
|
||||
startText = TextReference.Res(R.string.staking_details_available),
|
||||
endText = TextReference.Str("15 SOL"),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_apy,
|
||||
startText = TextReference.Res(R.string.staking_details_apy),
|
||||
endText = TextReference.Str("2.54-5.12%"),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_on_stake,
|
||||
startText = TextReference.Res(R.string.staking_details_on_stake),
|
||||
endText = TextReference.Str("0 SOL"),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_unbonding_period,
|
||||
startText = TextReference.Res(R.string.staking_details_unbonding_period),
|
||||
endText = TextReference.Str("3d"),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_minimum_requirement,
|
||||
startText = TextReference.Res(R.string.staking_details_minimum_requirement),
|
||||
endText = TextReference.Str("12 SOL"),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_reward_claiming,
|
||||
startText = TextReference.Res(R.string.staking_details_reward_claiming),
|
||||
endText = TextReference.Str("Auto"),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_warmup_period,
|
||||
startText = TextReference.Res(R.string.staking_details_warmup_period),
|
||||
endText = TextReference.Str("Days"),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_reward_schedule,
|
||||
startText = TextReference.Res(R.string.staking_details_reward_schedule),
|
||||
endText = TextReference.Str("Block"),
|
||||
),
|
||||
),
|
||||
onInfoClick = {},
|
||||
yieldBalance = InnerYieldBalanceState.Empty,
|
||||
isStakeMoreAvailable = true,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import com.tangem.utils.Provider
|
|||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
internal class SetConfirmationStateAssentTransformer(
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.tangem.features.staking.impl.presentation.state.*
|
|||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
@Suppress("UnusedPrivateMember")
|
||||
internal class SetConfirmationStateLoadingTransformer(
|
||||
private val yield: Yield,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.common.extensions.remove
|
|||
import com.tangem.common.ui.amountScreen.converters.AmountStateConverter
|
||||
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.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
|
|
@ -26,6 +27,8 @@ import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickInten
|
|||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class SetInitialDataStateTransformer(
|
||||
|
|
@ -70,33 +73,87 @@ internal class SetInitialDataStateTransformer(
|
|||
}
|
||||
|
||||
private fun createInitialInfoState(): StakingStates.InitialInfoState.Data {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance
|
||||
|
||||
return StakingStates.InitialInfoState.Data(
|
||||
isPrimaryButtonEnabled = true,
|
||||
available = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = cryptoCurrencyStatus.value.amount,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency.symbol,
|
||||
decimals = cryptoCurrencyStatus.currency.decimals,
|
||||
),
|
||||
onStake = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = (yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero(),
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency.symbol,
|
||||
decimals = cryptoCurrencyStatus.currency.decimals,
|
||||
),
|
||||
aprRange = getAprRange(),
|
||||
unbondingPeriod = yield.metadata.cooldownPeriod.days.toString(),
|
||||
minimumRequirement = yield.metadata.minimumStake.toString(),
|
||||
rewardClaiming = yield.metadata.rewardClaiming,
|
||||
warmupPeriod = yield.metadata.warmupPeriod.days.toString(),
|
||||
rewardSchedule = yield.metadata.rewardSchedule,
|
||||
infoItems = getInfoItems(),
|
||||
onInfoClick = clickIntents::onInfoClick,
|
||||
yieldBalance = yieldBalancesConverter.convert(Unit),
|
||||
isStakeMoreAvailable = isStakeMoreAvailable,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getInfoItems(): PersistentList<RoundedListWithDividersItemData> {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance
|
||||
|
||||
return persistentListOf(
|
||||
RoundedListWithDividersItemData(
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_apy,
|
||||
startText = TextReference.Res(R.string.staking_details_apy),
|
||||
endText = getAprRange(),
|
||||
iconClick = { clickIntents.onInfoClick(InfoType.APY) },
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_on_stake,
|
||||
startText = TextReference.Res(R.string.staking_details_on_stake),
|
||||
endText = TextReference.Str(
|
||||
value = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = (yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero(),
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency.symbol,
|
||||
decimals = cryptoCurrencyStatus.currency.decimals,
|
||||
),
|
||||
),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_unbonding_period,
|
||||
startText = TextReference.Res(R.string.staking_details_unbonding_period),
|
||||
endText = TextReference.Str(yield.metadata.cooldownPeriod.days.toString()),
|
||||
iconClick = { clickIntents.onInfoClick(InfoType.UNBOUNDING_PERIOD) },
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_minimum_requirement,
|
||||
startText = TextReference.Res(R.string.staking_details_minimum_requirement),
|
||||
endText = TextReference.Str(
|
||||
value = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = yield.args.enter.args[KEY_AMOUNT]?.minimum?.toBigDecimal(),
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency.symbol,
|
||||
decimals = cryptoCurrencyStatus.currency.decimals,
|
||||
),
|
||||
),
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_reward_claiming,
|
||||
startText = TextReference.Res(R.string.staking_details_reward_claiming),
|
||||
endText = TextReference.Str(yield.metadata.rewardClaiming),
|
||||
iconClick = { clickIntents.onInfoClick(InfoType.REWARD_CLAIMING) },
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_warmup_period,
|
||||
startText = TextReference.Res(R.string.staking_details_warmup_period),
|
||||
endText = TextReference.Str(yield.metadata.warmupPeriod.days.toString()),
|
||||
iconClick = { clickIntents.onInfoClick(InfoType.WARMUP_PERIOD) },
|
||||
),
|
||||
RoundedListWithDividersItemData(
|
||||
id = R.string.staking_details_reward_schedule,
|
||||
startText = TextReference.Res(R.string.staking_details_reward_schedule),
|
||||
endText = TextReference.Str(yield.metadata.rewardSchedule),
|
||||
iconClick = { clickIntents.onInfoClick(InfoType.REWARD_SCHEDULE) },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createInitialAmountState(): AmountState {
|
||||
return amountStateConverter.convert("")
|
||||
}
|
||||
|
|
@ -134,5 +191,6 @@ internal class SetInitialDataStateTransformer(
|
|||
|
||||
companion object {
|
||||
private val EQUALITY_THRESHOLD = BigDecimal(1E-10)
|
||||
private const val KEY_AMOUNT = "amount"
|
||||
}
|
||||
}
|
||||
|
|
@ -7,9 +7,7 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -27,8 +25,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
|||
import com.tangem.core.ui.components.containers.FooterContainer
|
||||
import com.tangem.core.ui.components.inputrow.InputRowDefault
|
||||
import com.tangem.core.ui.components.inputrow.InputRowImageInfo
|
||||
import com.tangem.core.ui.components.rows.CornersToRound
|
||||
import com.tangem.core.ui.components.rows.RoundableCornersRow
|
||||
import com.tangem.core.ui.components.list.RoundedListWithDividers
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
|
@ -37,7 +34,6 @@ import com.tangem.features.staking.impl.R
|
|||
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
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
import com.tangem.utils.StringsSigns.DOT
|
||||
import com.tangem.utils.StringsSigns.PLUS
|
||||
|
|
@ -55,13 +51,12 @@ internal fun StakingInitialInfoContent(state: StakingStates.InitialInfoState, cl
|
|||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
),
|
||||
) {
|
||||
AnimatedVisibility(state.yieldBalance == InnerYieldBalanceState.Empty) {
|
||||
MetricsBlock(state)
|
||||
}
|
||||
StakingDetailsRows(state)
|
||||
RoundedListWithDividers(state.infoItems)
|
||||
AnimatedContent(targetState = state.yieldBalance, label = "Rewards block visibility animation") {
|
||||
if (it is InnerYieldBalanceState.Data) {
|
||||
StakingRewardBlock(
|
||||
|
|
@ -142,57 +137,6 @@ private fun MetricsBlock(state: StakingStates.InitialInfoState.Data) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun StakingDetailsRows(state: StakingStates.InitialInfoState.Data) {
|
||||
Column {
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_available),
|
||||
endText = state.available,
|
||||
cornersToRound = CornersToRound.TOP_2,
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_on_stake),
|
||||
endText = state.onStake,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_apy),
|
||||
endText = state.aprRange.resolveReference(),
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.APY) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_unbonding_period),
|
||||
endText = state.unbondingPeriod,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.UNBOUNDING_PERIOD) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_minimum_requirement),
|
||||
endText = state.minimumRequirement,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_reward_claiming),
|
||||
endText = state.rewardClaiming,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.REWARD_CLAIMING) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_warmup_period),
|
||||
endText = state.warmupPeriod,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.WARMUP_PERIOD) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_reward_schedule),
|
||||
endText = state.rewardSchedule,
|
||||
cornersToRound = CornersToRound.BOTTOM_2,
|
||||
iconClick = { state.onInfoClick(InfoType.REWARD_SCHEDULE) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StakingRewardBlock(
|
||||
rewardCrypto: String,
|
||||
|
|
@ -295,25 +239,7 @@ private fun ActiveStakingBlock(groups: List<BalanceGroupedState>, onClick: (Bala
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InitialInfoContentRow(
|
||||
startText: String,
|
||||
endText: String,
|
||||
cornersToRound: CornersToRound,
|
||||
iconClick: (() -> Unit)? = null,
|
||||
) {
|
||||
RoundableCornersRow(
|
||||
startText = startText,
|
||||
startTextColor = TangemTheme.colors.text.primary1,
|
||||
startTextStyle = TangemTheme.typography.body2,
|
||||
endText = endText,
|
||||
endTextColor = TangemTheme.colors.text.tertiary,
|
||||
endTextStyle = TangemTheme.typography.body2,
|
||||
cornersToRound = cornersToRound,
|
||||
iconResId = R.drawable.ic_information_24,
|
||||
iconClick = iconClick,
|
||||
)
|
||||
}
|
||||
// region preview
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue