From b192949d27f8016979710446b493d9a13c6d34fa Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 20 Jun 2024 17:58:31 +0500 Subject: [PATCH] Updated on 2026-08-14 --- core/res/src/main/res/values-ru/strings.xml | 3 +- core/res/src/main/res/values/strings.xml | 6 +- .../segmentedbutton/SegmentedButton.kt | 14 ++- .../ui/components/rows/SelectorRowItem.kt | 3 +- .../com/tangem/core/ui/res/TangemDimens.kt | 2 + .../src/main/java/com/tangem/utils/Strings.kt | 1 + .../presentation/ui/fee/SendSpeedSelector.kt | 2 +- .../impl/presentation/ui/send/FeeBlock.kt | 2 +- .../tokendetails/TokenDetailsPreviewData.kt | 60 ++++++++++-- .../tokendetails/state/StakingBlockState.kt | 26 ++++- .../TokenBalanceSegmentedButtonConfig.kt | 13 +++ .../state/TokenDetailsBalanceBlockState.kt | 10 ++ .../tokendetails/state/TokenDetailsState.kt | 2 +- .../TokenDetailsLoadedBalanceConverter.kt | 24 ++++- .../TokenDetailsSkeletonStateConverter.kt | 24 ++++- .../state/factory/TokenDetailsStateFactory.kt | 19 +++- .../factory/TokenStakingStateConverter.kt | 34 ++++--- .../tokendetails/ui/TokenDetailsScreen.kt | 29 +++++- .../ui/components/TokenDetailsBalanceBlock.kt | 40 +++++++- .../components/staking/StakingBalanceBlock.kt | 98 +++++++++++++++++++ .../{ => staking}/TokenStakingBlock.kt | 38 +++---- .../viewmodels/TokenDetailsClickIntents.kt | 3 + .../viewmodels/TokenDetailsViewModel.kt | 10 +- .../VisaTxHistoryItemStateConverter.kt | 3 +- 24 files changed, 387 insertions(+), 79 deletions(-) create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenBalanceSegmentedButtonConfig.kt create mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/StakingBalanceBlock.kt rename features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/{ => staking}/TokenStakingBlock.kt (86%) diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 7ad7dd2099..8c1d366c83 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -84,6 +84,7 @@ Копировать Скопировать адрес Создать + Свое Удалить Отключено Готово @@ -95,7 +96,6 @@ Обозреватель Комиссия Сетевые комиссии – это плата пользователя за обработку и подтверждение транзакций. Размер комиссии зависит от нагрузки на сеть, объема транзакции и приоритета исполнения. %s - Свое Быстро По рынку Медленно @@ -302,6 +302,7 @@ %1$d из %2$d кошельков %1$d из %2$d кошельков + Удалить например Bitcoin Ваш портфель был обновлен Выбранный токен не доступен в кошельке на данный момент. Но не переживайте, вы можете выразить свой интерес проголосовав за его добавление. diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 752c42de5a..38c7f41345 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -67,6 +67,7 @@ Not enough ADA Accept Access denied + All Allow Apply Approval @@ -84,6 +85,7 @@ Copy Copy address Create + Custom Delete Disabled Done @@ -95,7 +97,6 @@ Explorer Fee Network fees are charges users pay to process and confirm transactions. The fee amount can be affected by network congestion, transaction size, and execution priority. %s - Custom Fast Market Slow @@ -304,6 +305,7 @@ %1$d of %2$d wallet %1$d of %2$d wallets + Remove e.g. BTC I trust, hodl I must Your portfolio has been updated The selected token is currently unavailable for actions within the crypto wallet. But worry not, you can express your interest by upvoting it. @@ -562,9 +564,11 @@ On stake Reward claiming Reward schedule + Rewards to claim: %s Staking %s Unbonding Period Warmup period + Native staking Staking allow you to earn %1s. Your staking rewards arrive every ~%2s days. Earn staking rewards Rewards diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt index 0bfeb822a6..1101935641 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt @@ -8,10 +8,7 @@ import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.* @@ -21,8 +18,9 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider -import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf @@ -42,7 +40,7 @@ import kotlinx.collections.immutable.persistentListOf */ @Composable inline fun SegmentedButtons( - config: PersistentList, + config: ImmutableList, crossinline onClick: (T) -> Unit, modifier: Modifier = Modifier, color: Color = TangemTheme.colors.background.tertiary, @@ -51,7 +49,7 @@ inline fun SegmentedButtons( showIndication: Boolean = true, initialSelectedItem: T? = null, isEnabled: Boolean = true, - crossinline buttonContent: @Composable (T) -> Unit, + crossinline buttonContent: @Composable BoxScope.(T) -> Unit, ) { if (config.isEmpty() || config.size == 1) return @@ -97,7 +95,7 @@ inline fun SegmentedButtons( onClick(config[index]) }, ) { - buttonContent.invoke(config[index]) + buttonContent.invoke(this, config[index]) } } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/SelectorRowItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/SelectorRowItem.kt index c1e06e7270..5eb6b5f287 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/SelectorRowItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/SelectorRowItem.kt @@ -25,6 +25,7 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemTheme +import com.tangem.utils.Strings @Composable fun SelectorRowItem( @@ -127,7 +128,7 @@ private fun RowScope.SelectorValueContent( ) if (postDot != null) { Text( - text = "•", + text = Strings.DOT, style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.primary1, textAlign = TextAlign.Center, diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index cc51084e66..2ad32a8f6f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -102,9 +102,11 @@ data class TangemDimens internal constructor( val spacing2: Dp = 2.dp, val spacing3: Dp = 3.dp, val spacing4: Dp = 4.dp, + val spacing5: Dp = 5.dp, val spacing6: Dp = 6.dp, val spacing8: Dp = 8.dp, val spacing10: Dp = 10.dp, + val spacing11: Dp = 11.dp, val spacing12: Dp = 12.dp, val spacing14: Dp = 14.dp, val spacing15: Dp = 15.dp, diff --git a/core/utils/src/main/java/com/tangem/utils/Strings.kt b/core/utils/src/main/java/com/tangem/utils/Strings.kt index 59adbdab5b..f6cc72a41a 100644 --- a/core/utils/src/main/java/com/tangem/utils/Strings.kt +++ b/core/utils/src/main/java/com/tangem/utils/Strings.kt @@ -3,4 +3,5 @@ package com.tangem.utils object Strings { const val STARS = "\u2217\u2217\u2217" + const val DOT = "•" } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt index f403cf423b..096fbe1d8b 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt @@ -63,7 +63,7 @@ internal fun SendSpeedSelector( onSelect = { clickIntents.onFeeSelectorClick(FeeType.Fast) }, ) SendSpeedSelectorItem( - titleRes = R.string.common_fee_selector_option_custom, + titleRes = R.string.common_custom, iconRes = R.drawable.ic_edit_24, feeType = FeeType.Custom, state = state, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt index 6873ed2b63..11c4f4ebab 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt @@ -53,7 +53,7 @@ internal fun FeeBlock(feeState: SendStates.FeeState, isClickDisabled: Boolean, o FeeType.Slow -> R.string.common_fee_selector_option_slow to R.drawable.ic_tortoise_24 FeeType.Market -> R.string.common_fee_selector_option_market to R.drawable.ic_bird_24 FeeType.Fast -> R.string.common_fee_selector_option_fast to R.drawable.ic_hare_24 - FeeType.Custom -> R.string.common_fee_selector_option_custom to R.drawable.ic_edit_24 + FeeType.Custom -> R.string.common_custom to R.drawable.ic_edit_24 } } else { R.string.common_fee_selector_option_market to R.drawable.ic_bird_24 diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt index f99dbb1cd3..79b16936c9 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt @@ -9,7 +9,9 @@ import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.event.consumedEvent 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.res.TangemTheme import com.tangem.feature.tokendetails.presentation.tokendetails.state.* import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton @@ -18,6 +20,7 @@ import com.tangem.features.tokendetails.impl.R import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow +@Suppress("LargeClass") internal object TokenDetailsPreviewData { val tokenDetailsTopAppBarConfig = TokenDetailsTopAppBarConfig( @@ -100,17 +103,47 @@ internal object TokenDetailsPreviewData { TokenDetailsActionButton.Swap(dimContent = false, onClick = {}), ) - val balanceLoading = TokenDetailsBalanceBlockState.Loading(actionButtons = actionButtons) + private val balanceSegmentedButtonConfig = persistentListOf( + TokenBalanceSegmentedButtonConfig( + title = resourceReference(R.string.common_all), + type = BalanceType.ALL, + ), + TokenBalanceSegmentedButtonConfig( + title = resourceReference(R.string.staking_details_available), + type = BalanceType.AVAILABLE, + ), + ) + + val balanceLoading = TokenDetailsBalanceBlockState.Loading( + actionButtons = actionButtons, + balanceSegmentedButtonConfig = balanceSegmentedButtonConfig, + selectedBalanceType = BalanceType.ALL, + ) val balanceContent = TokenDetailsBalanceBlockState.Content( actionButtons = actionButtons, fiatBalance = "91,50$", cryptoBalance = "966,96 XLM", + isStakingEnabled = true, + balanceSegmentedButtonConfig = balanceSegmentedButtonConfig, + selectedBalanceType = BalanceType.ALL, + onBalanceSelect = {}, + ) + val balanceError = TokenDetailsBalanceBlockState.Error( + actionButtons = actionButtons, + balanceSegmentedButtonConfig = balanceSegmentedButtonConfig, + selectedBalanceType = BalanceType.ALL, ) - val balanceError = TokenDetailsBalanceBlockState.Error(actionButtons = actionButtons) private val marketPriceLoading = MarketPriceBlockState.Loading(currencySymbol = "USDT") - private val stakingLoading = StakingBlockState.Loading(iconState = iconState) + private val stakingLoading = StakingBlocksState( + stakingAvailable = StakingAvailable.Loading(iconState), + stakingBalance = StakingBalance.Content( + cryptoAmount = stringReference("5 SOL"), + fiatAmount = stringReference("456.34 $"), + rewardAmount = resourceReference(R.string.staking_details_no_rewards_to_claim, wrappedList("0.43 $")), + ), + ) private val pullToRefreshConfig = TokenDetailsPullToRefreshConfig( isRefreshing = false, @@ -246,7 +279,7 @@ internal object TokenDetailsPreviewData { tokenInfoBlockState = tokenInfoBlockState, tokenBalanceBlockState = balanceLoading, marketPriceBlockState = marketPriceLoading, - stakingBlockState = stakingLoading, + stakingBlocksState = stakingLoading, notifications = persistentListOf(), txHistoryState = TxHistoryState.Content( contentItems = MutableStateFlow( @@ -278,12 +311,19 @@ internal object TokenDetailsPreviewData { type = PriceChangeType.UP, ), ), - stakingBlockState = StakingBlockState.Content( - interestRate = "7.38", - periodInDays = 4, - tokenSymbol = "XLM", - iconState = iconState, - onStakeClicked = {}, + stakingBlocksState = StakingBlocksState( + stakingAvailable = StakingAvailable.Content( + interestRate = "7.38", + periodInDays = 4, + tokenSymbol = "XLM", + iconState = iconState, + onStakeClicked = {}, + ), + stakingBalance = StakingBalance.Content( + cryptoAmount = stringReference("5 SOL"), + fiatAmount = stringReference("456.34 $"), + rewardAmount = resourceReference(R.string.staking_details_rewards_to_claim, wrappedList("0.43 $")), + ), ), notifications = persistentListOf(), txHistoryState = TxHistoryState.NotSupported( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockState.kt index 76d9483c9b..6b5802a796 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockState.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/StakingBlockState.kt @@ -1,15 +1,20 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state import androidx.compose.runtime.Immutable +import com.tangem.core.ui.extensions.TextReference + +internal data class StakingBlocksState( + val stakingAvailable: StakingAvailable, + val stakingBalance: StakingBalance, +) @Immutable -internal sealed interface StakingBlockState { - +internal sealed interface StakingAvailable { val iconState: IconState - data class Error(override val iconState: IconState) : StakingBlockState + data class Error(override val iconState: IconState) : StakingAvailable - data class Loading(override val iconState: IconState) : StakingBlockState + data class Loading(override val iconState: IconState) : StakingAvailable data class Content( override val iconState: IconState, @@ -17,5 +22,16 @@ internal sealed interface StakingBlockState { val periodInDays: Int, val tokenSymbol: String, val onStakeClicked: () -> Unit, - ) : StakingBlockState + ) : StakingAvailable +} + +@Immutable +sealed class StakingBalance { + data object Empty : StakingBalance() + + data class Content( + val cryptoAmount: TextReference, + val fiatAmount: TextReference, + val rewardAmount: TextReference, + ) : StakingBalance() } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenBalanceSegmentedButtonConfig.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenBalanceSegmentedButtonConfig.kt new file mode 100644 index 0000000000..d741d3e609 --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenBalanceSegmentedButtonConfig.kt @@ -0,0 +1,13 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.state + +import com.tangem.core.ui.extensions.TextReference + +data class TokenBalanceSegmentedButtonConfig( + val title: TextReference, + val type: BalanceType, +) + +enum class BalanceType { + ALL, + AVAILABLE, +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsBalanceBlockState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsBalanceBlockState.kt index 2f066dae01..21ee681241 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsBalanceBlockState.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsBalanceBlockState.kt @@ -6,19 +6,29 @@ import kotlinx.collections.immutable.ImmutableList internal sealed class TokenDetailsBalanceBlockState { abstract val actionButtons: ImmutableList + abstract val balanceSegmentedButtonConfig: ImmutableList + abstract val selectedBalanceType: BalanceType data class Loading( override val actionButtons: ImmutableList, + override val balanceSegmentedButtonConfig: ImmutableList, + override val selectedBalanceType: BalanceType, ) : TokenDetailsBalanceBlockState() data class Content( override val actionButtons: ImmutableList, + override val balanceSegmentedButtonConfig: ImmutableList, + override val selectedBalanceType: BalanceType, val fiatBalance: String, val cryptoBalance: String, + val isStakingEnabled: Boolean, + val onBalanceSelect: (TokenBalanceSegmentedButtonConfig) -> Unit, ) : TokenDetailsBalanceBlockState() data class Error( override val actionButtons: ImmutableList, + override val balanceSegmentedButtonConfig: ImmutableList, + override val selectedBalanceType: BalanceType, ) : TokenDetailsBalanceBlockState() fun copyActionButtons(buttons: ImmutableList): TokenDetailsBalanceBlockState { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt index d1ec291d51..b9594669da 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt @@ -17,7 +17,7 @@ internal data class TokenDetailsState( val tokenInfoBlockState: TokenInfoBlockState, val tokenBalanceBlockState: TokenDetailsBalanceBlockState, val marketPriceBlockState: MarketPriceBlockState, - val stakingBlockState: StakingBlockState, + val stakingBlocksState: StakingBlocksState, val notifications: ImmutableList, val pendingTxs: PersistentList, val swapTxs: PersistentList, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index 63d735d3ad..87e2d83683 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -10,6 +10,7 @@ import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.feature.tokendetails.presentation.tokendetails.state.BalanceType import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification @@ -23,6 +24,7 @@ import kotlinx.collections.immutable.toPersistentList internal class TokenDetailsLoadedBalanceConverter( private val currentStateProvider: Provider, private val appCurrencyProvider: Provider, + private val isStakingEnabled: Boolean, private val symbol: String, private val decimals: Int, private val clickIntents: TokenDetailsClickIntents, @@ -39,7 +41,11 @@ internal class TokenDetailsLoadedBalanceConverter( private fun convertError(): TokenDetailsState { val state = currentStateProvider() return state.copy( - tokenBalanceBlockState = TokenDetailsBalanceBlockState.Error(state.tokenBalanceBlockState.actionButtons), + tokenBalanceBlockState = TokenDetailsBalanceBlockState.Error( + actionButtons = state.tokenBalanceBlockState.actionButtons, + balanceSegmentedButtonConfig = state.tokenBalanceBlockState.balanceSegmentedButtonConfig, + selectedBalanceType = state.tokenBalanceBlockState.selectedBalanceType, + ), marketPriceBlockState = MarketPriceBlockState.Error(state.marketPriceBlockState.currencySymbol), notifications = persistentListOf(TokenDetailsNotification.NetworksUnreachable), ) @@ -74,12 +80,24 @@ internal class TokenDetailsLoadedBalanceConverter( actionButtons = currentState.actionButtons, fiatBalance = formatFiatAmount(status.value, appCurrencyProvider()), cryptoBalance = formatCryptoAmount(status), + isStakingEnabled = isStakingEnabled, + balanceSegmentedButtonConfig = currentState.balanceSegmentedButtonConfig, + onBalanceSelect = clickIntents::onBalanceSelect, + selectedBalanceType = currentState.selectedBalanceType, + ) + is CryptoCurrencyStatus.Loading -> TokenDetailsBalanceBlockState.Loading( + actionButtons = currentState.actionButtons, + balanceSegmentedButtonConfig = currentState.balanceSegmentedButtonConfig, + selectedBalanceType = BalanceType.ALL, ) - is CryptoCurrencyStatus.Loading -> TokenDetailsBalanceBlockState.Loading(currentState.actionButtons) is CryptoCurrencyStatus.MissedDerivation, is CryptoCurrencyStatus.Unreachable, is CryptoCurrencyStatus.NoAmount, - -> TokenDetailsBalanceBlockState.Error(currentState.actionButtons) + -> TokenDetailsBalanceBlockState.Error( + actionButtons = currentState.actionButtons, + balanceSegmentedButtonConfig = currentState.balanceSegmentedButtonConfig, + selectedBalanceType = BalanceType.ALL, + ) } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt index 9cc42f3c2d..3268e37904 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt @@ -48,9 +48,16 @@ internal class TokenDetailsSkeletonStateConverter( ) }, ), - tokenBalanceBlockState = TokenDetailsBalanceBlockState.Loading(actionButtons = createButtons()), + tokenBalanceBlockState = TokenDetailsBalanceBlockState.Loading( + actionButtons = createButtons(), + balanceSegmentedButtonConfig = createBalanceSegmentedButtonConfig(), + selectedBalanceType = BalanceType.ALL, + ), marketPriceBlockState = MarketPriceBlockState.Loading(value.symbol), - stakingBlockState = StakingBlockState.Loading(iconState = iconState), + stakingBlocksState = StakingBlocksState( + stakingAvailable = StakingAvailable.Loading(iconState), + stakingBalance = StakingBalance.Empty, + ), notifications = persistentListOf(), pendingTxs = persistentListOf(), swapTxs = persistentListOf(), @@ -99,6 +106,19 @@ internal class TokenDetailsSkeletonStateConverter( ) } + private fun createBalanceSegmentedButtonConfig(): ImmutableList { + return persistentListOf( + TokenBalanceSegmentedButtonConfig( + title = resourceReference(R.string.common_all), + type = BalanceType.ALL, + ), + TokenBalanceSegmentedButtonConfig( + title = resourceReference(R.string.staking_details_available), + type = BalanceType.AVAILABLE, + ), + ) + } + private fun createPullToRefresh(): TokenDetailsPullToRefreshConfig = TokenDetailsPullToRefreshConfig( isRefreshing = false, onRefresh = clickIntents::onRefreshSwipe, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 9f8de13d25..54210912e8 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -24,9 +24,7 @@ import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryStateError -import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState -import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsAppBarMenuConfig -import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.* import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadedTxHistoryConverter import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter @@ -46,6 +44,7 @@ internal class TokenDetailsStateFactory( private val appCurrencyProvider: Provider, private val clickIntents: TokenDetailsClickIntents, private val featureToggles: TokenDetailsFeatureToggles, + private val isStakingEnabled: Boolean, symbol: String, decimals: Int, ) { @@ -65,6 +64,7 @@ internal class TokenDetailsStateFactory( TokenDetailsLoadedBalanceConverter( currentStateProvider = currentStateProvider, appCurrencyProvider = appCurrencyProvider, + isStakingEnabled = isStakingEnabled, symbol = symbol, decimals = decimals, clickIntents = clickIntents, @@ -218,7 +218,7 @@ internal class TokenDetailsStateFactory( fun getStateWithStaking(stakingEither: Either): TokenDetailsState { return currentStateProvider().copy( - stakingBlockState = stakingStateConverter.convert(stakingEither), + stakingBlocksState = stakingStateConverter.convert(stakingEither), ) } @@ -348,6 +348,17 @@ internal class TokenDetailsStateFactory( } } + fun getStateWithUpdatedBalanceSegmentedButtonConfig( + buttonConfig: TokenBalanceSegmentedButtonConfig, + ): TokenDetailsState { + return with(currentStateProvider()) { + val updatedState = (tokenBalanceBlockState as? TokenDetailsBalanceBlockState.Content) + ?.copy(selectedBalanceType = buttonConfig.type) + ?: tokenBalanceBlockState + copy(tokenBalanceBlockState = updatedState) + } + } + private fun TokenDetailsAppBarMenuConfig.updateMenu( cardTypesResolver: CardTypesResolver, isBitcoin: Boolean, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenStakingStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenStakingStateConverter.kt index 0bdf52927f..29fe830039 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenStakingStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenStakingStateConverter.kt @@ -3,7 +3,9 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory import arrow.core.Either import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.staking.model.StakingEntryInfo -import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingAvailable +import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBalance +import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlocksState import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.utils.Provider @@ -12,25 +14,31 @@ import com.tangem.utils.converter.Converter internal class TokenStakingStateConverter( private val clickIntents: TokenDetailsClickIntents, private val currentStateProvider: Provider, -) : Converter, StakingBlockState> { +) : Converter, StakingBlocksState> { - override fun convert(value: Either): StakingBlockState { + override fun convert(value: Either): StakingBlocksState { value.fold( ifLeft = { - return StakingBlockState.Error( - iconState = currentStateProvider().tokenInfoBlockState.iconState, + return StakingBlocksState( + stakingAvailable = StakingAvailable.Error( + iconState = currentStateProvider().tokenInfoBlockState.iconState, + ), + stakingBalance = StakingBalance.Empty, ) }, ifRight = { - return StakingBlockState.Content( - interestRate = BigDecimalFormatter.formatPercent( - percent = it.interestRate, - useAbsoluteValue = true, + return StakingBlocksState( + stakingAvailable = StakingAvailable.Content( + interestRate = BigDecimalFormatter.formatPercent( + percent = it.interestRate, + useAbsoluteValue = true, + ), + periodInDays = it.periodInDays, + tokenSymbol = it.tokenSymbol, + iconState = currentStateProvider().tokenInfoBlockState.iconState, + onStakeClicked = clickIntents::onStakeBannerClick, ), - periodInDays = it.periodInDays, - tokenSymbol = it.tokenSymbol, - iconState = currentStateProvider().tokenInfoBlockState.iconState, - onStakeClicked = clickIntents::onStakeBannerClick, + stakingBalance = StakingBalance.Empty, ) }, ) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt index e4574897ac..f076f05240 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt @@ -41,10 +41,10 @@ import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData -import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingAvailable +import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBalance import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification -import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.* import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsBalanceBlock import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsDialogs import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsTopAppBar @@ -52,6 +52,8 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.T import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.ExchangeStatusBottomSheet import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.ExchangeStatusBottomSheetConfig import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.swapTransactionsItems +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking.StakingBalanceBlock +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking.TokenStakingBlock // TODO: Split to blocks [REDACTED_JIRA] @Suppress("LongMethod") @@ -140,10 +142,27 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) { if (state.isStakingBlockShown) { item( - key = StakingBlockState::class.java, - contentType = StakingBlockState::class.java, - content = { TokenStakingBlock(modifier = itemModifier, state = state.stakingBlockState) }, + key = StakingAvailable::class.java, + contentType = StakingAvailable::class.java, + content = { + TokenStakingBlock( + modifier = itemModifier, + state = state.stakingBlocksState.stakingAvailable, + ) + }, ) + if (state.stakingBlocksState.stakingBalance is StakingBalance.Content) { + item( + key = StakingBalance::class.java, + contentType = StakingBalance::class.java, + content = { + StakingBalanceBlock( + state = state.stakingBlocksState.stakingBalance, + modifier = itemModifier, + ) + }, + ) + } } swapTransactionsItems( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt index 06d871b579..081fab085e 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt @@ -13,6 +13,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.buttons.HorizontalActionChips +import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons +import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.utils.BigDecimalFormatter @@ -35,20 +37,23 @@ internal fun TokenDetailsBalanceBlock( color = TangemTheme.colors.background.primary, ) { Column { - Box( + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier - .padding(top = TangemTheme.dimens.spacing12) .padding(horizontal = TangemTheme.dimens.spacing12) .fillMaxWidth() .heightIn(min = TangemTheme.dimens.spacing24), - contentAlignment = Alignment.CenterStart, ) { Text( text = stringResource(id = R.string.common_balance_title), color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.subtitle2, maxLines = 1, + modifier = Modifier + .weight(1f) + .padding(top = TangemTheme.dimens.spacing12), ) + BalanceButtons(state) } FiatBalance( state = state, @@ -130,6 +135,35 @@ private fun CryptoBalance( } } +@Composable +private fun BalanceButtons(state: TokenDetailsBalanceBlockState) { + if (state !is TokenDetailsBalanceBlockState.Content) return + + SegmentedButtons( + config = state.balanceSegmentedButtonConfig, + onClick = state.onBalanceSelect, + showIndication = false, + modifier = Modifier + .padding(top = TangemTheme.dimens.spacing11) + .width(IntrinsicSize.Min), + ) { config -> + Text( + text = config.title.resolveReference(), + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.caption1, + maxLines = 1, + modifier = Modifier + .padding( + start = TangemTheme.dimens.spacing5, + end = TangemTheme.dimens.spacing5, + top = TangemTheme.dimens.spacing3, + bottom = TangemTheme.dimens.spacing3, + ) + .align(Alignment.Center), + ) + } +} + @Preview(widthDp = 328, heightDp = 152) @Preview(widthDp = 328, heightDp = 152, uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/StakingBalanceBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/StakingBalanceBlock.kt new file mode 100644 index 0000000000..4a2929a40d --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/StakingBalanceBlock.kt @@ -0,0 +1,98 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +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.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData +import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBalance +import com.tangem.features.tokendetails.impl.R +import com.tangem.utils.Strings + +@Composable +fun StakingBalanceBlock(state: StakingBalance.Content, modifier: Modifier = Modifier) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + .fillMaxWidth() + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(TangemTheme.colors.background.action) + .padding(TangemTheme.dimens.spacing12), + ) { + Column( + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4), + modifier = Modifier.weight(1f), + ) { + Text( + text = stringResource(R.string.staking_native), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing2), + ) + Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { + Text( + text = state.fiatAmount.resolveReference(), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + ) + Text( + text = Strings.DOT, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + ) + Text( + text = state.cryptoAmount.resolveReference(), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.tertiary, + ) + } + Text( + text = state.rewardAmount.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + Icon( + painter = painterResource(id = R.drawable.ic_chevron_right_24), + contentDescription = null, + tint = TangemTheme.colors.icon.informative, + ) + } +} + +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun StakingBalanceBlock_Preview( + @PreviewParameter(StakingBalanceBlockPreviewProvider::class) data: StakingBalance, +) { + TangemThemePreview { + StakingBalanceBlock( + state = data as StakingBalance.Content, + modifier = Modifier.padding(TangemTheme.dimens.spacing16), + ) + } +} + +private class StakingBalanceBlockPreviewProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + TokenDetailsPreviewData.tokenDetailsState_1.stakingBlocksState.stakingBalance, + TokenDetailsPreviewData.tokenDetailsState_2.stakingBlocksState.stakingBalance, + ) +} +// endregion \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenStakingBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/TokenStakingBlock.kt similarity index 86% rename from features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenStakingBlock.kt rename to features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/TokenStakingBlock.kt index d888903a28..e9f66e62ab 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenStakingBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/staking/TokenStakingBlock.kt @@ -1,11 +1,12 @@ -package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components +package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking import android.content.res.Configuration import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.material.Text -import androidx.compose.runtime.* +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -14,14 +15,17 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider -import com.tangem.core.ui.components.* -import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.components.RectangleShimmer +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.feature.tokendetails.presentation.tokendetails.state.IconState -import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingBlockState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.StakingAvailable +import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.CurrencyIcon import com.tangem.features.tokendetails.impl.R /** @@ -31,7 +35,7 @@ import com.tangem.features.tokendetails.impl.R * @param modifier modifier */ @Composable -internal fun TokenStakingBlock(state: StakingBlockState, modifier: Modifier = Modifier) { +internal fun TokenStakingBlock(state: StakingAvailable, modifier: Modifier = Modifier) { Column( modifier = modifier .background( @@ -49,7 +53,7 @@ internal fun TokenStakingBlock(state: StakingBlockState, modifier: Modifier = Mo } @Composable -private fun Content(state: StakingBlockState, modifier: Modifier = Modifier) { +private fun Content(state: StakingAvailable, modifier: Modifier = Modifier) { AnimatedContent( modifier = modifier.heightIn(min = TangemTheme.dimens.size60), targetState = state, @@ -57,24 +61,24 @@ private fun Content(state: StakingBlockState, modifier: Modifier = Modifier) { label = "Update the content", ) { stakingBlockState -> when (stakingBlockState) { - is StakingBlockState.Content -> { + is StakingAvailable.Content -> { StakingContent( stakingBlockState = stakingBlockState, iconState = stakingBlockState.iconState, ) } - is StakingBlockState.Loading -> { + is StakingAvailable.Loading -> { StakingLoading( iconState = stakingBlockState.iconState, ) } - is StakingBlockState.Error -> Row {} // TODO staking + is StakingAvailable.Error -> Row {} // TODO staking } } } @Composable -private fun StakingContent(stakingBlockState: StakingBlockState.Content, iconState: IconState) { +private fun StakingContent(stakingBlockState: StakingAvailable.Content, iconState: IconState) { Column { Row { val (alpha, colorFilter) = remember(iconState.isGrayscale) { @@ -177,24 +181,24 @@ private fun StakingLoading(iconState: IconState) { @Composable private fun Preview_TokenStakingBlock( @PreviewParameter(StakingBlockStateProvider::class) - state: StakingBlockState, + state: StakingAvailable, ) { TangemThemePreview { TokenStakingBlock(state = state) } } -private class StakingBlockStateProvider : CollectionPreviewParameterProvider( +private class StakingBlockStateProvider : CollectionPreviewParameterProvider( collection = listOf( - StakingBlockState.Content( + StakingAvailable.Content( iconState = iconState, interestRate = "10", periodInDays = 4, tokenSymbol = "SOL", onStakeClicked = {}, ), - StakingBlockState.Loading(iconState = iconState), - StakingBlockState.Error(iconState = iconState), + StakingAvailable.Loading(iconState = iconState), + StakingAvailable.Error(iconState = iconState), ), ) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt index 51b5adda0c..9332738ab0 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsClickIntents.kt @@ -4,6 +4,7 @@ import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason +import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig @Suppress("TooManyFunctions") interface TokenDetailsClickIntents { @@ -59,4 +60,6 @@ interface TokenDetailsClickIntents { fun onAssociateClick() fun onStakeBannerClick() + + fun onBalanceSelect(config: TokenBalanceSegmentedButtonConfig) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 09a908e65f..333fdc48e6 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -63,6 +63,7 @@ import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRout import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsCurrencyStatusAnalyticsSender import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsNotificationsAnalyticsSender import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.ExchangeStatusBottomSheetConfig @@ -125,12 +126,12 @@ internal class TokenDetailsViewModel @Inject constructor( ) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents { private val userWalletId: UserWalletId = savedStateHandle.get(AppRoute.CurrencyDetails.USER_WALLET_ID_KEY) - ?.let { it.unbundle(UserWalletId.serializer()) } + ?.unbundle(UserWalletId.serializer()) ?: error("This screen can't open without `UserWalletId`") private val cryptoCurrency: CryptoCurrency = savedStateHandle.get(AppRoute.CurrencyDetails.CRYPTO_CURRENCY_KEY) - ?.let { it.unbundle(CryptoCurrency.serializer()) } + ?.unbundle(CryptoCurrency.serializer()) ?: error("This screen can't open without `CryptoCurrency`") private val userWallet: UserWallet @@ -154,6 +155,7 @@ internal class TokenDetailsViewModel @Inject constructor( symbol = cryptoCurrency.symbol, decimals = cryptoCurrency.decimals, featureToggles = tokenDetailsFeatureToggles, + isStakingEnabled = stakingFeatureToggles.isStakingEnabled, ) private val exchangeStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) { @@ -787,6 +789,10 @@ internal class TokenDetailsViewModel @Inject constructor( } } + override fun onBalanceSelect(config: TokenBalanceSegmentedButtonConfig) { + uiState = stateFactory.getStateWithUpdatedBalanceSegmentedButtonConfig(config) + } + private fun handleUnavailabilityReason(unavailabilityReason: ScenarioUnavailabilityReason): Boolean { if (unavailabilityReason == ScenarioUnavailabilityReason.None) return false diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxHistoryItemStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxHistoryItemStateConverter.kt index 5c1b0a20c5..819d0d20d1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxHistoryItemStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxHistoryItemStateConverter.kt @@ -9,6 +9,7 @@ import com.tangem.domain.visa.model.VisaCurrency import com.tangem.domain.visa.model.VisaTxHistoryItem import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.VisaWalletIntents +import com.tangem.utils.Strings import com.tangem.utils.converter.Converter import org.joda.time.DateTimeZone @@ -20,7 +21,7 @@ internal class VisaTxHistoryItemStateConverter( override fun convert(value: VisaTxHistoryItem): TransactionState { val localDate = value.date.withZone(DateTimeZone.getDefault()) val time = DateTimeFormatters.formatDate(localDate, DateTimeFormatters.timeFormatter) - val subtitle = "$time • ${value.status.capitalize()}" + val subtitle = "$time ${Strings.DOT} ${value.status.capitalize()}" return TransactionState.Content( txHash = value.id,