Updated on 2026-08-14
This commit is contained in:
parent
c203e8cc52
commit
a62354d5fb
13 changed files with 130 additions and 33 deletions
|
|
@ -160,6 +160,7 @@
|
|||
<string name="initial_message_tap_header">Tap the card</string>
|
||||
<string name="internal_error_wallet_manager_not_found">Internal error: wallet manager not found</string>
|
||||
<string name="key_invalidated_warning_description">You have updated biometrics, scan your card to enter</string>
|
||||
<string name="main_empty_tokens_list_message">To begin tracking your crypto assets and transactions, add tokens.</string>
|
||||
<string name="main_get_bonus_subtitle">You have completed all of the lessons, and are now eligible to receive your 1INCH tokens</string>
|
||||
<plurals name="main_learn_subtitle">
|
||||
<item quantity="one">Complete three lessons and receive %d 1INCH token to your wallet</item>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -11,7 +11,6 @@ internal class TokenListErrorConverter(
|
|||
private val inProgressStateConverter: InProgressStateConverter,
|
||||
) : Converter<TokenListError, OrganizeTokensState> {
|
||||
|
||||
// TODO: [REDACTED_JIRA]
|
||||
override fun convert(value: TokenListError): OrganizeTokensState {
|
||||
return inProgressStateConverter.convertBack(currentState())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ internal class TokenListSortingErrorConverter(
|
|||
private val inProgressStateConverter: InProgressStateConverter,
|
||||
) : Converter<TokenListSortingError, OrganizeTokensState> {
|
||||
|
||||
// TODO: [REDACTED_JIRA]
|
||||
override fun convert(value: TokenListSortingError): OrganizeTokensState {
|
||||
return inProgressStateConverter.convertBack(currentState())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<TokensListItemState>,
|
||||
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<TokensListItemState>,
|
||||
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<TokensListItemState>,
|
||||
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)),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<WalletTokensListState.TokensListItemState>,
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,6 @@ internal class TokenListErrorConverter(
|
|||
private val currentStateProvider: Provider<WalletState>,
|
||||
) : Converter<TokenListError, WalletMultiCurrencyState.Content> {
|
||||
|
||||
// TODO: [REDACTED_JIRA]
|
||||
override fun convert(value: TokenListError): WalletMultiCurrencyState.Content {
|
||||
return requireNotNull(currentStateProvider() as? WalletMultiCurrencyState.Content).copy(
|
||||
tokensListState = WalletTokensListState.Content(items = persistentListOf(), onOrganizeTokensClick = null),
|
||||
|
|
|
|||
|
|
@ -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<TokensListItemState> {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<WalletTokensListState.TokensListItemState.Token>()
|
||||
.any { it.state is TokenItemState.Loading }
|
||||
|
||||
tokensListState is WalletTokensListState.Loading || hasLoadingTokens
|
||||
}
|
||||
is WalletSingleCurrencyState -> {
|
||||
txHistoryState is TxHistoryState.Loading || marketPriceBlockState is MarketPriceBlockState.Loading
|
||||
|
|
|
|||
13
features/wallet/impl/src/main/res/drawable/ic_empty_64.xml
Normal file
13
features/wallet/impl/src/main/res/drawable/ic_empty_64.xml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="64"
|
||||
android:viewportHeight="64">
|
||||
<group>
|
||||
<clip-path android:pathData="M0,0h64v64h-64z" />
|
||||
<path
|
||||
android:pathData="M55.941,38.668C55.64,37.404 55.239,36.109 54.738,34.798C52.628,38.647 49.782,42.465 46.265,45.981C42.749,49.497 38.931,52.343 35.082,54.454C36.394,54.954 37.688,55.356 38.952,55.657C45.197,57.144 50.274,56.114 53.336,53.052C56.399,49.99 57.428,44.913 55.941,38.668ZM44.498,44.213C48.408,40.303 51.415,36.036 53.455,31.839C51.415,27.641 48.408,23.375 44.497,19.465C40.587,15.554 36.321,12.547 32.123,10.507C27.925,12.547 23.659,15.554 19.749,19.465C15.838,23.375 12.831,27.641 10.791,31.839C12.831,36.036 15.838,40.303 19.749,44.213C23.659,48.124 27.925,51.131 32.123,53.171C36.321,51.131 40.587,48.124 44.498,44.213ZM55.104,54.82C60.196,49.728 60.286,40.89 56.215,31.839C60.286,22.787 60.196,13.95 55.104,8.858C50.012,3.766 41.175,3.676 32.123,7.747C23.071,3.676 14.234,3.766 9.142,8.858C4.05,13.95 3.96,22.787 8.031,31.839C3.96,40.89 4.05,49.728 9.142,54.82C14.234,59.912 23.071,60.002 32.123,55.931C41.175,60.002 50.012,59.912 55.104,54.82ZM38.952,8.021C37.688,8.322 36.394,8.723 35.082,9.223C38.931,11.334 42.749,14.18 46.265,17.697C49.782,21.213 52.628,25.031 54.738,28.88C55.239,27.568 55.64,26.274 55.941,25.01C57.428,18.764 56.399,13.688 53.336,10.626C50.274,7.563 45.197,6.534 38.952,8.021ZM25.294,8.021C26.558,8.322 27.853,8.723 29.164,9.223C25.315,11.334 21.497,14.18 17.981,17.697C14.465,21.213 11.619,25.031 9.508,28.88C9.008,27.568 8.606,26.274 8.305,25.01C6.818,18.764 7.847,13.688 10.91,10.626C13.972,7.563 19.049,6.534 25.294,8.021ZM8.305,38.668C8.606,37.404 9.008,36.109 9.508,34.798C11.619,38.647 14.465,42.465 17.981,45.981C21.497,49.497 25.315,52.343 29.164,54.454C27.853,54.954 26.558,55.356 25.294,55.657C19.049,57.144 13.972,56.114 10.91,53.052C7.847,49.99 6.818,44.913 8.305,38.668ZM32.853,27.774L32,22L31.147,27.774C30.889,29.519 29.519,30.889 27.774,31.147L22,32L27.774,32.853C29.519,33.111 30.889,34.481 31.147,36.225L32,42L32.853,36.225C33.111,34.481 34.481,33.111 36.225,32.853L42,32L36.225,31.147C34.481,30.889 33.111,29.519 32.853,27.774Z"
|
||||
android:fillColor="#C9C9C9"
|
||||
android:fillType="evenOdd" />
|
||||
</group>
|
||||
</vector>
|
||||
Loading…
Add table
Add a link
Reference in a new issue