diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 6cb64bafa2..1eeeaec28d 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 6b23058989..8a4d5d07db 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -39,6 +39,7 @@
Too many attempts
You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings.
Start backup process
+ From your fiat card or bank account
- %d card
- %d cards
@@ -87,6 +88,10 @@
Copy address
Create
Custom
+
+ - %d day
+ - %d days
+
Delete
Disabled
Done
@@ -257,6 +262,7 @@
No tokens found. Please try another request
ID: %s
Transaction ID copied
+ From another currency in your wallet
The following information is optional. You can erase it if you don\'t want to share it.
Tell us what functions you are missing, and we will try to help you.
Please tell us what card do you have
@@ -329,10 +335,12 @@
To start buying, exchanging or receiving this asset, add this token to at least 1 network
This asset is not available
Add to portfolio
+ Add token
Available networks
My portfolio
Market
To generate addresses for selected networks, you need to attach a Tangem card
+ Quick actions
Select network
Select wallet
1m
@@ -348,6 +356,7 @@
Top Gainers
Top losers
Trending
+ About %s
- Based on %d rating
- Based on %d ratings
@@ -486,6 +495,7 @@
Camera access denied
%1$s (%2$s) on %3$s network
Send only %s to this address. Sending any other currency will result in its irreversible loss.
+ Show QR-code or share your address
Participate
Failed to load the information about the referral program. Please try again later.
Failed to load the information about the referral program. Error code: %s. Please try again later.
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