diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 6b09dce789..a04ba073c9 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -160,6 +160,7 @@ Tap the card Internal error: wallet manager not found You have updated biometrics, scan your card to enter + To begin tracking your crypto assets and transactions, add tokens. You have completed all of the lessons, and are now eligible to receive your 1INCH tokens Complete three lessons and receive %d 1INCH token to your wallet diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index ddba0a20cd..1c60364fa2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -105,11 +105,13 @@ data class TangemDimens internal constructor( val spacing38: Dp = 38.dp, val spacing40: Dp = 40.dp, val spacing44: Dp = 44.dp, + val spacing48: Dp = 48.dp, val spacing50: Dp = 50.dp, val spacing52: Dp = 52.dp, val spacing54: Dp = 54.dp, val spacing56: Dp = 56.dp, val spacing92: Dp = 92.dp, + val spacing96: Dp = 96.dp, val spacing154: Dp = 154.dp, // endregion Spacing ) \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt index 9c11b12566..53f553015f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt @@ -11,7 +11,6 @@ internal class TokenListErrorConverter( private val inProgressStateConverter: InProgressStateConverter, ) : Converter { - // TODO: [REDACTED_JIRA] override fun convert(value: TokenListError): OrganizeTokensState { return inProgressStateConverter.convertBack(currentState()) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt index fab77e7e34..e738cdada5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt @@ -11,7 +11,6 @@ internal class TokenListSortingErrorConverter( private val inProgressStateConverter: InProgressStateConverter, ) : Converter { - // TODO: [REDACTED_JIRA] override fun convert(value: TokenListSortingError): OrganizeTokensState { return inProgressStateConverter.convertBack(currentState()) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt index cc607f9f17..1209a2da37 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt @@ -10,18 +10,26 @@ import kotlinx.collections.immutable.persistentListOf /** * Wallet tokens list state * - * @property items content items - * @property onOrganizeTokensClick lambda be invoked when organize tokens button is clicked - * [REDACTED_AUTHOR] */ -internal sealed class WalletTokensListState( - open val items: ImmutableList, - open val onOrganizeTokensClick: (() -> Unit)?, -) { +internal sealed class WalletTokensListState { + + /** Empty token list state */ + object Empty : WalletTokensListState() + + /** + * Wallet content token list state + * + * @property items content items + * @property onOrganizeTokensClick lambda be invoked when organize tokens button is clicked + */ + sealed class ContentState( + open val items: ImmutableList, + open val onOrganizeTokensClick: (() -> Unit)?, + ) : WalletTokensListState() /** Loading content state */ - object Loading : WalletTokensListState( + object Loading : ContentState( items = persistentListOf( TokensListItemState.Token(state = TokenItemState.Loading(id = FIRST_LOADING_TOKEN_ID)), TokensListItemState.Token(state = TokenItemState.Loading(id = SECOND_LOADING_TOKEN_ID)), @@ -38,11 +46,11 @@ internal sealed class WalletTokensListState( data class Content( override val items: ImmutableList, override val onOrganizeTokensClick: (() -> Unit)?, - ) : WalletTokensListState(items, onOrganizeTokensClick) + ) : ContentState(items, onOrganizeTokensClick) /** Locked content state */ object Locked : - WalletTokensListState( + ContentState( items = persistentListOf( TokensListItemState.NetworkGroupTitle(value = TextReference.Res(id = R.string.main_tokens)), TokensListItemState.Token(state = TokenItemState.Loading(id = LOCKED_TOKEN_ID)), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index db0cee15f1..4bc618e81d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -22,6 +22,7 @@ import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeButton @@ -106,7 +107,10 @@ private fun WalletContent(state: WalletState.ContentState) { contentItems(state = state, txHistoryItems = txHistoryItems, modifier = movableItemModifier) if (state is WalletMultiCurrencyState) { - organizeButton(onClick = state.tokensListState.onOrganizeTokensClick, modifier = itemModifier) + val tokensListState = state.tokensListState + if (tokensListState is WalletTokensListState.ContentState) { + organizeButton(onClick = tokensListState.onOrganizeTokensClick, modifier = itemModifier) + } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index bf7451e5ec..350cb95ecf 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -1,11 +1,26 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import com.tangem.core.ui.decorations.roundedShapeItemDecoration +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState +import kotlinx.collections.immutable.ImmutableList + +private const val NON_CONTENT_TOKENS_LIST_KEY = "NON_CONTENT_TOKENS_LIST" /** * LazyList extension for [WalletTokensListState] @@ -15,10 +30,20 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletToke * [REDACTED_AUTHOR] */ -@OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.tokensListItems(state: WalletTokensListState, modifier: Modifier = Modifier) { + when (state) { + is WalletTokensListState.ContentState -> contentItems(items = state.items, modifier = modifier) + WalletTokensListState.Empty -> nonContentItem(modifier = modifier) + } +} + +@OptIn(ExperimentalFoundationApi::class) +private fun LazyListScope.contentItems( + items: ImmutableList, + modifier: Modifier = Modifier, +) { itemsIndexed( - items = state.items, + items = items, key = { _, item -> when (item) { is WalletTokensListState.TokensListItemState.NetworkGroupTitle -> item.value.hashCode() @@ -33,9 +58,40 @@ internal fun LazyListScope.tokensListItems(state: WalletTokensListState, modifie .animateItemPlacement() .roundedShapeItemDecoration( currentIndex = index, - lastIndex = state.items.lastIndex, + lastIndex = items.lastIndex, ), ) }, ) +} + +@OptIn(ExperimentalFoundationApi::class) +private fun LazyListScope.nonContentItem(modifier: Modifier = Modifier) { + item( + key = NON_CONTENT_TOKENS_LIST_KEY, + contentType = NON_CONTENT_TOKENS_LIST_KEY, + ) { + Column( + modifier = modifier + .animateItemPlacement() + .padding(top = TangemTheme.dimens.spacing96), + verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing16), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + painter = painterResource(id = R.drawable.ic_empty_64), + contentDescription = null, + modifier = Modifier.size(size = TangemTheme.dimens.size64), + tint = TangemTheme.colors.icon.inactive, + ) + + Text( + text = stringResource(id = R.string.main_empty_tokens_list_message), + modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing48), + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Center, + style = TangemTheme.typography.caption, + ) + } + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt index 4d8b4cc1f4..ac32cdb876 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt @@ -12,7 +12,6 @@ internal class TokenListErrorConverter( private val currentStateProvider: Provider, ) : Converter { - // TODO: [REDACTED_JIRA] override fun convert(value: TokenListError): WalletMultiCurrencyState.Content { return requireNotNull(currentStateProvider() as? WalletMultiCurrencyState.Content).copy( tokensListState = WalletTokensListState.Content(items = persistentListOf(), onOrganizeTokensClick = null), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt index d20d628c57..5c8fb261e0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt @@ -27,18 +27,28 @@ internal class TokenListToContentItemsConverter( ) override fun convert(value: TokenList): WalletTokensListState { - return WalletTokensListState.Content( - items = when (value) { - is TokenList.GroupedByNetwork -> value.mapToMultiCurrencyItems() - is TokenList.Ungrouped -> value.mapToMultiCurrencyItems() - is TokenList.NotInitialized -> getLoadingMultiCurrencyTokens() - }, - onOrganizeTokensClick = if (value.totalFiatBalance is TokenList.FiatBalance.Loaded) { - clickIntents::onOrganizeTokensClick - } else { - null - }, - ) + val isEmptyList = when (value) { + is TokenList.GroupedByNetwork -> value.groups.isEmpty() + is TokenList.NotInitialized -> true + is TokenList.Ungrouped -> value.currencies.isEmpty() + } + + return if (isEmptyList) { + WalletTokensListState.Empty + } else { + WalletTokensListState.Content( + items = when (value) { + is TokenList.GroupedByNetwork -> value.mapToMultiCurrencyItems() + is TokenList.Ungrouped -> value.mapToMultiCurrencyItems() + is TokenList.NotInitialized -> getLoadingMultiCurrencyTokens() + }, + onOrganizeTokensClick = if (value.totalFiatBalance is TokenList.FiatBalance.Loaded) { + clickIntents::onOrganizeTokensClick + } else { + null + }, + ) + } } private fun TokenList.GroupedByNetwork.mapToMultiCurrencyItems(): PersistentList { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt index 885c12dd52..e4e37d8e7d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt @@ -63,7 +63,9 @@ internal class TokenListToWalletStateConverter( } private fun WalletState.getRefreshingStatus(): Boolean { - return if (this is WalletMultiCurrencyState.Content) { + return if (this is WalletMultiCurrencyState.Content && + this.tokensListState is WalletTokensListState.ContentState + ) { tokensListState.items.any { tokensListItemState -> tokensListItemState is WalletTokensListState.TokensListItemState.Token && tokensListItemState.state is TokenItemState.Loading diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt index 18bfbbbe34..11beecade5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt @@ -118,7 +118,9 @@ internal class WalletNotificationsListFactory( } return currentStateProvider().let { state -> - state is WalletMultiCurrencyState.Content && state.tokensListState.items.any(isUnreachableState) + state is WalletMultiCurrencyState.Content && + state.tokensListState is WalletTokensListState.ContentState && + state.tokensListState.items.any(isUnreachableState) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 12cdb339a1..f7d4a9fb1b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -352,10 +352,12 @@ internal class WalletViewModel @Inject constructor( // Check the special components return when (this) { is WalletMultiCurrencyState -> { - tokensListState is WalletTokensListState.Loading || - tokensListState.items + val hasLoadingTokens = tokensListState is WalletTokensListState.ContentState && + (tokensListState as WalletTokensListState.ContentState).items .filterIsInstance() .any { it.state is TokenItemState.Loading } + + tokensListState is WalletTokensListState.Loading || hasLoadingTokens } is WalletSingleCurrencyState -> { txHistoryState is TxHistoryState.Loading || marketPriceBlockState is MarketPriceBlockState.Loading diff --git a/features/wallet/impl/src/main/res/drawable/ic_empty_64.xml b/features/wallet/impl/src/main/res/drawable/ic_empty_64.xml new file mode 100644 index 0000000000..090f906247 --- /dev/null +++ b/features/wallet/impl/src/main/res/drawable/ic_empty_64.xml @@ -0,0 +1,13 @@ + + + + + +