Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-27 18:13:45 +05:00
parent 6cfb8213f0
commit fe935fdba0
16 changed files with 933 additions and 572 deletions

View file

@ -201,6 +201,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
override fun onAccountExpandClick(account: Account) {
analyticsEventHandler.send(MainScreenAnalyticsEvent.AccountShowTokens())
accountDependencies.expandedAccountsHolder.expandAccount(account.accountId)
walletEventSender.send(WalletEvent.CollapseBalance)
}
override fun onAccountCollapseClick(account: Account) {

View file

@ -2,243 +2,97 @@ package com.tangem.feature.wallet.presentation.common.preview
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
import com.tangem.core.ui.components.token.AccountItemPreviewData
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.ds.button.TangemButtonType
import com.tangem.core.ui.ds.button.TangemButtonUM
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
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.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.WalletPreviewDataLegacy.topBarConfig
import com.tangem.feature.wallet.presentation.preview.WalletBalancePreview
import com.tangem.feature.wallet.presentation.preview.WalletPreviewData
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.utils.StringsSigns.DASH_SIGN
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
internal object WalletScreenPreviewData {
private val tokenItemState = TokenItemState.Content(
private val tokenRowDefault = TangemTokenRowUM.Content(
id = "1",
iconState = CurrencyIconState.Locked,
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Bitcoin")),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "12 368,14 \$"),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "0,35853044 BTC"),
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
price = "34 496,75 \$",
priceChangePercent = "0,43 %",
type = PriceChangeType.DOWN,
headIconUM = TangemIconUM.Currency(CurrencyIconState.Loading),
titleUM = TangemTokenRowUM.TitleUM.Content(
text = stringReference("Bitcoin"),
),
subtitleUM = TangemTokenRowUM.SubtitleUM.Content(
text = stringReference("Bitcoin"),
),
topEndContentUM = TangemTokenRowUM.EndContentUM.Content(
text = stringReference("1 234,56 \$"),
),
bottomEndContentUM = TangemTokenRowUM.EndContentUM.Content(
text = stringReference("0,12345678 BTC"),
),
promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty,
tailUM = TangemTokenRowUM.TailUM.Empty,
onItemClick = {},
onItemLongClick = {},
)
private val textContentTokensState = WalletTokensListState.ContentState.Content(
items = persistentListOf(
TokensListItemUM.GroupTitle(id = 1, text = stringReference("Network Bitcoin")),
TokensListItemUM.Token(state = tokenItemState),
TokensListItemUM.GroupTitle(id = 2, text = stringReference("Network Ethereum")),
TokensListItemUM.Token(
state = tokenItemState.copy(
id = "2",
titleState = TokenItemState.TitleState.Content(text = stringReference("Ethereum")),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "3 340,79 \$"),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "1,856660295 ETH"),
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
price = "1 799,41 \$",
priceChangePercent = "5,16 %",
type = PriceChangeType.UP,
),
),
),
TokensListItemUM.Token(
state = TokenItemState.Unreachable(
id = "3",
iconState = CurrencyIconState.Locked,
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Polygon")),
onItemClick = {},
onItemLongClick = {},
),
),
TokensListItemUM.Token(
state = tokenItemState.copy(
id = "4",
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Shiba Inu")),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "48,64 \$"),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "6 200 220,00 SHIB"),
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
price = "0.01 \$",
priceChangePercent = "1,34 %",
type = PriceChangeType.DOWN,
),
),
),
private val tokenListDefault = WalletTokensListUM.Content(
tokenList = persistentListOf(
TokensListItemUM2.Token(tokenRowDefault.copy(id = "0")),
TokensListItemUM2.Token(tokenRowDefault.copy(id = "1")),
TokensListItemUM2.Token(tokenRowDefault.copy(id = "2")),
),
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
isEnabled = true,
organizeButtonUM = TangemButtonUM(
text = resourceReference(R.string.organize_tokens_title),
type = TangemButtonType.Secondary,
onClick = {},
),
)
private val portfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
items = persistentListOf(
TokensListItemUM.Portfolio(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
isExpanded = false,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem
.copy(iconState = AccountItemPreviewData.accountLetterIcon),
),
TokensListItemUM.Portfolio(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
isExpanded = true,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem,
),
),
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
isEnabled = true,
onClick = {},
),
private val walletLocked = WalletUM.Locked(
walletsBalanceUM = WalletBalancePreview.content,
buttons = WalletPreviewData.actionButtons,
type = WalletType.Cold,
notifications = persistentListOf(),
)
private val emptyPortfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
items = persistentListOf(
TokensListItemUM.Portfolio(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
isExpanded = false,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem
.copy(iconState = AccountItemPreviewData.accountLetterIcon),
),
TokensListItemUM.Portfolio(
tokens = persistentListOf(),
isExpanded = true,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem,
),
private val walletDefault = WalletUM.Content(
walletsBalanceUM = WalletBalancePreview.content,
buttons = WalletPreviewData.actionButtons,
type = WalletType.Cold,
pullToRefreshConfig = PullToRefreshConfig(
isRefreshing = false,
onRefresh = {},
),
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
isEnabled = true,
onClick = {},
notifications = persistentListOf(),
notificationsCarousel = persistentListOf(),
tokensListUM = tokenListDefault,
nftState = WalletNFTItemUM.Content(
previews = persistentListOf(),
collectionsCount = 0,
allAssetsCount = 0,
noCollectionAssetsCount = 0,
isFlickering = false,
onItemClick = {},
),
tangemPayState = TangemPayState.Loading,
)
private val noteLockedCard by lazy {
WalletCardState.LockedContent(
id = UserWalletId(stringValue = "1"),
title = "Note",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("Locked"),
),
imageResId = R.drawable.ill_note_btc_120_106,
dropDownItems = persistentListOf(),
)
}
private val miltiUnreachableCard by lazy {
WalletCardState.Content(
id = UserWalletId(stringValue = "2"),
title = "Wallet 1",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("Seed phrase"),
),
imageResId = R.drawable.ill_wallet2_cards3_120_106,
cardCount = 3,
balance = DASH_SIGN,
dropDownItems = persistentListOf(),
isZeroBalance = false,
isBalanceFlickering = false,
)
}
private val multiWalletState by lazy {
WalletState.MultiCurrency.Content(
pullToRefreshConfig = PullToRefreshConfig(
isRefreshing = false,
onRefresh = {},
),
walletCardState = miltiUnreachableCard,
buttons = persistentListOf(buyButton),
warnings = persistentListOf(
WalletNotification.Warning.SomeNetworksUnreachable,
WalletNotification.FinishWalletActivation(
type = WalletActivationBannerType.Attention,
buttonsState = ButtonsState.SecondaryButtonConfig(
text = resourceReference(R.string.hw_activation_need_finish),
onClick = { },
),
isBackupExists = false,
),
),
bottomSheetConfig = null,
tokensListState = textContentTokensState,
nftState = WalletNFTItemUM.Content(
previews = persistentListOf(WalletNFTItemUM.Content.CollectionPreview.Image("img1")),
collectionsCount = 1,
allAssetsCount = 3,
noCollectionAssetsCount = 0,
isFlickering = false,
onItemClick = { },
),
tangemPayState = TangemPayState.Empty,
type = WalletType.Cold,
)
}
private val buyButton = WalletManageButton.Buy(enabled = false, dimContent = true, onClick = {})
private val sendButton = WalletManageButton.Send(enabled = false, dimContent = true, onClick = {})
private val receiveButton = WalletManageButton.Receive(
enabled = false,
dimContent = true,
onClick = {},
onLongClick = null,
)
private val singleWalletLockedState = WalletState.SingleCurrency.Locked(
walletCardState = noteLockedCard,
buttons = persistentListOf(
buyButton,
sendButton,
receiveButton,
),
bottomSheetConfig = null,
onUnlockNotificationClick = {},
onExploreClick = {},
)
internal val walletScreenState = WalletScreenState(
internal val defaultState = WalletScreenState(
topBarConfig = topBarConfig,
selectedWalletIndex = 0,
wallets = persistentListOf(
singleWalletLockedState,
multiWalletState,
wallets = persistentListOf(),
wallets2 = persistentListOf(
walletLocked,
walletDefault,
),
wallets2 = persistentListOf(),
onWalletChange = { _, _ -> },
event = consumedEvent(),
isHidingMode = false,
showMarketsOnboarding = false,
onDismissMarketsTooltip = {},
)
internal val accountScreenState =
walletScreenState.copy(
wallets = persistentListOf(
singleWalletLockedState,
multiWalletState.copy(tokensListState = portfolioContentState),
),
)
internal val accountScreenWithEmptyTokensState =
walletScreenState.copy(
wallets = persistentListOf(
singleWalletLockedState,
multiWalletState.copy(tokensListState = emptyPortfolioContentState),
),
)
}

View file

@ -0,0 +1,245 @@
package com.tangem.feature.wallet.presentation.common.preview
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
import com.tangem.core.ui.components.token.AccountItemPreviewData
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
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.domain.models.wallet.UserWalletId
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.WalletPreviewDataLegacy.topBarConfig
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.utils.StringsSigns.DASH_SIGN
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
internal object WalletScreenPreviewDataLegacy {
private val buyButton = WalletManageButton.Buy(enabled = false, dimContent = true, onClick = {})
private val sendButton = WalletManageButton.Send(enabled = false, dimContent = true, onClick = {})
private val receiveButton = WalletManageButton.Receive(
enabled = false,
dimContent = true,
onClick = {},
onLongClick = null,
)
private val tokenItemState = TokenItemState.Content(
id = "1",
iconState = CurrencyIconState.Locked,
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Bitcoin")),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "12 368,14 \$"),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "0,35853044 BTC"),
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
price = "34 496,75 \$",
priceChangePercent = "0,43 %",
type = PriceChangeType.DOWN,
),
onItemClick = {},
onItemLongClick = {},
)
private val textContentTokensState = WalletTokensListState.ContentState.Content(
items = persistentListOf(
TokensListItemUM.GroupTitle(id = 1, text = stringReference("Network Bitcoin")),
TokensListItemUM.Token(state = tokenItemState),
TokensListItemUM.GroupTitle(id = 2, text = stringReference("Network Ethereum")),
TokensListItemUM.Token(
state = tokenItemState.copy(
id = "2",
titleState = TokenItemState.TitleState.Content(text = stringReference("Ethereum")),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "3 340,79 \$"),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "1,856660295 ETH"),
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
price = "1 799,41 \$",
priceChangePercent = "5,16 %",
type = PriceChangeType.UP,
),
),
),
TokensListItemUM.Token(
state = TokenItemState.Unreachable(
id = "3",
iconState = CurrencyIconState.Locked,
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Polygon")),
onItemClick = {},
onItemLongClick = {},
),
),
TokensListItemUM.Token(
state = tokenItemState.copy(
id = "4",
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Shiba Inu")),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "48,64 \$"),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "6 200 220,00 SHIB"),
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
price = "0.01 \$",
priceChangePercent = "1,34 %",
type = PriceChangeType.DOWN,
),
),
),
),
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
isEnabled = true,
onClick = {},
),
)
private val portfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
items = persistentListOf(
TokensListItemUM.Portfolio(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
isExpanded = false,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem
.copy(iconState = AccountItemPreviewData.accountLetterIcon),
),
TokensListItemUM.Portfolio(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
isExpanded = true,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem,
),
),
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
isEnabled = true,
onClick = {},
),
)
private val emptyPortfolioContentState = WalletTokensListState.ContentState.PortfolioContent(
items = persistentListOf(
TokensListItemUM.Portfolio(
tokens = textContentTokensState.items.filterIsInstance<PortfolioTokensListItemUM>().toPersistentList(),
isExpanded = false,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem
.copy(iconState = AccountItemPreviewData.accountLetterIcon),
),
TokensListItemUM.Portfolio(
tokens = persistentListOf(),
isExpanded = true,
isCollapsable = true,
tokenItemUM = AccountItemPreviewData.accountItem,
),
),
organizeTokensButtonConfig = WalletTokensListState.OrganizeTokensButtonConfig(
isEnabled = true,
onClick = {},
),
)
private val noteLockedCard by lazy {
WalletCardState.LockedContent(
id = UserWalletId(stringValue = "1"),
title = "Note",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("Locked"),
),
imageResId = R.drawable.ill_note_btc_120_106,
dropDownItems = persistentListOf(),
)
}
private val miltiUnreachableCard by lazy {
WalletCardState.Content(
id = UserWalletId(stringValue = "2"),
title = "Wallet 1",
additionalInfo = WalletAdditionalInfo(
hideable = false,
content = TextReference.Str("Seed phrase"),
),
imageResId = R.drawable.ill_wallet2_cards3_120_106,
cardCount = 3,
balance = DASH_SIGN,
dropDownItems = persistentListOf(),
isZeroBalance = false,
isBalanceFlickering = false,
)
}
private val multiWalletState by lazy {
WalletState.MultiCurrency.Content(
pullToRefreshConfig = PullToRefreshConfig(
isRefreshing = false,
onRefresh = {},
),
walletCardState = miltiUnreachableCard,
buttons = persistentListOf(buyButton),
warnings = persistentListOf(
WalletNotification.Warning.SomeNetworksUnreachable,
WalletNotification.FinishWalletActivation(
type = WalletActivationBannerType.Attention,
buttonsState = ButtonsState.SecondaryButtonConfig(
text = resourceReference(R.string.hw_activation_need_finish),
onClick = { },
),
isBackupExists = false,
),
),
bottomSheetConfig = null,
tokensListState = textContentTokensState,
nftState = WalletNFTItemUM.Content(
previews = persistentListOf(WalletNFTItemUM.Content.CollectionPreview.Image("img1")),
collectionsCount = 1,
allAssetsCount = 3,
noCollectionAssetsCount = 0,
isFlickering = false,
onItemClick = { },
),
tangemPayState = TangemPayState.Empty,
type = WalletType.Cold,
)
}
private val singleWalletLockedState = WalletState.SingleCurrency.Locked(
walletCardState = noteLockedCard,
buttons = persistentListOf(
buyButton,
sendButton,
receiveButton,
),
bottomSheetConfig = null,
onUnlockNotificationClick = {},
onExploreClick = {},
)
internal val walletScreenState = WalletScreenState(
topBarConfig = topBarConfig,
selectedWalletIndex = 0,
wallets = persistentListOf(
singleWalletLockedState,
multiWalletState,
),
wallets2 = persistentListOf(),
onWalletChange = { _, _ -> },
event = consumedEvent(),
isHidingMode = false,
showMarketsOnboarding = false,
onDismissMarketsTooltip = {},
)
internal val accountScreenState =
walletScreenState.copy(
wallets = persistentListOf(
singleWalletLockedState,
multiWalletState.copy(tokensListState = portfolioContentState),
),
)
internal val accountScreenWithEmptyTokensState =
walletScreenState.copy(
wallets = persistentListOf(
singleWalletLockedState,
multiWalletState.copy(tokensListState = emptyPortfolioContentState),
),
)
}

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.preview
import androidx.compose.ui.text.SpanStyle
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.styledStringReference
@ -12,6 +13,14 @@ internal object WalletBalancePreview {
val content: WalletBalanceUM.Content = WalletBalanceUM.Content(
id = UserWalletId("0"),
name = "My Wallet",
balanceInAppBar = combinedReference(
stringReference("1,234"),
styledStringReference(
".56",
{ SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) },
),
stringReference(" $"),
),
balance = combinedReference(
stringReference("1,234"),
styledStringReference(

View file

@ -35,6 +35,7 @@ internal sealed interface WalletBalanceUM {
override val id: UserWalletId,
override val name: String,
val balance: TextReference,
val balanceInAppBar: TextReference,
val isBalanceFlickering: Boolean,
val isZeroBalance: Boolean?,
) : WalletBalanceUM

View file

@ -31,4 +31,6 @@ internal sealed class WalletEvent {
val onAllow: () -> Unit,
val onDeny: () -> Unit,
) : WalletEvent()
data object CollapseBalance : WalletEvent()
}

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers
import androidx.compose.ui.text.SpanStyle
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.formatStyled
@ -90,6 +91,13 @@ internal class SetTokenListErrorTransformer(
return WalletBalanceUM.Content(
id = id,
name = name,
balanceInAppBar = BigDecimal.ZERO.formatStyled {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) },
)
},
balance = BigDecimal.ZERO.formatStyled {
fiat(
fiatCurrencyCode = appCurrency.code,

View file

@ -10,7 +10,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.transformers.converte
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletUM
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCardStateConverter
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TokenListStateConverter
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.WalletTokensListUMTransformer
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.WalletTokensListUMConverter
import com.tangem.feature.wallet.presentation.wallet.state.utils.enableButtons
import timber.log.Timber
import java.math.BigDecimal
@ -101,7 +101,7 @@ internal class SetTokenListTransformer(
private fun toLoadedState(): WalletTokensListUM {
if (params !is TokenConverterParams.Account) return WalletTokensListUM.Empty
return WalletTokensListUMTransformer(
return WalletTokensListUMConverter(
selectedWallet = userWallet,
appCurrency = appCurrency,
clickIntents = clickIntents,

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
import androidx.compose.ui.text.SpanStyle
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.formatStyled
import com.tangem.core.ui.res.TangemTheme
@ -41,6 +42,13 @@ internal class MultiWalletBalanceUMTransformer(
return WalletBalanceUM.Content(
id = id,
name = name,
balanceInAppBar = fiatBalance.amount.formatStyled {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) },
)
},
balance = fiatBalance.amount.formatStyled {
fiat(
fiatCurrencyCode = appCurrency.code,

View file

@ -0,0 +1,130 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
import androidx.compose.ui.text.SpanStyle
import com.tangem.common.ui.account.AccountIconItemStateConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.ui.components.account.AccountIconSize
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.core.ui.extensions.pluralReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.styledResourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.formatStyled
import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.quote.PriceChange
import com.tangem.feature.wallet.impl.R
import com.tangem.utils.StringsSigns
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.persistentListOf
internal class WalletTokenAccountItemConverter(
private val appCurrency: AppCurrency,
private val expandedAccounts: Set<AccountId>,
private val onAccountCollapseClick: (account: Account) -> Unit,
private val onAccountExpandClick: (account: Account) -> Unit,
) : Converter<AccountStatus.CryptoPortfolio, TangemTokenRowUM> {
override fun convert(value: AccountStatus.CryptoPortfolio): TangemTokenRowUM {
val account = value.account
val isExpanded = expandedAccounts.contains(account.accountId)
return TangemTokenRowUM.Content(
id = account.accountId.value,
headIconUM = TangemIconUM.Currency(
currencyIconState = AccountIconItemStateConverter(size = AccountIconSize.ExtraSmall).convert(account),
),
titleUM = TangemTokenRowUM.TitleUM.Content(
text = account.accountName.toUM().value,
),
subtitleUM = TangemTokenRowUM.SubtitleUM.Content(
text = pluralReference(
R.plurals.common_tokens_count,
count = account.tokensCount,
formatArgs = wrappedList(account.tokensCount),
),
),
topEndContentUM = getTopEndContent(value.tokenList.totalFiatBalance),
bottomEndContentUM = getBottomEndContent(
value.tokenList.totalFiatBalance,
value.priceChangeLce.getOrNull(),
),
onItemClick = {
if (isExpanded) {
onAccountCollapseClick(account)
} else {
onAccountExpandClick(account)
}
},
onItemLongClick = null,
)
}
private fun getTopEndContent(accountBalance: TotalFiatBalance): TangemTokenRowUM.EndContentUM {
return when (accountBalance) {
TotalFiatBalance.Failed -> TangemTokenRowUM.EndContentUM.Content(
text = stringReference(StringsSigns.DASH_SIGN),
)
is TotalFiatBalance.Loaded -> TangemTokenRowUM.EndContentUM.Content(
text = accountBalance.amount.formatStyled {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) },
)
},
)
TotalFiatBalance.Loading -> TangemTokenRowUM.EndContentUM.Loading
}
}
private fun getBottomEndContent(
accountBalance: TotalFiatBalance,
priceChange: PriceChange?,
): TangemTokenRowUM.EndContentUM {
return when (accountBalance) {
TotalFiatBalance.Failed -> TangemTokenRowUM.EndContentUM.Content(
text = styledResourceReference(
id = R.string.common_unreachable,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.status.attention) },
),
endIcons = persistentListOf(
TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.status.attention },
),
),
)
is TotalFiatBalance.Loaded -> if (priceChange != null) {
val priceChangeType = PriceChangeType.fromBigDecimal(priceChange.value)
TangemTokenRowUM.EndContentUM.Content(
text = stringReference(
priceChange.value.format {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
},
),
priceChangeUM = PriceChangeState.Content(
type = priceChangeType,
valueInPercent = priceChange.value.format { percent() },
),
)
} else {
TangemTokenRowUM.EndContentUM.Empty
}
TotalFiatBalance.Loading -> TangemTokenRowUM.EndContentUM.Loading
}
}
}

View file

@ -3,252 +3,50 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert
import androidx.compose.ui.text.SpanStyle
import com.tangem.common.getTotalCryptoAmount
import com.tangem.common.getTotalFiatAmount
import com.tangem.common.ui.account.AccountIconItemStateConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.ui.components.account.AccountIconSize
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.ds.badge.*
import com.tangem.core.ui.ds.button.TangemButtonShape
import com.tangem.core.ui.ds.button.TangemButtonSize
import com.tangem.core.ui.ds.button.TangemButtonType
import com.tangem.core.ui.ds.button.TangemButtonUM
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.header.TangemHeaderRowUM
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM.EndContentUM
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.styledResourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.*
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.tokenlist.TokenList
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.child.wallet.model.intents.WalletContentClickIntents
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TokensListItemUM2
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListUM
import com.tangem.utils.StringsSigns
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.addIf
import com.tangem.utils.extensions.orZero
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.toPersistentList
import java.math.BigDecimal
@Suppress("LargeClass", "LongParameterList")
internal class WalletTokensListUMTransformer(
internal class WalletTokenCurrencyItemConverter(
private val appCurrency: AppCurrency,
private val selectedWallet: UserWallet,
private val clickIntents: WalletClickIntents,
private val yieldModuleApyMap: Map<String, BigDecimal>,
private val isAccountsModeEnabled: Boolean,
private val expandedAccounts: Set<AccountId>,
private val clickIntents: WalletContentClickIntents,
stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability>,
shouldShowMainPromo: Boolean,
) : Converter<AccountStatusList, WalletTokensListUM> {
) : Converter<Pair<CryptoCurrencyStatus, Boolean>, TangemTokenRowUM> {
private val yieldSupplyPromoBannerConverter = YieldSupplyPromoBannerConverter(
yieldModuleApyMap,
shouldShowMainPromo,
)
private val currencyToIconStateConverter = CryptoCurrencyToIconStateConverter()
private val earnApyConverter = EarnApyConverter(
yieldModuleApyMap = yieldModuleApyMap,
stakingApyMap = stakingAvailabilityMap,
)
override fun convert(value: AccountStatusList): WalletTokensListUM {
val promoCryptoCurrency = yieldSupplyPromoBannerConverter.convert2(value = value)
return if (value.accountStatuses.isEmpty()) {
WalletTokensListUM.Empty
} else {
val isCollapsable = value.accountStatuses.count {
it is AccountStatus.CryptoPortfolio && it.account.tokensCount > 0
} > 1
val tokenListUM = value.accountStatuses
.filterIsInstance<AccountStatus.CryptoPortfolio>()
.asSequence()
.flatMap { accountStatus ->
if (isAccountsModeEnabled) {
val isExpanded = expandedAccounts.contains(accountStatus.account.accountId)
sequenceOf(
TokensListItemUM2.Portfolio(
tokenRowUM = toAccountRow(accountStatus, isExpanded),
isExpanded = isExpanded || !isCollapsable,
isCollapsable = isCollapsable,
tokenList = getTokenListItems(
accountStatus.tokenList,
promoCryptoCurrency,
).toPersistentList(),
),
)
} else {
getTokenListItems(accountStatus.tokenList, promoCryptoCurrency)
}
}.toPersistentList()
WalletTokensListUM.Content(
tokenList = tokenListUM,
organizeButtonUM = getOrganizeButtonUM(value),
)
}
}
private fun getTokenListItems(
tokenList: TokenList,
promoCryptoCurrency: CryptoCurrencyStatus?,
): Sequence<TokensListItemUM2> {
return when (tokenList) {
TokenList.Empty -> emptySequence()
is TokenList.GroupedByNetwork -> {
tokenList.groups.asSequence().flatMap { (network, currencies) ->
buildList {
add(
TokensListItemUM2.GroupTitle(
tokenRowUM = toGroupRow(network),
),
)
addAll(
currencies.asSequence().map { currencyStatus ->
val shouldShowPromo = promoCryptoCurrency?.currency?.id == currencyStatus.currency.id
TokensListItemUM2.Token(
tokenRowUM = toCurrencyRow(
currencyStatus = currencyStatus,
shouldShowPromo = shouldShowPromo,
),
)
}.toList(),
)
}
}
}
is TokenList.Ungrouped -> {
tokenList.currencies.asSequence().map { currencyStatus ->
TokensListItemUM2.Token(
toCurrencyRow(
currencyStatus = currencyStatus,
shouldShowPromo = promoCryptoCurrency?.currency?.id == currencyStatus.currency.id,
),
)
}
}
}
}
private fun toAccountRow(accountStatus: AccountStatus.CryptoPortfolio, isExpanded: Boolean): TangemTokenRowUM {
val account = accountStatus.account
val (topEndContent, bottomEndContent) = when (val accountBalance = accountStatus.tokenList.totalFiatBalance) {
TotalFiatBalance.Failed -> toFailedAccountRow()
is TotalFiatBalance.Loaded -> toLoadedAccountRow(accountStatus, accountBalance)
TotalFiatBalance.Loading -> EndContentUM.Loading to EndContentUM.Loading
}
return TangemTokenRowUM.Content(
id = accountStatus.account.accountId.value,
headIconUM = TangemIconUM.Currency(
currencyIconState = AccountIconItemStateConverter(size = AccountIconSize.ExtraSmall).convert(account),
),
titleUM = TangemTokenRowUM.TitleUM.Content(
text = account.accountName.toUM().value,
),
subtitleUM = TangemTokenRowUM.SubtitleUM.Content(
text = pluralReference(
R.plurals.common_tokens_count,
count = account.tokensCount,
formatArgs = wrappedList(account.tokensCount),
),
),
topEndContentUM = topEndContent,
bottomEndContentUM = bottomEndContent,
onItemClick = {
if (isExpanded) {
clickIntents.onAccountCollapseClick(account)
} else {
clickIntents.onAccountExpandClick(account)
}
},
onItemLongClick = null,
)
}
private fun toFailedAccountRow(): Pair<EndContentUM.Content, EndContentUM.Content> {
return EndContentUM.Content(
text = stringReference(StringsSigns.DASH_SIGN),
) to EndContentUM.Content(
text = styledResourceReference(
id = R.string.common_unreachable,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.status.attention) },
),
endIcons = persistentListOf(
TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.status.attention },
),
),
)
}
private fun toLoadedAccountRow(
accountStatus: AccountStatus.CryptoPortfolio,
accountBalance: TotalFiatBalance.Loaded,
): Pair<EndContentUM.Content, EndContentUM> {
val priceChange = accountStatus.priceChangeLce.getOrNull()
return EndContentUM.Content(
text = accountBalance.amount.formatStyled {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) },
)
},
) to if (priceChange != null) {
val priceChangeType = PriceChangeType.fromBigDecimal(priceChange.value)
EndContentUM.Content(
text = stringReference(
priceChange.value.format {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
},
),
priceChangeUM = PriceChangeState.Content(
type = priceChangeType,
valueInPercent = priceChange.value.format { percent() },
),
)
} else {
EndContentUM.Empty
}
}
private fun toGroupRow(network: Network): TangemHeaderRowUM {
return TangemHeaderRowUM(
id = network.hashCode().toString(),
title = resourceReference(
id = R.string.wallet_network_group_title,
formatArgs = wrappedList(network.name),
),
)
}
private fun toCurrencyRow(currencyStatus: CryptoCurrencyStatus, shouldShowPromo: Boolean): TangemTokenRowUM {
override fun convert(value: Pair<CryptoCurrencyStatus, Boolean>): TangemTokenRowUM {
val (currencyStatus, shouldShowPromo) = value
val earnApyInfo = earnApyConverter.convert(currencyStatus)
return TangemTokenRowUM.Content(
@ -362,7 +160,7 @@ internal class WalletTokensListUMTransformer(
}
}
private fun toCurrencyRowTopEnd(currencyStatus: CryptoCurrencyStatus): EndContentUM {
private fun toCurrencyRowTopEnd(currencyStatus: CryptoCurrencyStatus): TangemTokenRowUM.EndContentUM {
val yieldSupply = currencyStatus.value.yieldSupplyStatus
return when (currencyStatus.value) {
is CryptoCurrencyStatus.Loaded,
@ -370,7 +168,7 @@ internal class WalletTokensListUMTransformer(
is CryptoCurrencyStatus.NoQuote,
is CryptoCurrencyStatus.NoAccount,
-> {
EndContentUM.Content(
TangemTokenRowUM.EndContentUM.Content(
text = currencyStatus.getTotalFiatAmount().formatStyled {
fiat(
fiatCurrencyCode = appCurrency.code,
@ -397,11 +195,11 @@ internal class WalletTokensListUMTransformer(
}.toImmutableList(),
)
}
is CryptoCurrencyStatus.Loading -> EndContentUM.Loading
is CryptoCurrencyStatus.MissedDerivation -> EndContentUM.Content(
is CryptoCurrencyStatus.Loading -> TangemTokenRowUM.EndContentUM.Loading
is CryptoCurrencyStatus.MissedDerivation -> TangemTokenRowUM.EndContentUM.Content(
text = stringReference(StringsSigns.DASH_SIGN),
)
is CryptoCurrencyStatus.Unreachable -> EndContentUM.Content(
is CryptoCurrencyStatus.Unreachable -> TangemTokenRowUM.EndContentUM.Content(
text = styledResourceReference(
id = R.string.common_unreachable,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.status.attention) },
@ -414,17 +212,17 @@ internal class WalletTokensListUMTransformer(
),
)
is CryptoCurrencyStatus.NoAmount,
-> EndContentUM.Empty
-> TangemTokenRowUM.EndContentUM.Empty
}
}
private fun toCurrencyRowBottomEnd(currencyStatus: CryptoCurrencyStatus): EndContentUM {
private fun toCurrencyRowBottomEnd(currencyStatus: CryptoCurrencyStatus): TangemTokenRowUM.EndContentUM {
return when (currencyStatus.value) {
is CryptoCurrencyStatus.Loaded,
is CryptoCurrencyStatus.Custom,
is CryptoCurrencyStatus.NoQuote,
is CryptoCurrencyStatus.NoAccount,
-> EndContentUM.Content(
-> TangemTokenRowUM.EndContentUM.Content(
text = stringReference(
currencyStatus.getTotalCryptoAmount().format {
crypto(cryptoCurrency = currencyStatus.currency)
@ -432,8 +230,8 @@ internal class WalletTokensListUMTransformer(
),
isFlickering = currencyStatus.value.isFlickering(),
)
is CryptoCurrencyStatus.Loading -> EndContentUM.Loading
is CryptoCurrencyStatus.MissedDerivation -> EndContentUM.Content(
is CryptoCurrencyStatus.Loading -> TangemTokenRowUM.EndContentUM.Loading
is CryptoCurrencyStatus.MissedDerivation -> TangemTokenRowUM.EndContentUM.Content(
text = styledResourceReference(
id = R.string.common_no_address,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.status.attention) },
@ -445,7 +243,7 @@ internal class WalletTokensListUMTransformer(
),
),
)
is CryptoCurrencyStatus.Unreachable -> EndContentUM.Content(
is CryptoCurrencyStatus.Unreachable -> TangemTokenRowUM.EndContentUM.Content(
text = styledResourceReference(
id = R.string.common_unreachable,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.status.attention) },
@ -458,7 +256,7 @@ internal class WalletTokensListUMTransformer(
),
)
is CryptoCurrencyStatus.NoAmount,
-> EndContentUM.Empty
-> TangemTokenRowUM.EndContentUM.Empty
}
}
@ -496,26 +294,5 @@ internal class WalletTokensListUMTransformer(
)
}
private fun getOrganizeButtonUM(accountList: AccountStatusList): TangemButtonUM? {
return if (accountList.flattenCurrencies().size > 1 && !isSingleCurrencyWalletWithToken()) {
TangemButtonUM(
text = resourceReference(R.string.organize_tokens_title),
isEnabled = accountList.totalFiatBalance !is TotalFiatBalance.Loading,
size = TangemButtonSize.X9,
shape = TangemButtonShape.Rounded,
type = TangemButtonType.PrimaryInverse,
iconRes = R.drawable.ic_filter_default_24,
onClick = clickIntents::onOrganizeTokensClick,
)
} else {
null
}
}
private fun CryptoCurrencyStatus.Value.isFlickering(): Boolean = sources.total == StatusSource.CACHE
private fun isSingleCurrencyWalletWithToken(): Boolean {
return selectedWallet is UserWallet.Cold &&
selectedWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
}
}

View file

@ -0,0 +1,166 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
import com.tangem.core.ui.ds.button.TangemButtonShape
import com.tangem.core.ui.ds.button.TangemButtonSize
import com.tangem.core.ui.ds.button.TangemButtonType
import com.tangem.core.ui.ds.button.TangemButtonUM
import com.tangem.core.ui.ds.row.header.TangemHeaderRowUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.tokenlist.TokenList
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TokensListItemUM2
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListUM
import com.tangem.feature.wallet.presentation.wallet.state.utils.isSingleWalletWithToken
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toPersistentList
import java.math.BigDecimal
@Suppress("LongParameterList")
internal class WalletTokensListUMConverter(
private val appCurrency: AppCurrency,
private val selectedWallet: UserWallet,
private val clickIntents: WalletClickIntents,
private val yieldModuleApyMap: Map<String, BigDecimal>,
private val isAccountsModeEnabled: Boolean,
private val expandedAccounts: Set<AccountId>,
stakingAvailabilityMap: Map<CryptoCurrency, StakingAvailability>,
shouldShowMainPromo: Boolean,
) : Converter<AccountStatusList, WalletTokensListUM> {
private val accountRowConverter by lazy(LazyThreadSafetyMode.NONE) {
WalletTokenAccountItemConverter(
appCurrency = appCurrency,
expandedAccounts = expandedAccounts,
onAccountCollapseClick = clickIntents::onAccountCollapseClick,
onAccountExpandClick = clickIntents::onAccountExpandClick,
)
}
private val currencyRowConverter by lazy(LazyThreadSafetyMode.NONE) {
WalletTokenCurrencyItemConverter(
appCurrency = appCurrency,
selectedWallet = selectedWallet,
yieldModuleApyMap = yieldModuleApyMap,
stakingAvailabilityMap = stakingAvailabilityMap,
clickIntents = clickIntents,
)
}
private val yieldSupplyPromoBannerConverter by lazy(LazyThreadSafetyMode.NONE) {
YieldSupplyPromoBannerConverter(
yieldModuleApyMap,
shouldShowMainPromo,
)
}
override fun convert(value: AccountStatusList): WalletTokensListUM {
val promoCryptoCurrency = yieldSupplyPromoBannerConverter.convert2(value = value)
return if (value.accountStatuses.isEmpty()) {
WalletTokensListUM.Empty
} else {
val isCollapsable = value.accountStatuses.count {
it is AccountStatus.CryptoPortfolio && it.account.tokensCount > 0
} > 1
val tokenListUM = value.accountStatuses
.filterIsInstance<AccountStatus.CryptoPortfolio>()
.asSequence()
.flatMap { accountStatus ->
if (isAccountsModeEnabled) {
val isExpanded = expandedAccounts.contains(accountStatus.account.accountId)
sequenceOf(
TokensListItemUM2.Portfolio(
tokenRowUM = accountRowConverter.convert(accountStatus),
isExpanded = isExpanded || !isCollapsable,
isCollapsable = isCollapsable,
tokenList = getTokenListItems(
accountStatus.tokenList,
promoCryptoCurrency,
).toPersistentList(),
),
)
} else {
getTokenListItems(accountStatus.tokenList, promoCryptoCurrency)
}
}.toPersistentList()
WalletTokensListUM.Content(
tokenList = tokenListUM,
organizeButtonUM = getOrganizeButtonUM(value),
)
}
}
private fun getTokenListItems(
tokenList: TokenList,
promoCryptoCurrency: CryptoCurrencyStatus?,
): Sequence<TokensListItemUM2> {
return when (tokenList) {
TokenList.Empty -> emptySequence()
is TokenList.GroupedByNetwork -> {
tokenList.groups.asSequence().flatMap { (network, currencies) ->
buildList {
add(
TokensListItemUM2.GroupTitle(
tokenRowUM = toGroupRow(network),
),
)
addAll(
currencies.asSequence().map { currencyStatus ->
val shouldShowPromo = promoCryptoCurrency?.currency?.id == currencyStatus.currency.id
TokensListItemUM2.Token(
tokenRowUM = currencyRowConverter.convert(currencyStatus to shouldShowPromo),
)
}.toList(),
)
}
}
}
is TokenList.Ungrouped -> {
tokenList.currencies.asSequence().map { currencyStatus ->
val shouldShowPromo = promoCryptoCurrency?.currency?.id == currencyStatus.currency.id
TokensListItemUM2.Token(
tokenRowUM = currencyRowConverter.convert(currencyStatus to shouldShowPromo),
)
}
}
}
}
private fun toGroupRow(network: Network): TangemHeaderRowUM {
return TangemHeaderRowUM(
id = network.hashCode().toString(),
title = resourceReference(
id = R.string.wallet_network_group_title,
formatArgs = wrappedList(network.name),
),
)
}
private fun getOrganizeButtonUM(accountList: AccountStatusList): TangemButtonUM? {
return if (accountList.flattenCurrencies().size > 1 && !selectedWallet.isSingleWalletWithToken()) {
TangemButtonUM(
text = resourceReference(R.string.organize_tokens_title),
isEnabled = accountList.totalFiatBalance !is TotalFiatBalance.Loading,
size = TangemButtonSize.X9,
shape = TangemButtonShape.Rounded,
type = TangemButtonType.PrimaryInverse,
iconRes = R.drawable.ic_filter_default_24,
onClick = clickIntents::onOrganizeTokensClick,
)
} else {
null
}
}
}

View file

@ -21,4 +21,8 @@ private fun UserWallet.Cold.isWalletWithTokens(): Boolean {
internal fun UserWallet.isSingleWallet(): Boolean {
return this is UserWallet.Cold && scanResponse.cardTypesResolver.isSingleWallet()
}
internal fun UserWallet.isSingleWalletWithToken(): Boolean {
return this is UserWallet.Cold && scanResponse.cardTypesResolver.isSingleWalletWithToken()
}

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.presentation.wallet.ui
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.pager.PagerState
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.*
@ -17,7 +18,7 @@ import com.tangem.feature.wallet.presentation.wallet.ui.utils.demonstrateScrolli
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
@Composable
internal fun WalletEventEffect(
internal fun WalletEventEffectLegacy(
walletsListState: LazyListState,
snackbarHostState: SnackbarHostState,
event: StateEvent<WalletEvent>,
@ -59,6 +60,53 @@ internal fun WalletEventEffect(
is WalletEvent.RequestPushPermissions -> {
showPermissionRequest = value.onAllow to value.onDeny
}
is WalletEvent.CollapseBalance -> { /* no-op */ }
}
},
)
}
@Composable
internal fun WalletEventEffect(
walletsPagerState: PagerState,
snackbarHostState: SnackbarHostState,
event: StateEvent<WalletEvent>,
onCollapseBalance: () -> Unit,
) {
val resources = LocalContext.current.resources
var showPermissionRequest by remember { mutableStateOf<Pair<() -> Unit, () -> Unit>?>(null) }
HandlePermissionRequest(
permissionRequestParams = showPermissionRequest,
onPermissionRequestResult = { showPermissionRequest = null },
)
EventEffect(
event = event,
onTrigger = { value ->
when (value) {
is WalletEvent.ChangeWallet -> {
walletsPagerState.animateScrollToPage(page = value.newIndex)
}
is WalletEvent.ChangeWalletWithoutScroll -> {
walletsPagerState.scrollToPage(page = value.newIndex)
}
is WalletEvent.ShowError -> {
snackbarHostState.showSnackbar(message = value.text.resolveReference(resources))
}
is WalletEvent.CopyAddress -> {
snackbarHostState.showSnackbar(
message = resources.getStringSafe(R.string.wallet_notification_address_copied),
duration = SnackbarDuration.Short,
)
}
is WalletEvent.DemonstrateWalletsScrollPreview -> {
/* no-op */
}
is WalletEvent.RequestPushPermissions -> {
showPermissionRequest = value.onAllow to value.onDeny
}
is WalletEvent.CollapseBalance -> onCollapseBalance()
}
},
)

View file

@ -68,9 +68,9 @@ import com.tangem.core.ui.utils.lineTo
import com.tangem.core.ui.utils.moveTo
import com.tangem.core.ui.utils.toPx
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenState
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenWithEmptyTokensState
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.walletScreenState
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewDataLegacy.accountScreenState
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewDataLegacy.accountScreenWithEmptyTokensState
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewDataLegacy.walletScreenState
import com.tangem.feature.wallet.presentation.wallet.state.model.*
import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder
import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet
@ -111,7 +111,7 @@ internal fun WalletScreen(
onBottomSheetStateChange = onBottomSheetStateChange,
)
WalletEventEffect(
WalletEventEffectLegacy(
walletsListState = walletsListState,
snackbarHostState = snackbarHostState,
event = state.event,
@ -499,7 +499,7 @@ private fun MarketsTooltip(
}
@Composable
internal fun MarketsHint(isVisible: Boolean, modifier: Modifier = Modifier) {
private fun MarketsHint(isVisible: Boolean, modifier: Modifier = Modifier) {
AnimatedVisibility(
modifier = modifier,
visible = isVisible,

View file

@ -3,7 +3,9 @@ package com.tangem.feature.wallet.presentation.wallet.ui
import android.content.res.Configuration
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
@ -12,51 +14,66 @@ import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.*
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.arkivanov.decompose.ExperimentalDecomposeApi
import com.tangem.core.ui.components.atoms.Hand
import com.tangem.core.ui.components.atoms.handComposableComponentHeight
import com.tangem.core.ui.components.background.northernlights.NorthernLightsBackground
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.core.ui.components.haze.hazeEffectTangem
import com.tangem.core.ui.components.haze.hazeSourceTangem
import com.tangem.core.ui.components.rememberIsKeyboardVisible
import com.tangem.core.ui.components.sheetscaffold.*
import com.tangem.core.ui.components.snackbar.CopiedTextSnackbar
import com.tangem.core.ui.components.snackbar.TangemSnackbar
import com.tangem.core.ui.ds.topbar.TangemTopBar
import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingAppBarBehavior
import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingTopBar
import com.tangem.core.ui.ds.topbar.collapsing.rememberTangemExitUntilCollapsedScrollBehavior
import com.tangem.core.ui.event.StateEvent
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.*
import com.tangem.core.ui.test.MainScreenTestTags
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenState
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.accountScreenWithEmptyTokensState
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData.walletScreenState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.LocalWindowSize
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.feature.wallet.presentation.common.preview.WalletScreenPreviewData
import com.tangem.feature.wallet.presentation.wallet.state.model.NOT_INITIALIZED_WALLET_INDEX
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBalanceUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState
import dev.chrisbanes.haze.HazeProgressive
import com.tangem.feature.wallet.presentation.wallet.ui.components.MarketsHint
import com.tangem.feature.wallet.presentation.wallet.ui.components.MarketsTooltip
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.WalletBalance
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.WalletListContent
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.WalletPagerIndicator
import com.tangem.feature.wallet.presentation.wallet.ui.components.common.WalletTopBar
import com.tangem.feature.wallet.presentation.wallet.ui.utils.lazyListStateMapSaver
import kotlinx.coroutines.launch
import kotlin.math.abs
private const val MARKET_HINT_THRESHOLD = 0.5f
@OptIn(ExperimentalDecomposeApi::class)
@Composable
@ -69,98 +86,201 @@ internal fun WalletScreen2(
// It means that screen is still initializing
if (state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX) return
val walletsListState = rememberLazyListState(initialFirstVisibleItemIndex = state.selectedWalletIndex)
val statusBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getTop(this).toDp() }
val snackbarHostState = remember(::SnackbarHostState)
val isAutoScroll = remember { mutableStateOf(value = false) }
val walletsPagerState = rememberPagerState(
initialPage = state.selectedWalletIndex,
pageCount = { state.wallets2.size },
)
val partialCollapsedHeight = 64.dp + statusBarHeight
val balanceBlockHeight = 320.dp + partialCollapsedHeight
val behavior = rememberTangemExitUntilCollapsedScrollBehavior(
expandedHeight = balanceBlockHeight,
partialCollapsedHeight = partialCollapsedHeight,
snapAnimationSpec = spring(stiffness = Spring.StiffnessMedium),
)
val coroutineScope = rememberCoroutineScope()
WalletContent2(
state = state,
walletsListState = walletsListState,
walletsPagerState = walletsPagerState,
snackbarHostState = snackbarHostState,
isAutoScroll = isAutoScroll,
onAutoScrollReset = { isAutoScroll.value = false },
behavior = behavior,
bottomSheetContent = bottomSheetContent,
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
onBottomSheetStateChange = onBottomSheetStateChange,
)
WalletEventEffect(
walletsListState = walletsListState,
walletsPagerState = walletsPagerState,
snackbarHostState = snackbarHostState,
event = state.event,
onAutoScrollSet = { isAutoScroll.value = true },
onCollapseBalance = {
if (behavior.state.collapsedFraction < 1f) {
coroutineScope.launch {
behavior.state.collapse()
}
}
},
)
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalDecomposeApi::class)
@Suppress("LongMethod", "LongParameterList", "UnusedPrivateMember")
@Suppress("LongMethod", "LongParameterList", "CyclomaticComplexMethod")
@Composable
private fun WalletContent2(
state: WalletScreenState,
walletsListState: LazyListState,
walletsPagerState: PagerState,
behavior: TangemCollapsingAppBarBehavior,
snackbarHostState: SnackbarHostState,
isAutoScroll: State<Boolean>,
onAutoScrollReset: () -> Unit,
bottomSheetHeaderHeightProvider: () -> Dp,
onBottomSheetStateChange: (BottomSheetState) -> Unit,
bottomSheetContent: @Composable (() -> Unit),
) {
/*
* Don't pass key to remember, because it will brake scroll animation.
* selectedWalletIndex will be changed in WalletsListEffects.
*/
// val selectedWalletIndex by remember(state.selectedWalletIndex) { mutableIntStateOf(state.selectedWalletIndex) }
// val selectedWallet = state.wallets2.getOrElse(selectedWalletIndex) { state.wallets2[state.selectedWalletIndex] }
val density = LocalDensity.current
val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(this).toDp() }
val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getBottom(this).toDp() }
val listState = rememberLazyListState()
val partialCollapsedHeight = 64.dp + statusBarHeight
val scaffoldContent: @Composable (PaddingValues?) -> Unit = { _ ->
Box(Modifier.fillMaxSize()) {
NorthernLightsBackground(Modifier.matchParentSize())
}
val pagerState = rememberPagerState(
initialPage = state.selectedWalletIndex,
pageCount = { state.wallets2.size },
)
LaunchedEffect(pagerState.currentPage) {
if (pagerState.currentPage != state.selectedWalletIndex) {
state.onWalletChange(pagerState.currentPage, false)
}
}
}
var walletBalance by remember { mutableStateOf<TextReference?>(TextReference.EMPTY) }
BaseScaffoldWithMarkets(
state = state,
listState = listState,
snackbarHostState = snackbarHostState,
bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider,
onBottomSheetStateChange = onBottomSheetStateChange,
bottomSheetContent = bottomSheetContent,
content = scaffoldContent,
)
appBarContent = {
WalletTopBar(
topBarConfig = state.topBarConfig,
walletBalance = walletBalance,
behavior = behavior,
)
},
) { paddingValues, bottomSheetState ->
val marketHintApproxHeight = 140.dp
val contentPadding = PaddingValues(
bottom = paddingValues.calculateBottomPadding() + marketHintApproxHeight,
)
LaunchedEffect(walletsPagerState.currentPage) {
if (walletsPagerState.currentPage != state.selectedWalletIndex) {
state.onWalletChange(walletsPagerState.currentPage, false)
}
}
val listStates = rememberSaveable(saver = lazyListStateMapSaver(walletsPagerState.pageCount)) {
mutableMapOf<Int, LazyListState>().apply {
repeat(walletsPagerState.pageCount) { index -> put(index, LazyListState()) }
}
}
val canPagerScroll by remember { derivedStateOf { behavior.state.heightOffset == 0f } }
Box(
modifier = Modifier
.fillMaxSize()
.hazeSourceTangem(-1f),
) {
NorthernLightsBackground(Modifier.matchParentSize())
WalletPagerIndicator(
pagerState = walletsPagerState,
behavior = behavior,
)
HorizontalPager(
state = walletsPagerState,
userScrollEnabled = canPagerScroll,
beyondViewportPageCount = 1,
) { currentWalletIndex ->
val listState = listStates[currentWalletIndex] ?: rememberLazyListState()
val currentWallet = state.wallets2.getOrElse(currentWalletIndex) {
state.wallets2[state.selectedWalletIndex]
}
LaunchedEffect(walletsPagerState.currentPage) {
if (walletsPagerState.currentPage == currentWalletIndex) {
walletBalance = (currentWallet.walletsBalanceUM as? WalletBalanceUM.Content)?.balanceInAppBar
}
}
val isShowMarketsHint by remember {
derivedStateOf {
behavior.state.collapsedFraction > MARKET_HINT_THRESHOLD &&
listState.layoutInfo.totalItemsCount > 0 &&
!listState.canScrollBackward && !listState.canScrollForward ||
listState.canScrollBackward && !listState.canScrollForward
}
}
val pageSlideAlpha by rememberPageAlpha(walletsPagerState, currentWalletIndex)
Box(
modifier = Modifier.alpha(pageSlideAlpha),
) {
TangemCollapsingTopBar(
state = behavior.state,
collapsingPart = {
WalletBalance(
behavior = behavior,
walletBalanceUM = currentWallet.walletsBalanceUM,
buttons = currentWallet.buttons,
isBalanceHidden = state.isHidingMode,
)
},
body = {
WalletListContent(
currentWallet = currentWallet,
listState = listState,
isBalanceHidden = state.isHidingMode,
contentPadding = contentPadding,
modifier = Modifier
.fillMaxSize()
.nestedScroll(behavior.nestedScrollConnection),
)
},
)
val peekHeight = bottomSheetHeaderHeightProvider() + handComposableComponentHeight + bottomBarHeight
MarketsHint(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = peekHeight + TangemTheme.dimens2.x7),
isVisible = isShowMarketsHint,
)
}
}
MarketsTooltip(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 24.dp)
.fillMaxWidth(fraction = 0.7f),
isVisible = state.showMarketsOnboarding,
availableHeight = LocalWindowSize.current.height,
bottomSheetState = bottomSheetState,
)
}
}
}
@Suppress("LongParameterList", "LongMethod", "CyclomaticComplexMethod", "UnusedPrivateMember")
@Suppress("LongParameterList", "LongMethod")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private inline fun BaseScaffoldWithMarkets(
state: WalletScreenState,
snackbarHostState: SnackbarHostState,
listState: LazyListState,
bottomSheetHeaderHeightProvider: () -> Dp,
modifier: Modifier = Modifier,
noinline onBottomSheetStateChange: (BottomSheetState) -> Unit,
crossinline appBarContent: @Composable () -> Unit,
crossinline bottomSheetContent: @Composable () -> Unit,
crossinline content: @Composable (PaddingValues) -> Unit,
crossinline content: @Composable (PaddingValues, TangemSheetState) -> Unit,
) {
val bottomSheetState = rememberTangemStandardBottomSheetState()
val isPowerSaving by LocalPowerSavingState.current.isPowerSavingModeEnabled.collectAsStateWithLifecycle()
val isKeyboardVisible by rememberIsKeyboardVisible()
@ -176,7 +296,7 @@ private inline fun BaseScaffoldWithMarkets(
val maxHeight = LocalWindowSize.current.height
val coroutineScope = rememberCoroutineScope()
val background = TangemTheme.colors2.surface.level2
val background = TangemTheme.colors2.surface.level3
CompositionLocalProvider(
LocalMainBottomSheetColor provides remember(background) { mutableStateOf(background) },
@ -194,20 +314,12 @@ private inline fun BaseScaffoldWithMarkets(
Box(modifier = modifier) {
TangemBottomSheetScaffold(
modifier = Modifier.background(
brush = Brush.verticalGradient(
listOf(
TangemTheme.colors2.surface.level1,
TangemTheme.colors2.surface.level2,
),
),
),
snackbarHost = { snackbarHostState ->
WalletSnackbarHost(
snackbarHostState = snackbarHostState,
event = state.event,
modifier = Modifier
.padding(bottom = TangemTheme.dimens.spacing4)
.padding(bottom = TangemTheme.dimens2.x1)
.navigationBarsPadding(),
)
},
@ -250,47 +362,22 @@ private inline fun BaseScaffoldWithMarkets(
}
},
content = { paddingValues ->
Box {
Column(
modifier = Modifier.hazeSourceTangem(-1f),
) {
content(paddingValues)
}
content(paddingValues, bottomSheetState)
appBarContent()
Surface(
color = Color.Unspecified,
contentColor = Color.Unspecified,
modifier = Modifier
.hazeEffectTangem {
progressive =
HazeProgressive.verticalGradient(startIntensity = 1f, endIntensity = 0f)
},
) {
TangemTopBar(
title = stringReference(""), // todo balance
startIconRes = R.drawable.ic_tangem_24,
endIconRes = R.drawable.ic_more_default_24,
onEndContentClick = state.topBarConfig.onDetailsClick,
isGhostButtons = !isPowerSaving,
modifier = Modifier
.testTag(MainScreenTestTags.TOP_BAR),
)
}
BottomSheetScrim(
color = if (state.showMarketsOnboarding) {
Color.Black.copy(alpha = .65f)
} else {
Color.Black.copy(alpha = .40f)
},
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded ||
state.showMarketsOnboarding,
onDismissRequest = {
coroutineScope.launch { bottomSheetState.partialExpand() }
state.onDismissMarketsTooltip()
},
)
}
BottomSheetScrim(
color = if (state.showMarketsOnboarding) {
Color.Black.copy(alpha = .65f)
} else {
Color.Black.copy(alpha = .40f)
},
visible = bottomSheetState.targetValue == TangemSheetValue.Expanded ||
state.showMarketsOnboarding,
onDismissRequest = {
coroutineScope.launch { bottomSheetState.partialExpand() }
state.onDismissMarketsTooltip()
},
)
},
)
@ -410,6 +497,29 @@ private fun WalletSnackbarHost(
}
}
@Composable
private fun rememberPageAlpha(pagerState: PagerState, currentPageIndex: Int): State<Float> {
return remember {
derivedStateOf {
val pageOffset = pagerState.currentPageOffsetFraction
val currentPage = pagerState.currentPage
when {
// Current page is being swiped away
currentPageIndex == currentPage -> {
1f - abs(pageOffset) * 2f
}
// Target page is being swiped in
currentPageIndex == pagerState.targetPage -> {
(abs(pageOffset) * 2f - 1f).coerceAtLeast(0f)
}
// Other pages remain invisible
else -> 0f
}.coerceIn(0f, 1f)
}
}
}
// region Preview
@OptIn(ExperimentalDecomposeApi::class)
@Preview(showBackground = true, widthDp = 360)
@ -431,10 +541,8 @@ private fun WalletScreen2_Preview(@PreviewParameter(WalletScreen2PreviewProvider
private class WalletScreen2PreviewProvider : PreviewParameterProvider<WalletScreenState> {
override val values: Sequence<WalletScreenState>
get() = sequenceOf(
walletScreenState,
walletScreenState.copy(selectedWalletIndex = 1),
accountScreenState.copy(selectedWalletIndex = 1),
accountScreenWithEmptyTokensState.copy(selectedWalletIndex = 1),
WalletScreenPreviewData.defaultState,
WalletScreenPreviewData.defaultState.copy(selectedWalletIndex = 1),
)
}
// endregion