Updated on 2026-08-14
This commit is contained in:
commit
3c8fb7f668
25 changed files with 195 additions and 97 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ internal class StakingStateController @Inject constructor() {
|
|||
isBalanceHidden = false,
|
||||
event = consumedEvent(),
|
||||
bottomSheetConfig = null,
|
||||
routeType = RouteType.STAKE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue