diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 84cc72c8e2..ee19fe0771 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -86,6 +86,12 @@
Скопировать адрес
Создать
Свое
+
+ - %d день
+ - %d дня
+ - %d дней
+ - %d дней
+
Удалить
Отключено
Готово
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 5ef91d4236..002bbbf669 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -88,6 +88,10 @@
Copy address
Create
Custom
+
+ - %d day
+ - %d days
+
Delete
Disabled
Done
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt
index ce67d13f6f..d06e59fc30 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt
@@ -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(
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageBase.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageBase.kt
index 00213b0077..417a7edbec 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageBase.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageBase.kt
@@ -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),
)
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageInfo.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageInfo.kt
index c4fb398c6e..f9811bbe1a 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageInfo.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowImageInfo.kt
@@ -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),
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/inner/InputRowAsyncImage.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/inner/InputRowAsyncImage.kt
index 3fd1ae7722..b85e91ba30 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/inner/InputRowAsyncImage.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/inner/InputRowAsyncImage.kt
@@ -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)
diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/MarkdownExtension.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/MarkdownExtension.kt
index b8b11c058f..c510835242 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/extensions/MarkdownExtension.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/MarkdownExtension.kt
@@ -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(" ")
\ No newline at end of file
+fun AnnotatedString.Builder.appendSpace() = append(" ")
+
+fun AnnotatedString.Builder.appendColored(text: String, color: Color) = withStyle(SpanStyle(color = color)) {
+ append(text)
+}
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt
index 386f69b66b..4aab281036 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt
@@ -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.
*
diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/GrayscaleUtils.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/GrayscaleUtils.kt
index f3edb75b60..a71cf3875a 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/utils/GrayscaleUtils.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/utils/GrayscaleUtils.kt
@@ -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) })
\ No newline at end of file
+ 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 = if (isGrayscale) {
+ GRAY_SCALE_ALPHA to GrayscaleColorFilter
+} else {
+ NORMAL_ALPHA to null
+}
\ No newline at end of file
diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletMappers.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletMappers.kt
index 94fc6e00b1..eab5ed50e3 100644
--- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletMappers.kt
+++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletMappers.kt
@@ -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
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt
index 61015e2357..4d5cfcff8a 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerYieldBalanceState.kt
@@ -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,
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 {
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt
index f263b18a14..d3570e6c23 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt
@@ -44,6 +44,7 @@ internal class StakingStateController @Inject constructor() {
isBalanceHidden = false,
event = consumedEvent(),
bottomSheetConfig = null,
+ routeType = RouteType.STAKE,
)
}
}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt
index d3edfd4322..2c0b98f544 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt
@@ -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,
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt
index cd1e65b946..4a1be94850 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt
@@ -23,6 +23,7 @@ internal data class StakingUiState(
val confirmationState: StakingStates.ConfirmationState,
val isBalanceHidden: Boolean,
val bottomSheetConfig: TangemBottomSheetConfig?,
+ val routeType: RouteType,
val event: StateEvent,
) {
@@ -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,
}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt
index 169be97410..e05f2dfcdf 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/RewardsValidatorStateConverter.kt
@@ -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,
)
}
}
\ No newline at end of file
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt
index 28ede76079..f6d436f3c3 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/converters/YieldBalancesConverter.kt
@@ -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),
+ ),
)
}
}
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt
index 4eadd4916a..2c9ac37b88 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/InitialStakingStatePreview.kt
@@ -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"),
),
),
),
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt
index 3cc9baa334..c001927cd1 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt
@@ -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,
private val userWalletProvider: Provider,
private val appCurrencyProvider: Provider,
@@ -92,6 +93,7 @@ internal class SetInitialDataStateTransformer(
rewardSchedule = yield.metadata.rewardSchedule,
onInfoClick = clickIntents::onInfoClick,
yieldBalance = yieldBalancesConverter.convert(Unit),
+ isStakeMoreAvailable = isStakeMoreAvailable,
)
}
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt
index 98f42fccf2..261d4437cf 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmationContent.kt
@@ -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,
)
}
}
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt
index 44b3566f06..4eb8244ef3 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingInitialInfoContent.kt
@@ -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) {
+private fun ActiveStakingBlock(groups: List, onClick: (BalanceState) -> Unit) {
Column(
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
@@ -253,15 +248,43 @@ private fun ActiveStakingBlock(groups: List) {
) {
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) },
+ ),
)
}
}
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingNavigationButtons.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingNavigationButtons.kt
index 73e2c6e526..41b426c018 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingNavigationButtons.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingNavigationButtons.kt
@@ -126,7 +126,12 @@ private fun getButtonData(currentState: StakingUiState): Pair 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
}
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt
index e1b08be7ac..c26935b80c 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt
@@ -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
diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt
index 157dfbebf4..c2b477f54b 100644
--- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt
+++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt
@@ -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 },
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt
index 773dc2868f..a6d81965bb 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt
@@ -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
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/TokenStakingBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/TokenStakingBlock.kt
index afa3fc743d..c2d7940cbc 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/TokenStakingBlock.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/TokenStakingBlock.kt
@@ -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