From 6227d835b0cec7fcc8fe9316f065b098820d5901 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 2 Aug 2024 16:16:39 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../model/error/StakeKitErrorResponse.kt | 2 +- .../list/RoundedListWithDividers.kt | 35 +++- .../data/staking/DefaultStakingRepository.kt | 24 +-- .../impl/presentation/state/FeeState.kt | 2 + .../state/InnerYieldBalanceState.kt | 6 +- .../presentation/state/StakingNotification.kt | 11 +- .../state/StakingStateController.kt | 2 + .../impl/presentation/state/StakingUiState.kt | 1 + .../converters/YieldBalancesConverter.kt | 1 + .../previewdata/InitialStakingStatePreview.kt | 2 +- .../AddStakingErrorTransformer.kt | 36 ++++ .../SetInitialDataStateTransformer.kt | 157 ++++++++++++------ .../ShowInfoBottomSheetStateTransformer.kt | 4 +- .../ui/StakingInitialInfoContent.kt | 36 ++-- .../impl/presentation/ui/StakingScreen.kt | 3 +- .../ui/block/NotificationsBlock.kt | 16 +- .../viewmodel/StakingViewModel.kt | 5 +- 17 files changed, 250 insertions(+), 93 deletions(-) create mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingErrorTransformer.kt diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/error/StakeKitErrorResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/error/StakeKitErrorResponse.kt index 30396de002..9ad42062ca 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/error/StakeKitErrorResponse.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/stakekit/models/response/model/error/StakeKitErrorResponse.kt @@ -16,7 +16,7 @@ class StakeKitErrorResponse( @Json(name = "code") val code: String? = null, @Json(name = "countryCode") - val countryCode: String, + val countryCode: String?, @Json(name = "regionCode") val regionCode: String? = null, @Json(name = "tags") diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/list/RoundedListWithDividers.kt b/core/ui/src/main/java/com/tangem/core/ui/components/list/RoundedListWithDividers.kt index 1779adf487..336f35d454 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/list/RoundedListWithDividers.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/list/RoundedListWithDividers.kt @@ -16,16 +16,39 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +private const val ROUNDED_LIST_WITH_DIVIDERS_HEADER_KEY = "ROUNDED_LIST_WITH_DIVIDERS_HEADER_KEY" +private const val ROUNDED_LIST_WITH_DIVIDERS_FOOTER_KEY = "ROUNDED_LIST_WITH_DIVIDERS_FOOTER_KEY" + @Composable -fun RoundedListWithDividers(rows: List, modifier: Modifier = Modifier) { +fun RoundedListWithDividers( + rows: ImmutableList, + modifier: Modifier = Modifier, + headerContent: (@Composable () -> Unit)? = null, + footerContent: (@Composable () -> Unit)? = null, +) { LazyColumn(modifier = modifier) { - this.roundedListItems(rows) + this.roundedListWithDividersItems( + rows = rows, + headerContent = headerContent, + footerContent = footerContent, + ) } } -fun LazyListScope.roundedListItems(rows: List) { +fun LazyListScope.roundedListWithDividersItems( + rows: ImmutableList, + headerContent: (@Composable () -> Unit)? = null, + footerContent: (@Composable () -> Unit)? = null, +) { + if (headerContent != null) { + item(key = ROUNDED_LIST_WITH_DIVIDERS_HEADER_KEY) { + headerContent() + } + } + itemsIndexed( items = rows, key = { _, item -> item.id }, @@ -40,6 +63,12 @@ fun LazyListScope.roundedListItems(rows: List) RoundedListDivider() } } + + if (footerContent != null) { + item(key = ROUNDED_LIST_WITH_DIVIDERS_FOOTER_KEY) { + footerContent() + } + } } @Composable diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index a8c7009e7b..6f5ce7909c 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt @@ -46,6 +46,7 @@ import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.extensions.orZero import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch @@ -135,7 +136,7 @@ internal class DefaultStakingRepository( val yield = getYield(cryptoCurrencyId, symbol) StakingEntryInfo( - interestRate = yield.apy, + interestRate = requireNotNull(yield.validators.maxByOrNull { it.apr.orZero() }?.apr), periodInDays = yield.metadata.cooldownPeriod.days, tokenSymbol = yield.token.symbol, ) @@ -568,23 +569,24 @@ internal class DefaultStakingRepository( const val AVALANCHE_INTEGRATION_ID = "avalanche-avax-native-staking" const val TRON_INTEGRATION_ID = "tron-trx-native-staking" const val CRONOS_INTEGRATION_ID = "cronos-cro-native-staking" - const val BINANCE_INTEGRATION_ID = "binance-bnb-native-staking" + const val BINANCE_INTEGRATION_ID = "bsc-bnb-native-staking" const val KAVA_INTEGRATION_ID = "kava-kava-native-staking" const val NEAR_INTEGRATION_ID = "near-near-native-staking" const val TEZOS_INTEGRATION_ID = "tezos-xtz-native-staking" + // uncomment items as implementation is ready val integrationIdMap = mapOf( Blockchain.Solana.toCoinId() to SOLANA_INTEGRATION_ID, Blockchain.Cosmos.toCoinId() to COSMOS_INTEGRATION_ID, - Blockchain.Polkadot.toCoinId() to POLKADOT_INTEGRATION_ID, - Blockchain.Polygon.toCoinId() to ETHEREUM_INTEGRATION_ID, - Blockchain.Avalanche.toCoinId() to AVALANCHE_INTEGRATION_ID, - Blockchain.Tron.toCoinId() to TRON_INTEGRATION_ID, - Blockchain.Cronos.toCoinId() to CRONOS_INTEGRATION_ID, - Blockchain.Binance.toCoinId() to BINANCE_INTEGRATION_ID, - Blockchain.Kava.toCoinId() to KAVA_INTEGRATION_ID, - Blockchain.Near.toCoinId() to NEAR_INTEGRATION_ID, - Blockchain.Tezos.toCoinId() to TEZOS_INTEGRATION_ID, + // Blockchain.Polkadot.toCoinId() to POLKADOT_INTEGRATION_ID, + // Blockchain.Polygon.toCoinId() to ETHEREUM_INTEGRATION_ID, + // Blockchain.Avalanche.toCoinId() to AVALANCHE_INTEGRATION_ID, + // Blockchain.Tron.toCoinId() to TRON_INTEGRATION_ID, + // Blockchain.Cronos.toCoinId() to CRONOS_INTEGRATION_ID, + // Blockchain.Binance.toCoinId() to BINANCE_INTEGRATION_ID, + // Blockchain.Kava.toCoinId() to KAVA_INTEGRATION_ID, + // Blockchain.Near.toCoinId() to NEAR_INTEGRATION_ID, + // Blockchain.Tezos.toCoinId() to TEZOS_INTEGRATION_ID, ) } } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/FeeState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/FeeState.kt index c58332a870..ad93ecd3a3 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/FeeState.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/FeeState.kt @@ -1,9 +1,11 @@ package com.tangem.features.staking.impl.presentation.state +import androidx.compose.runtime.Immutable import com.tangem.blockchain.common.transaction.Fee import com.tangem.domain.appcurrency.model.AppCurrency import java.math.BigDecimal +@Immutable sealed class FeeState { data class Content( 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 78ef0dc9dd..ce7d7eecb5 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 @@ -1,5 +1,6 @@ package com.tangem.features.staking.impl.presentation.state +import androidx.compose.runtime.Immutable import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.staking.model.stakekit.BalanceType import com.tangem.domain.staking.model.stakekit.PendingAction @@ -12,12 +13,14 @@ sealed class InnerYieldBalanceState { val rewardsCrypto: String, val rewardsFiat: String, val isRewardsToClaim: Boolean, - val balance: List, + val balance: ImmutableList, ) : InnerYieldBalanceState() data object Empty : InnerYieldBalanceState() } +// TODO staking get rid of unstable types +@Immutable data class BalanceGroupedState( val items: ImmutableList, val footer: TextReference?, @@ -26,6 +29,7 @@ data class BalanceGroupedState( val isClickable: Boolean, ) +@Immutable data class BalanceState( val validator: Yield.Validator, val cryptoValue: String, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt index 967f681edb..525eed8a61 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt @@ -3,6 +3,7 @@ package com.tangem.features.staking.impl.presentation.state import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.features.staking.impl.R @@ -23,7 +24,15 @@ internal sealed class StakingNotification(val config: NotificationConfig) { onCloseClick = onCloseClick, ), ) { - // TODO staking + data class StakedPositionNotFoundError(val message: String) : Error( + title = stringReference(message), + subtitle = stringReference(message), + ) + + data class Common(val subtitle: TextReference) : Error( + title = resourceReference(R.string.common_error), + subtitle = subtitle, + ) } sealed class Warning( 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 2dd8aa4900..2556ac4e2a 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 @@ -3,6 +3,7 @@ package com.tangem.features.staking.impl.presentation.state import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.navigationButtons.NavigationButtonsState import com.tangem.core.ui.event.consumedEvent +import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType import com.tangem.features.staking.impl.presentation.state.stub.StakingClickIntentsStub import com.tangem.features.staking.impl.presentation.state.transformers.SetButtonsStateTransformer @@ -42,6 +43,7 @@ internal class StakingStateController @Inject constructor() { private fun getInitialState(): StakingUiState { return StakingUiState( + title = TextReference.EMPTY, clickIntents = StakingClickIntentsStub, cryptoCurrencyName = "", currentStep = StakingStep.InitialInfo, 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 95f3d4cdab..98b09f5e78 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 @@ -18,6 +18,7 @@ import kotlinx.collections.immutable.ImmutableList */ @Immutable internal data class StakingUiState( + val title: TextReference, val clickIntents: StakingClickIntents, val cryptoCurrencyName: String, val currentStep: StakingStep, 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 3411ac4946..85ad364a40 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 @@ -69,6 +69,7 @@ internal class YieldBalancesConverter( ) } } + .toPersistentList() private fun List.mapBalances(): List { val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() 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 2a5b57532b..f7d431316d 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 @@ -65,7 +65,7 @@ internal object InitialStakingStatePreview { rewardsFiat = "100 $", rewardsCrypto = "100 SOL", isRewardsToClaim = false, - balance = listOf( + balance = persistentListOf( BalanceGroupedState( title = stringReference("Staked"), footer = null, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingErrorTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingErrorTransformer.kt new file mode 100644 index 0000000000..69278afeb6 --- /dev/null +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingErrorTransformer.kt @@ -0,0 +1,36 @@ +package com.tangem.features.staking.impl.presentation.state.transformers + +import com.tangem.core.ui.extensions.stringReference +import com.tangem.domain.staking.model.stakekit.StakingError +import com.tangem.features.staking.impl.presentation.state.* +import com.tangem.utils.transformer.Transformer +import kotlinx.collections.immutable.toPersistentList + +internal class AddStakingErrorTransformer( + private val error: StakingError, +) : Transformer { + + override fun transform(prevState: StakingUiState): StakingUiState { + val confirmationState = + prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState + + return prevState.copy( + confirmationState = confirmationState.copy( + notifications = (confirmationState.notifications + convertToNotification(error)).toPersistentList(), + feeState = FeeState.Error, + ), + ) + } + + private fun convertToNotification(error: StakingError): StakingNotification { + return when (error) { + is StakingError.StakedPositionNotFoundError -> StakingNotification.Error.StakedPositionNotFoundError( + message = error.toString(), + ) + // TODO staking + else -> StakingNotification.Error.Common( + subtitle = stringReference(error.toString()), + ) + } + } +} \ No newline at end of file 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 6023ae90fb..1cecfa9476 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 @@ -5,10 +5,7 @@ import com.tangem.common.ui.amountScreen.converters.AmountStateConverter import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.components.list.RoundedListWithDividersItemData -import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringReference -import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.extensions.* import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.staking.model.stakekit.Yield @@ -28,7 +25,7 @@ import com.tangem.utils.Provider import com.tangem.utils.extensions.orZero import com.tangem.utils.transformer.Transformer import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList import java.math.BigDecimal internal class SetInitialDataStateTransformer( @@ -62,6 +59,10 @@ internal class SetInitialDataStateTransformer( override fun transform(prevState: StakingUiState): StakingUiState { return prevState.copy( + title = TextReference.Res( + R.string.staking_initial_info_title, + wrappedList(cryptoCurrencyStatusProvider().currency.name), + ), clickIntents = clickIntents, currentStep = StakingStep.InitialInfo, initialInfoState = createInitialInfoState(), @@ -87,70 +88,118 @@ internal class SetInitialDataStateTransformer( val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() val yieldBalance = cryptoCurrencyStatus.value.yieldBalance - return persistentListOf( - RoundedListWithDividersItemData( - id = R.string.staking_details_available, - startText = TextReference.Res(R.string.staking_details_available), - endText = TextReference.Str( - value = BigDecimalFormatter.formatCryptoAmount( - cryptoAmount = cryptoCurrencyStatus.value.amount, - cryptoCurrency = cryptoCurrencyStatus.currency.symbol, - decimals = cryptoCurrencyStatus.currency.decimals, - ), + return listOfNotNull( + createAvailableItem(cryptoCurrencyStatus), + createApyItem(), + createOnStakeItem(cryptoCurrencyStatus, yieldBalance), + createUnbondingPeriodItem(), + createMinimumRequirementItem(cryptoCurrencyStatus), + createRewardClaimingItem(), + createWarmupPeriodItem(), + createRewardScheduleItem(), + ).toPersistentList() + } + + private fun createAvailableItem(cryptoCurrencyStatus: CryptoCurrencyStatus): RoundedListWithDividersItemData { + return RoundedListWithDividersItemData( + id = R.string.staking_details_available, + startText = TextReference.Res(R.string.staking_details_available), + endText = TextReference.Str( + value = BigDecimalFormatter.formatCryptoAmount( + cryptoAmount = cryptoCurrencyStatus.value.amount, + cryptoCurrency = cryptoCurrencyStatus.currency.symbol, + decimals = cryptoCurrencyStatus.currency.decimals, ), ), - RoundedListWithDividersItemData( - id = R.string.staking_details_apy, - startText = TextReference.Res(R.string.staking_details_apy), - endText = getAprRange(), - iconClick = { clickIntents.onInfoClick(InfoType.APY) }, - ), - RoundedListWithDividersItemData( - id = R.string.staking_details_on_stake, - startText = TextReference.Res(R.string.staking_details_on_stake), - endText = TextReference.Str( - value = BigDecimalFormatter.formatCryptoAmount( - cryptoAmount = (yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero(), - cryptoCurrency = cryptoCurrencyStatus.currency.symbol, - decimals = cryptoCurrencyStatus.currency.decimals, - ), + ) + } + + private fun createApyItem(): RoundedListWithDividersItemData { + return RoundedListWithDividersItemData( + id = R.string.staking_details_apy, + startText = TextReference.Res(R.string.staking_details_apy), + endText = getAprRange(), + iconClick = { clickIntents.onInfoClick(InfoType.APY) }, + ) + } + + private fun createOnStakeItem( + cryptoCurrencyStatus: CryptoCurrencyStatus, + yieldBalance: YieldBalance?, + ): RoundedListWithDividersItemData { + return RoundedListWithDividersItemData( + id = R.string.staking_details_on_stake, + startText = TextReference.Res(R.string.staking_details_on_stake), + endText = TextReference.Str( + value = BigDecimalFormatter.formatCryptoAmount( + cryptoAmount = (yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero(), + cryptoCurrency = cryptoCurrencyStatus.currency.symbol, + decimals = cryptoCurrencyStatus.currency.decimals, ), ), - RoundedListWithDividersItemData( - id = R.string.staking_details_unbonding_period, - startText = TextReference.Res(R.string.staking_details_unbonding_period), - endText = TextReference.Str(yield.metadata.cooldownPeriod.days.toString()), - iconClick = { clickIntents.onInfoClick(InfoType.UNBOUNDING_PERIOD) }, + ) + } + + private fun createUnbondingPeriodItem(): RoundedListWithDividersItemData { + return RoundedListWithDividersItemData( + id = R.string.staking_details_unbonding_period, + startText = TextReference.Res(R.string.staking_details_unbonding_period), + endText = pluralReference( + id = R.plurals.common_days, + count = yield.metadata.cooldownPeriod.days, + formatArgs = wrappedList(yield.metadata.cooldownPeriod.days), ), + iconClick = { clickIntents.onInfoClick(InfoType.UNBONDING_PERIOD) }, + ) + } + + private fun createMinimumRequirementItem( + cryptoCurrencyStatus: CryptoCurrencyStatus, + ): RoundedListWithDividersItemData? { + val minimumCryptoAmount = yield.args.enter.args[Yield.Args.ArgType.AMOUNT]?.minimum + return minimumCryptoAmount?.let { RoundedListWithDividersItemData( id = R.string.staking_details_minimum_requirement, startText = TextReference.Res(R.string.staking_details_minimum_requirement), endText = TextReference.Str( value = BigDecimalFormatter.formatCryptoAmount( - cryptoAmount = yield.args.enter.args[Yield.Args.ArgType.AMOUNT]?.minimum, + cryptoAmount = it, cryptoCurrency = cryptoCurrencyStatus.currency.symbol, decimals = cryptoCurrencyStatus.currency.decimals, ), ), + ) + } + } + + private fun createRewardClaimingItem(): RoundedListWithDividersItemData { + return RoundedListWithDividersItemData( + id = R.string.staking_details_reward_claiming, + startText = TextReference.Res(R.string.staking_details_reward_claiming), + endText = TextReference.Str(yield.metadata.rewardClaiming), + iconClick = { clickIntents.onInfoClick(InfoType.REWARD_CLAIMING) }, + ) + } + + private fun createWarmupPeriodItem(): RoundedListWithDividersItemData { + return RoundedListWithDividersItemData( + id = R.string.staking_details_warmup_period, + startText = TextReference.Res(R.string.staking_details_warmup_period), + endText = pluralReference( + id = R.plurals.common_days, + count = yield.metadata.warmupPeriod.days, + formatArgs = wrappedList(yield.metadata.warmupPeriod.days), ), - RoundedListWithDividersItemData( - id = R.string.staking_details_reward_claiming, - startText = TextReference.Res(R.string.staking_details_reward_claiming), - endText = TextReference.Str(yield.metadata.rewardClaiming), - iconClick = { clickIntents.onInfoClick(InfoType.REWARD_CLAIMING) }, - ), - RoundedListWithDividersItemData( - id = R.string.staking_details_warmup_period, - startText = TextReference.Res(R.string.staking_details_warmup_period), - endText = TextReference.Str(yield.metadata.warmupPeriod.days.toString()), - iconClick = { clickIntents.onInfoClick(InfoType.WARMUP_PERIOD) }, - ), - RoundedListWithDividersItemData( - id = R.string.staking_details_reward_schedule, - startText = TextReference.Res(R.string.staking_details_reward_schedule), - endText = TextReference.Str(yield.metadata.rewardSchedule), - iconClick = { clickIntents.onInfoClick(InfoType.REWARD_SCHEDULE) }, - ), + iconClick = { clickIntents.onInfoClick(InfoType.WARMUP_PERIOD) }, + ) + } + + private fun createRewardScheduleItem(): RoundedListWithDividersItemData { + return RoundedListWithDividersItemData( + id = R.string.staking_details_reward_schedule, + startText = TextReference.Res(R.string.staking_details_reward_schedule), + endText = TextReference.Str(yield.metadata.rewardSchedule), + iconClick = { clickIntents.onInfoClick(InfoType.REWARD_SCHEDULE) }, ) } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ShowInfoBottomSheetStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ShowInfoBottomSheetStateTransformer.kt index ff1701bc9b..da8124ccf1 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ShowInfoBottomSheetStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/ShowInfoBottomSheetStateTransformer.kt @@ -22,7 +22,7 @@ internal class ShowInfoBottomSheetStateTransformer( title = resourceReference(R.string.staking_details_apy), text = resourceReference(R.string.staking_details_apy_info), ) - InfoType.UNBOUNDING_PERIOD -> StakingInfoBottomSheetConfig( + InfoType.UNBONDING_PERIOD -> StakingInfoBottomSheetConfig( title = resourceReference(R.string.staking_details_unbonding_period), text = resourceReference(R.string.staking_details_unbonding_period_info), ) @@ -46,7 +46,7 @@ internal class ShowInfoBottomSheetStateTransformer( enum class InfoType { APY, - UNBOUNDING_PERIOD, + UNBONDING_PERIOD, REWARD_CLAIMING, WARMUP_PERIOD, REWARD_SCHEDULE, 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 cd1f6aaac1..b0cc41ab8e 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 @@ -26,7 +26,7 @@ import com.tangem.core.ui.components.SpacerH12 import com.tangem.core.ui.components.containers.FooterContainer import com.tangem.core.ui.components.inputrow.InputRowDefault import com.tangem.core.ui.components.inputrow.InputRowImageInfo -import com.tangem.core.ui.components.list.roundedListItems +import com.tangem.core.ui.components.list.roundedListWithDividersItems import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview @@ -40,9 +40,12 @@ import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickInten import com.tangem.utils.StringsSigns.DOT import com.tangem.utils.StringsSigns.PLUS import com.tangem.utils.extensions.orZero +import kotlinx.collections.immutable.ImmutableList + +// TODO staking metrics block is temporary disabled +// private const val METRICS_BLOCK_KEY = "MetricsBlock" private const val STAKING_REWARD_BLOCK_KEY = "StakingRewardBlock" -private const val METRICS_BLOCK_KEY = "MetricsBlock" private const val ACTIVE_STAKING_BLOCK_KEY = "ActiveStakingBlock" @OptIn(ExperimentalFoundationApi::class) @@ -52,21 +55,25 @@ internal fun StakingInitialInfoContent(state: StakingStates.InitialInfoState, cl LazyColumn( modifier = Modifier - .background(TangemTheme.colors.background.tertiary) + .background(TangemTheme.colors.background.secondary) .fillMaxSize(), contentPadding = PaddingValues(TangemTheme.dimens.spacing16), ) { - if (state.yieldBalance == InnerYieldBalanceState.Empty) { - item(key = METRICS_BLOCK_KEY) { - Column(modifier = Modifier.animateItemPlacement()) { - MetricsBlock(state) - SpacerH12() - } - } - } + // TODO staking metrics block is temporary disabled + // https://www.figma.com/design/Vs6SkVsFnUPsSCNwlnVf5U?node-id=12484-35755#876661319 + // if (state.yieldBalance == InnerYieldBalanceState.Empty) { + // item(key = METRICS_BLOCK_KEY) { + // Column(modifier = Modifier.animateItemPlacement()) { + // MetricsBlock(state) + // SpacerH12() + // } + // } + // } - this.roundedListItems(state.infoItems) - item { SpacerH12() } + this.roundedListWithDividersItems( + rows = state.infoItems, + footerContent = { SpacerH12() }, + ) if (state.yieldBalance is InnerYieldBalanceState.Data) { item(key = STAKING_REWARD_BLOCK_KEY) { @@ -93,6 +100,7 @@ internal fun StakingInitialInfoContent(state: StakingStates.InitialInfoState, cl } } +@Suppress("UnusedPrivateMember") @Composable private fun MetricsBlock(state: StakingStates.InitialInfoState.Data) { Column( @@ -193,7 +201,7 @@ private fun StakingRewardBlock( } @Composable -private fun ActiveStakingBlock(groups: List, onClick: (BalanceState) -> Unit) { +private fun ActiveStakingBlock(groups: ImmutableList, onClick: (BalanceState) -> Unit) { Column( verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), ) { 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 af3698a8b8..d3c1bda086 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 @@ -16,6 +16,7 @@ import com.tangem.common.ui.amountScreen.AmountScreenContent import com.tangem.common.ui.navigationButtons.NavigationButtonsBlock import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.features.staking.impl.R import com.tangem.features.staking.impl.presentation.state.StakingStates @@ -74,7 +75,7 @@ private fun SendAppBar(uiState: StakingUiState) { StakingStep.RewardsValidators, StakingStep.Validators, StakingStep.Confirmation, - -> stringResource(id = R.string.common_stake) + -> uiState.title.resolveReference() } val backIcon = when (uiState.currentStep) { StakingStep.Amount, diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/NotificationsBlock.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/NotificationsBlock.kt index 68d638aaaa..a0c3d67869 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/NotificationsBlock.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/NotificationsBlock.kt @@ -1,13 +1,23 @@ package com.tangem.features.staking.impl.presentation.ui.block import androidx.compose.runtime.Composable +import androidx.compose.runtime.key import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.res.TangemTheme import com.tangem.features.staking.impl.presentation.state.StakingNotification +import kotlinx.collections.immutable.ImmutableList @Composable -internal fun NotificationsBlock(notifications: List) { - notifications.forEach { - Notification(config = it.config, iconTint = TangemTheme.colors.icon.accent) +internal fun NotificationsBlock(notifications: ImmutableList) { + notifications.forEach { notification -> + key(notification) { + Notification( + config = notification.config, + iconTint = when (notification) { + is StakingNotification.Error -> TangemTheme.colors.icon.warning + is StakingNotification.Warning -> TangemTheme.colors.icon.accent + }, + ) + } } } \ No newline at end of file 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 a6fcb3d67c..23b5b5b76d 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 @@ -180,7 +180,10 @@ internal class StakingViewModel @Inject constructor( passthrough = pendingAction?.passthrough, type = pendingAction?.type, ), - ).getOrElse { error("Can't get fee info") } + ).getOrElse { + stateController.update(AddStakingErrorTransformer(it)) + return@launch + } stateController.update( SetConfirmationStateAssentTransformer(