diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/TangemPagerIndicator.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/TangemPagerIndicator.kt index 40de998c46..0f4afd8e23 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/TangemPagerIndicator.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/TangemPagerIndicator.kt @@ -127,6 +127,7 @@ fun TangemPagerIndicator( Box( modifier = modifier + .width(getSize(totalPages)) .conditionalCompose(colors.overlay != null) { colors.overlay?.let { overlay -> background( @@ -170,6 +171,19 @@ fun TangemPagerIndicator( } } +@Suppress("MagicNumber") +@Composable +private fun getSize(pageCount: Int): Dp { + return when (pageCount) { + 0 -> TangemTheme.dimens2.x0 + 1 -> TangemTheme.dimens2.x8 + 2 -> TangemTheme.dimens2.x12 + 3 -> TangemTheme.dimens2.x16 + 4 -> TangemTheme.dimens2.x20 + else -> TangemTheme.dimens2.x24 + } +} + private fun getWindowBounds(totalPages: Int, currentIndex: Int): Pair { if (totalPages <= MAX_VISIBLE_DOTS) { return 0 to totalPages diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt index 5c603a74b5..5b9d50325d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt @@ -70,6 +70,8 @@ internal interface WalletContentClickIntents { fun onManageTokensClick(accountId: AccountId) + fun onManageTokensClick(userWalletId: UserWalletId) + fun onTransactionClick(txHash: String) fun onDissmissBottomSheet() @@ -228,6 +230,12 @@ internal class WalletContentClickIntentsImplementor @Inject constructor( router.openManageTokensScreen(accountId) } + override fun onManageTokensClick(userWalletId: UserWalletId) { + router.openManageTokensScreen( + AccountId.forMainCryptoPortfolio(userWalletId), + ) + } + private fun openYieldSupply(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, apy: String) { router.openYieldSupplyEntryScreen( userWalletId = userWalletId, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt index 5458deb06b..16187090af 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/preview/WalletScreenPreviewData.kt @@ -42,6 +42,27 @@ internal object WalletScreenPreviewData { onItemLongClick = {}, ) + private val accountRowDefault = TangemTokenRowUM.Content( + id = "1", + headIconUM = TangemIconUM.Currency(CurrencyIconState.Loading), + titleUM = TangemTokenRowUM.TitleUM.Content( + text = stringReference("Main Account"), + ), + subtitleUM = TangemTokenRowUM.SubtitleUM.Content( + text = stringReference("2 tokens"), + ), + topEndContentUM = TangemTokenRowUM.EndContentUM.Content( + text = stringReference("1 234,56 \$"), + ), + bottomEndContentUM = TangemTokenRowUM.EndContentUM.Content( + text = stringReference("0,12345678 BTC"), + ), + promoBannerUM = TangemTokenRowUM.PromoBannerUM.Empty, + tailUM = TangemRowTailUM.Empty, + onItemClick = {}, + onItemLongClick = {}, + ) + private val tokenListDefault = WalletTokensListUM.Content( tokenList = persistentListOf( TokensListItemUM2.Token(tokenRowDefault.copy(id = "0")), @@ -58,6 +79,66 @@ internal object WalletScreenPreviewData { ), ) + private val tokenListAccounts = WalletTokensListUM.Content( + tokenList = persistentListOf( + TokensListItemUM2.Portfolio( + tokenRowUM = accountRowDefault.copy(id = "0"), + tokenList = persistentListOf( + TokensListItemUM2.Token(tokenRowDefault.copy(id = "1")), + TokensListItemUM2.Token(tokenRowDefault.copy(id = "2")), + ), + isExpanded = true, + isCollapsable = true, + onEmptyClick = {}, + ), + TokensListItemUM2.Portfolio( + tokenRowUM = accountRowDefault.copy(id = "3"), + tokenList = persistentListOf( + TokensListItemUM2.Token(tokenRowDefault.copy(id = "4")), + TokensListItemUM2.Token(tokenRowDefault.copy(id = "5")), + ), + isExpanded = true, + isCollapsable = true, + onEmptyClick = {}, + ), + ), + organizeButtonUM = TangemButtonUM( + text = resourceReference(R.string.organize_tokens_title), + type = TangemButtonType.Secondary, + onClick = {}, + iconRes = R.drawable.ic_filter_default_24, + size = TangemButtonSize.X9, + shape = TangemButtonShape.Rounded, + ), + ) + + private val tokenListAccountsWithEmpty = WalletTokensListUM.Content( + tokenList = persistentListOf( + TokensListItemUM2.Portfolio( + tokenRowUM = accountRowDefault.copy(id = "0"), + tokenList = persistentListOf(), + isExpanded = true, + isCollapsable = true, + onEmptyClick = {}, + ), + TokensListItemUM2.Portfolio( + tokenRowUM = accountRowDefault.copy(id = "3"), + tokenList = persistentListOf(), + isExpanded = false, + isCollapsable = true, + onEmptyClick = {}, + ), + ), + organizeButtonUM = TangemButtonUM( + text = resourceReference(R.string.organize_tokens_title), + type = TangemButtonType.Secondary, + onClick = {}, + iconRes = R.drawable.ic_filter_default_24, + size = TangemButtonSize.X9, + shape = TangemButtonShape.Rounded, + ), + ) + private val walletLocked = WalletUM.Locked( walletsBalanceUM = WalletBalancePreview.empty, buttons = WalletPreviewData.disabledActionButtons, @@ -99,11 +180,20 @@ internal object WalletScreenPreviewData { ), notifications = persistentListOf(), notificationsCarousel = persistentListOf(), - tokensListUM = WalletTokensListUM.Empty, + tokensListUM = WalletTokensListUM.Empty(onEmptyClick = {}), nftState = WalletNFTItemUM.Hidden, tangemPayState = TangemPayState.Empty, ) + private val walletAccountDefault = walletDefault.copy( + tokensListUM = tokenListAccounts, + ) + + private val walletAccountEmpty = walletEmpty.copy( + walletsBalanceUM = WalletBalancePreview.content, + tokensListUM = tokenListAccountsWithEmpty, + ) + val defaultState = WalletScreenState( topBarConfig = topBarConfig, selectedWalletIndex = 0, @@ -112,6 +202,8 @@ internal object WalletScreenPreviewData { walletDefault, walletEmpty, walletLocked, + walletAccountDefault, + walletAccountEmpty, ), onWalletChange = { _, _ -> }, event = consumedEvent(), @@ -120,10 +212,18 @@ internal object WalletScreenPreviewData { onDismissMarketsTooltip = {}, ) + val defaultAccountState = defaultState.copy( + selectedWalletIndex = 3, + ) + val emptyState = defaultState.copy( selectedWalletIndex = 1, ) + val emptyAccountState = defaultState.copy( + selectedWalletIndex = 4, + ) + val lockedState = defaultState.copy( selectedWalletIndex = 2, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletTokensListUM.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletTokensListUM.kt index ed35c7fd98..7d3d1f5dc5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletTokensListUM.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletTokensListUM.kt @@ -20,7 +20,9 @@ internal sealed class WalletTokensListUM { abstract val tokenList: ImmutableList abstract val organizeButtonUM: TangemButtonUM? - data object Empty : WalletTokensListUM() { + data class Empty( + val onEmptyClick: () -> Unit, + ) : WalletTokensListUM() { override val tokenList: ImmutableList = persistentListOf() override val organizeButtonUM: TangemButtonUM? = null } @@ -32,12 +34,14 @@ internal sealed class WalletTokensListUM { tokenList = persistentListOf(), isExpanded = false, isCollapsable = true, + onEmptyClick = {}, ), TokensListItemUM2.Portfolio( tokenRowUM = TangemTokenRowUM.Empty(id = "1"), tokenList = persistentListOf(), isExpanded = false, isCollapsable = true, + onEmptyClick = {}, ), ) override val organizeButtonUM: TangemButtonUM? = null @@ -50,18 +54,21 @@ internal sealed class WalletTokensListUM { tokenList = persistentListOf(), isExpanded = false, isCollapsable = true, + onEmptyClick = {}, ), TokensListItemUM2.Portfolio( tokenRowUM = TangemTokenRowUM.Loading(id = "1"), tokenList = persistentListOf(), isExpanded = false, isCollapsable = true, + onEmptyClick = {}, ), TokensListItemUM2.Portfolio( tokenRowUM = TangemTokenRowUM.Loading(id = "2"), tokenList = persistentListOf(), isExpanded = false, isCollapsable = true, + onEmptyClick = {}, ), ) override val organizeButtonUM: TangemButtonUM? = null @@ -90,6 +97,7 @@ internal sealed interface TokensListItemUM2 { data class Portfolio( override val tokenRowUM: TangemTokenRowUM, + val onEmptyClick: () -> Unit, val tokenList: ImmutableList, val isExpanded: Boolean, val isCollapsable: Boolean, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListErrorTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListErrorTransformer.kt index 0d7dc02991..d2740b30c4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListErrorTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListErrorTransformer.kt @@ -9,6 +9,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.common.util.getCardsCount import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.error.TokenListError +import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.state.model.* import com.tangem.feature.wallet.presentation.wallet.state.utils.disableButtons @@ -19,6 +20,7 @@ internal class SetTokenListErrorTransformer( private val selectedWallet: UserWallet, private val error: TokenListError, private val appCurrency: AppCurrency, + private val clickIntents: WalletClickIntents, ) : WalletStateTransformer(selectedWallet.walletId) { override fun transform(prevState: WalletState): WalletState { @@ -57,7 +59,11 @@ internal class SetTokenListErrorTransformer( is WalletUM.Content -> { walletUM.copy( walletsBalanceUM = walletUM.walletsBalanceUM.toLoadedState(), - tokensListUM = WalletTokensListUM.Empty, + tokensListUM = WalletTokensListUM.Empty( + onEmptyClick = { + clickIntents.onManageTokensClick(walletUM.walletsBalanceUM.id) + }, + ), buttons = walletUM.disableButtons(), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt index 57be4ec68c..8bbaf12cb9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt @@ -7,7 +7,6 @@ import com.tangem.domain.staking.model.StakingAvailability import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.presentation.wallet.state.model.* import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletBalanceUMTransformer -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.WalletTokensListUMConverter @@ -99,7 +98,13 @@ internal class SetTokenListTransformer( } private fun toLoadedState(): WalletTokensListUM { - if (params !is TokenConverterParams.Account) return WalletTokensListUM.Empty + if (params !is TokenConverterParams.Account) { + return WalletTokensListUM.Empty( + onEmptyClick = { + clickIntents.onManageTokensClick(userWallet.walletId) + }, + ) + } return WalletTokensListUMConverter( selectedWallet = userWallet, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/WalletTokensListUMConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/WalletTokensListUMConverter.kt index 4ebcd03f94..ac40b2a184 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/WalletTokensListUMConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/WalletTokensListUMConverter.kt @@ -72,7 +72,9 @@ internal class WalletTokensListUMConverter( override fun convert(value: AccountStatusList): WalletTokensListUM { val promoCryptoCurrency = yieldSupplyPromoBannerConverter.convert2(value = value) return if (value.flattenCurrencies().isEmpty()) { - WalletTokensListUM.Empty + WalletTokensListUM.Empty( + onEmptyClick = { clickIntents.onManageTokensClick(value.mainAccount.accountId) }, + ) } else { val isCollapsable = value.accountStatuses.count { it is AccountStatus.CryptoPortfolio && it.account.tokensCount > 0 @@ -89,6 +91,7 @@ internal class WalletTokensListUMConverter( tokenRowUM = accountRowConverter.convert(accountStatus), isExpanded = isExpanded || !isCollapsable, isCollapsable = isCollapsable, + onEmptyClick = { clickIntents.onManageTokensClick(accountStatus.account.accountId) }, tokenList = getTokenListItems( accountStatus, promoCryptoCurrency, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicAccountListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicAccountListSubscriber.kt index 8bff35ab6f..48aa789257 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicAccountListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicAccountListSubscriber.kt @@ -132,6 +132,7 @@ internal abstract class BasicAccountListSubscriber : BasicWalletSubscriber() { selectedWallet = userWallet, error = e, appCurrency = appCurrency, + clickIntents = clickIntents, ), ) return diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen2.kt index c6cef8b01d..27ff0395e6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen2.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen2.kt @@ -579,6 +579,8 @@ private class WalletScreen2PreviewProvider : PreviewParameterProvider nonContentItem(modifier = modifier) + is WalletTokensListUM.Empty -> nonContentItem2( + onEmptyClick = walletTokensListUM.onEmptyClick, + modifier = modifier, + ) } } @@ -168,7 +176,11 @@ private fun LazyListScope.portfolioItem( isBalanceHidden: Boolean, modifier: Modifier, ) { - val lastIndex = listItem.tokenList.lastIndex + 1 + val lastIndex = if (listItem.tokenList.isEmpty()) { + listItem.tokenList.lastIndex + 2 + } else { + listItem.tokenList.lastIndex + 1 + } accountItem( listItem = listItem, @@ -177,45 +189,54 @@ private fun LazyListScope.portfolioItem( lastIndex = lastIndex, isBalanceHidden = isBalanceHidden, ) - itemsIndexed( - items = listItem.tokenList, - key = { _, item -> item.tokenRowUM.id }, - contentType = { _, item -> item::class.java }, - itemContent = { tokenIndex, item -> - SlideInItemVisibility( - currentIndex = tokenIndex + 1, - lastIndex = lastIndex, - modifier = modifier - .animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null) - .roundedShapeItemDecoration( - radius = 18.dp, - currentIndex = tokenIndex + 1, - addDefaultPadding = false, - lastIndex = lastIndex, - backgroundColor = TangemTheme.colors2.surface.level3, - ), - visible = listItem.isExpanded, - ) { - val itemModifier = Modifier - .testTag(MainScreenTestTags.TOKEN_LIST_ITEM) - .semantics { lazyListItemPosition = tokenIndex + 1 } + if (listItem.tokenList.isEmpty()) { + nonContentAccountItem( + listItem = listItem, + index = index, + lastIndex = lastIndex, + modifier = modifier, + ) + } else { + itemsIndexed( + items = listItem.tokenList, + key = { _, item -> item.tokenRowUM.id }, + contentType = { _, item -> item::class.java }, + itemContent = { tokenIndex, item -> + SlideInItemVisibility( + currentIndex = tokenIndex + 1, + lastIndex = lastIndex, + modifier = modifier + .animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null) + .roundedShapeItemDecoration( + radius = 18.dp, + currentIndex = tokenIndex + 1, + addDefaultPadding = false, + lastIndex = lastIndex, + backgroundColor = TangemTheme.colors2.surface.level3, + ), + visible = listItem.isExpanded, + ) { + val itemModifier = Modifier + .testTag(MainScreenTestTags.TOKEN_LIST_ITEM) + .semantics { lazyListItemPosition = tokenIndex + 1 } - when (val tokenRowUM = item.tokenRowUM) { - is TangemTokenRowUM -> TangemTokenRow( - tokenRowUM = tokenRowUM, - isBalanceHidden = isBalanceHidden, - reorderableState = null, - modifier = itemModifier, - ) - is TangemHeaderRowUM -> TangemHeaderRow( - headerRowUM = tokenRowUM, - isBalanceHidden = isBalanceHidden, - modifier = itemModifier, - ) + when (val tokenRowUM = item.tokenRowUM) { + is TangemTokenRowUM -> TangemTokenRow( + tokenRowUM = tokenRowUM, + isBalanceHidden = isBalanceHidden, + reorderableState = null, + modifier = itemModifier, + ) + is TangemHeaderRowUM -> TangemHeaderRow( + headerRowUM = tokenRowUM, + isBalanceHidden = isBalanceHidden, + modifier = itemModifier, + ) + } } - } - }, - ) + }, + ) + } } @Suppress("MagicNumber") @@ -428,6 +449,54 @@ private fun LazyListScope.nonContentItem(modifier: Modifier = Modifier) { } } +private fun LazyListScope.nonContentItem2(onEmptyClick: () -> Unit, modifier: Modifier = Modifier) { + item( + key = NON_CONTENT_TOKENS_LIST_KEY, + contentType = NON_CONTENT_TOKENS_LIST_KEY, + ) { + NonContentItemContentV2( + onClick = onEmptyClick, + textColor = TangemTheme.colors2.text.neutral.tertiary, + modifier = modifier + .animateItem(fadeInSpec = null, fadeOutSpec = null) + .padding(top = 46.dp), + ) + } +} + +private fun LazyListScope.nonContentAccountItem( + listItem: TokensListItemUM2.Portfolio, + index: Int, + lastIndex: Int, + modifier: Modifier = Modifier, +) { + item( + key = "$NON_CONTENT_TOKENS_LIST_KEY account-${listItem.tokenRowUM.id}", + contentType = "$NON_CONTENT_TOKENS_LIST_KEY account-${listItem.tokenRowUM.id}", + ) { + SlideInItemVisibility( + currentIndex = index + 1, + lastIndex = lastIndex, + modifier = modifier + .animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null) + .roundedShapeItemDecoration( + radius = 18.dp, + addDefaultPadding = false, + currentIndex = 1, + lastIndex = 1, + backgroundColor = TangemTheme.colors2.surface.level3, + ), + visible = listItem.isExpanded, + ) { + NonContentItemContentV2( + modifier = Modifier.padding(vertical = 36.dp), + textColor = TangemTheme.colors2.text.neutral.secondary, + onClick = listItem.onEmptyClick, + ) + } + } +} + @Composable internal fun NonContentItemContent(modifier: Modifier = Modifier) { Column( @@ -450,4 +519,35 @@ internal fun NonContentItemContent(modifier: Modifier = Modifier) { style = TangemTheme.typography.caption2, ) } +} + +@Composable +internal fun NonContentItemContentV2(textColor: Color, modifier: Modifier = Modifier, onClick: () -> Unit) { + Column( + modifier = modifier, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + painter = painterResource(id = R.drawable.ic_empty_64), + contentDescription = null, + modifier = Modifier.size(size = TangemTheme.dimens2.x16), + tint = TangemTheme.colors2.graphic.neutral.quaternary, + ) + SpacerH(TangemTheme.dimens2.x5) + Text( + text = stringResourceSafe(id = R.string.main_empty_tokens_list_message), + modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing48), + color = textColor, + textAlign = TextAlign.Center, + style = TangemTheme.typography2.bodyRegular14, + ) + SpacerH(TangemTheme.dimens2.x2) + PrimaryInverseTangemButton( + text = resourceReference(id = R.string.common_add_tokens), + onClick = onClick, + size = TangemButtonSize.X8, + shape = TangemButtonShape.Rounded, + modifier = Modifier, + ) + } } \ No newline at end of file