Updated on 2026-08-14
This commit is contained in:
parent
6cfb8213f0
commit
fe935fdba0
16 changed files with 933 additions and 572 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -31,4 +31,6 @@ internal sealed class WalletEvent {
|
|||
val onAllow: () -> Unit,
|
||||
val onDeny: () -> Unit,
|
||||
) : WalletEvent()
|
||||
|
||||
data object CollapseBalance : WalletEvent()
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue