Updated on 2026-08-14
This commit is contained in:
parent
db0c6cc933
commit
606ae0918b
13 changed files with 129 additions and 77 deletions
|
|
@ -31,7 +31,11 @@ fun HorizontalActionChips(
|
|||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
items(items = buttons, itemContent = { ActionButton(config = it) })
|
||||
items(
|
||||
items = buttons,
|
||||
key = { config -> "${config.text.hashCode()} ${config.iconResId}" },
|
||||
itemContent = { ActionButton(config = it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
|
||||
import com.tangem.core.ui.components.transactions.state.TransactionState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState
|
||||
|
|
@ -142,7 +142,7 @@ internal object WalletPreviewData {
|
|||
)
|
||||
}
|
||||
|
||||
val loadingTokenItemState by lazy { TokenItemState.Loading }
|
||||
val loadingTokenItemState by lazy { TokenItemState.Loading(id = "Loading#1") }
|
||||
|
||||
private const val networksSize = 10
|
||||
private const val tokensSize = 3
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.feature.wallet.presentation.common.component
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -16,11 +19,11 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.constraintlayout.compose.ConstrainedLayoutReference
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.ConstraintLayoutScope
|
||||
|
|
@ -124,7 +127,7 @@ internal fun UnreachableTokenItem(state: TokenItemState.Unreachable, modifier: M
|
|||
options = { ref ->
|
||||
Text(
|
||||
modifier = Modifier.constrainAsOptionsItem(scope = this, ref),
|
||||
text = "Unreachable", // TODO (conform this text)
|
||||
text = stringResource(id = R.string.common_unreachable),
|
||||
style = TangemTypography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
|
|
@ -143,7 +146,7 @@ private fun LoadingTokenItem(modifier: Modifier = Modifier) {
|
|||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24),
|
||||
) {
|
||||
CircleShimmer(modifier = Modifier.size(size = TangemTheme.dimens.size42))
|
||||
Row(
|
||||
|
|
@ -188,22 +191,17 @@ private fun LoadingTokenItem(modifier: Modifier = Modifier) {
|
|||
* Block for end part of token item
|
||||
* shows status is reachable, is drag, hidden or show balance
|
||||
*/
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
private fun TokenOptionsBlock(state: TokenOptionsState, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is TokenOptionsState.Visible -> {
|
||||
TokenFiatPercentageBlock(
|
||||
modifier = modifier,
|
||||
fiatAmount = state.fiatAmount,
|
||||
priceChange = state.priceChange,
|
||||
)
|
||||
}
|
||||
is TokenOptionsState.Hidden -> {
|
||||
TokenFiatPercentageBlock(
|
||||
modifier = modifier,
|
||||
fiatAmount = DOTS,
|
||||
priceChange = state.priceChange,
|
||||
)
|
||||
AnimatedContent(targetState = state, label = "Update the options", modifier = modifier) { options ->
|
||||
when (options) {
|
||||
is TokenOptionsState.Visible -> {
|
||||
TokenFiatPercentageBlock(fiatAmount = options.fiatAmount, priceChange = options.priceChange)
|
||||
}
|
||||
is TokenOptionsState.Hidden -> {
|
||||
TokenFiatPercentageBlock(fiatAmount = DOTS, priceChange = options.priceChange)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -243,7 +241,7 @@ private fun InternalTokenItem(
|
|||
|
||||
TokenTitleAmountBlock(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing12)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing8)
|
||||
.constrainAs(tokenNameItem) {
|
||||
centerVerticallyTo(parent)
|
||||
start.linkTo(iconItem.end)
|
||||
|
|
@ -298,17 +296,18 @@ private fun TokenTitleAmountBlock(title: String, amount: String?, hasPending: Bo
|
|||
style = TangemTypography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
if (hasPending) {
|
||||
|
||||
AnimatedVisibility(visible = hasPending, modifier = Modifier.align(Alignment.CenterVertically)) {
|
||||
Image(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
painter = painterResource(id = R.drawable.img_loader_15),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!amount.isNullOrBlank()) {
|
||||
|
||||
AnimatedVisibility(visible = !amount.isNullOrBlank()) {
|
||||
Text(
|
||||
text = amount,
|
||||
text = requireNotNull(amount),
|
||||
style = TangemTypography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
|
|
@ -316,6 +315,7 @@ private fun TokenTitleAmountBlock(title: String, amount: String?, hasPending: Bo
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
private fun TokenFiatPercentageBlock(
|
||||
fiatAmount: String,
|
||||
|
|
@ -334,30 +334,39 @@ private fun TokenFiatPercentageBlock(
|
|||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
val iconChangeArrow: Int
|
||||
val changeTextColor: Color
|
||||
when (priceChange.type) {
|
||||
PriceChangeConfig.Type.UP -> {
|
||||
iconChangeArrow = R.drawable.img_arrow_up_8
|
||||
changeTextColor = TangemTheme.colors.text.accent
|
||||
}
|
||||
PriceChangeConfig.Type.DOWN -> {
|
||||
iconChangeArrow = R.drawable.img_arrow_down_8
|
||||
changeTextColor = TangemTheme.colors.text.warning
|
||||
}
|
||||
AnimatedContent(
|
||||
targetState = priceChange.type,
|
||||
label = "Update the price change's arrow",
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(
|
||||
id = when (priceChange.type) {
|
||||
PriceChangeConfig.Type.UP -> R.drawable.img_arrow_up_8
|
||||
PriceChangeConfig.Type.DOWN -> R.drawable.img_arrow_down_8
|
||||
},
|
||||
),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
Image(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
painter = painterResource(id = iconChangeArrow),
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
SpacerW4()
|
||||
Text(
|
||||
|
||||
AnimatedContent(
|
||||
targetState = priceChange.type,
|
||||
label = "Update the price change's arrow",
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
text = priceChange.valueInPercent,
|
||||
style = TangemTypography.body2,
|
||||
color = changeTextColor,
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
text = priceChange.valueInPercent,
|
||||
style = TangemTypography.body2,
|
||||
color = when (priceChange.type) {
|
||||
PriceChangeConfig.Type.UP -> TangemTheme.colors.text.accent
|
||||
PriceChangeConfig.Type.DOWN -> TangemTheme.colors.text.warning
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -389,20 +398,20 @@ private fun TokenIcon(
|
|||
contentDescription = null,
|
||||
)
|
||||
|
||||
if (networkIconRes != null) {
|
||||
Box(
|
||||
AnimatedVisibility(
|
||||
visible = networkIconRes != null,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.size(TangemTheme.dimens.size18)
|
||||
.background(color = Color.White, shape = CircleShape),
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.size(TangemTheme.dimens.size18)
|
||||
.background(color = Color.White, shape = CircleShape),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.padding(all = 0.5.dp),
|
||||
painter = painterResource(id = networkIconRes),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
.padding(all = TangemTheme.dimens.spacing0_5)
|
||||
.align(Alignment.Center),
|
||||
painter = painterResource(id = requireNotNull(networkIconRes)),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,16 @@ import com.tangem.core.ui.components.marketprice.PriceChangeConfig
|
|||
@Immutable
|
||||
internal sealed interface TokenItemState {
|
||||
|
||||
/** Unique id */
|
||||
val id: String
|
||||
|
||||
/** Loading token state */
|
||||
object Loading : TokenItemState
|
||||
data class Loading(override val id: String) : TokenItemState
|
||||
|
||||
/**
|
||||
* Content token state
|
||||
*
|
||||
* @property id unique id
|
||||
* @property tokenIconUrl token icon url
|
||||
* @property tokenIconResId token icon resource id
|
||||
* @property networkIconResId network icon resource id, may be null if it is a coin
|
||||
|
|
@ -23,7 +27,7 @@ internal sealed interface TokenItemState {
|
|||
* @property tokenOptions state for token options
|
||||
*/
|
||||
data class Content(
|
||||
val id: String,
|
||||
override val id: String,
|
||||
val tokenIconUrl: String?,
|
||||
@DrawableRes val tokenIconResId: Int,
|
||||
@DrawableRes val networkIconResId: Int?,
|
||||
|
|
@ -36,13 +40,15 @@ internal sealed interface TokenItemState {
|
|||
/**
|
||||
* Draggable token state
|
||||
*
|
||||
* @property id unique id
|
||||
* @property tokenIconUrl token icon url
|
||||
* @property tokenIconResId token icon resource id
|
||||
* @property networkIconResId network icon resource id, may be null if it is a coin
|
||||
* @property name token name
|
||||
* @property fiatAmount fiat amount of token
|
||||
*/
|
||||
data class Draggable(
|
||||
val id: String,
|
||||
override val id: String,
|
||||
val tokenIconUrl: String?,
|
||||
@DrawableRes val tokenIconResId: Int,
|
||||
@DrawableRes val networkIconResId: Int?,
|
||||
|
|
@ -60,7 +66,7 @@ internal sealed interface TokenItemState {
|
|||
* @property name token name
|
||||
*/
|
||||
data class Unreachable(
|
||||
val id: String,
|
||||
override val id: String,
|
||||
val tokenIconUrl: String?,
|
||||
@DrawableRes val tokenIconResId: Int,
|
||||
@DrawableRes val networkIconResId: Int?,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.components
|
||||
|
||||
import com.tangem.core.ui.components.wallet.WalletLockedContentState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.core.ui.components.wallet.WalletLockedContentState
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -23,9 +23,8 @@ internal sealed class WalletTokensListState(
|
|||
/** Loading content state */
|
||||
object Loading : WalletTokensListState(
|
||||
items = persistentListOf(
|
||||
TokensListItemState.NetworkGroupTitle(value = TextReference.Res(id = R.string.main_tokens)),
|
||||
TokensListItemState.Token(state = TokenItemState.Loading),
|
||||
TokensListItemState.Token(state = TokenItemState.Loading),
|
||||
TokensListItemState.Token(state = TokenItemState.Loading(id = FIRST_LOADING_TOKEN_ID)),
|
||||
TokensListItemState.Token(state = TokenItemState.Loading(id = SECOND_LOADING_TOKEN_ID)),
|
||||
),
|
||||
onOrganizeTokensClick = null,
|
||||
)
|
||||
|
|
@ -46,7 +45,7 @@ internal sealed class WalletTokensListState(
|
|||
WalletTokensListState(
|
||||
items = persistentListOf(
|
||||
TokensListItemState.NetworkGroupTitle(value = TextReference.Res(id = R.string.main_tokens)),
|
||||
TokensListItemState.Token(state = TokenItemState.Loading),
|
||||
TokensListItemState.Token(state = TokenItemState.Loading(id = LOCKED_TOKEN_ID)),
|
||||
),
|
||||
onOrganizeTokensClick = null,
|
||||
),
|
||||
|
|
@ -69,4 +68,10 @@ internal sealed class WalletTokensListState(
|
|||
*/
|
||||
data class Token(val state: TokenItemState) : TokensListItemState
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val FIRST_LOADING_TOKEN_ID = "Loading#1"
|
||||
const val SECOND_LOADING_TOKEN_ID = "Loading#2"
|
||||
const val LOCKED_TOKEN_ID = "Locked#1"
|
||||
}
|
||||
}
|
||||
|
|
@ -45,7 +45,11 @@ internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState
|
|||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState),
|
||||
) {
|
||||
items(items = config.wallets, key = { it.id.stringValue }) { state ->
|
||||
items(
|
||||
items = config.wallets,
|
||||
key = { it.id.stringValue },
|
||||
contentType = { it::class.java },
|
||||
) { state ->
|
||||
WalletCard(
|
||||
state = state,
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ internal fun LazyListScope.notifications(configs: ImmutableList<WalletNotificati
|
|||
items(
|
||||
items = configs,
|
||||
key = { it.state.title.hashCode() },
|
||||
contentType = { it.state::class.java },
|
||||
itemContent = { Notification(state = it.state, modifier = modifier.animateItemPlacement()) },
|
||||
)
|
||||
}
|
||||
|
|
@ -19,7 +19,13 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletToke
|
|||
internal fun LazyListScope.tokensListItems(state: WalletTokensListState, modifier: Modifier = Modifier) {
|
||||
itemsIndexed(
|
||||
items = state.items,
|
||||
key = { index, _ -> index },
|
||||
key = { _, item ->
|
||||
when (item) {
|
||||
is WalletTokensListState.TokensListItemState.NetworkGroupTitle -> item.value.hashCode()
|
||||
is WalletTokensListState.TokensListItemState.Token -> item.state.id
|
||||
}
|
||||
},
|
||||
contentType = { _, item -> item::class.java },
|
||||
itemContent = { index, item ->
|
||||
MultiCurrencyContentItem(
|
||||
state = item,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import androidx.compose.foundation.ExperimentalFoundationApi
|
|||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
private const val ORGANIZE_BUTTON_CONTENT_TYPE = "OrganizeTokensButton"
|
||||
|
||||
/**
|
||||
* Organize tokens button
|
||||
*
|
||||
|
|
@ -14,5 +16,7 @@ import androidx.compose.ui.Modifier
|
|||
*/
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
internal fun LazyListScope.organizeButton(onClick: (() -> Unit)?, modifier: Modifier = Modifier) {
|
||||
item { OrganizeTokensButton(onClick = onClick, modifier = modifier.animateItemPlacement()) }
|
||||
item(key = ORGANIZE_BUTTON_CONTENT_TYPE, contentType = ORGANIZE_BUTTON_CONTENT_TYPE) {
|
||||
OrganizeTokensButton(onClick = onClick, modifier = modifier.animateItemPlacement())
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,8 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletMana
|
|||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
private const val CONTROL_BUTTONS_CONTENT_TYPE = "ControlButtons"
|
||||
|
||||
/**
|
||||
* Single currency control buttons. Like, "Buy", "Sell", etc
|
||||
*
|
||||
|
|
@ -20,7 +22,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
*/
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
internal fun LazyListScope.controlButtons(configs: ImmutableList<WalletManageButton>, modifier: Modifier = Modifier) {
|
||||
item {
|
||||
item(key = CONTROL_BUTTONS_CONTENT_TYPE, contentType = CONTROL_BUTTONS_CONTENT_TYPE) {
|
||||
HorizontalActionChips(
|
||||
buttons = configs.map(WalletManageButton::config).toImmutableList(),
|
||||
modifier = modifier.animateItemPlacement(),
|
||||
|
|
|
|||
|
|
@ -16,5 +16,10 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
|||
*/
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
internal fun LazyListScope.marketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier) {
|
||||
item { MarketPriceBlock(state = state, modifier = modifier.animateItemPlacement()) }
|
||||
item(
|
||||
key = MarketPriceBlockState::class.java,
|
||||
contentType = MarketPriceBlockState::class.java,
|
||||
) {
|
||||
MarketPriceBlock(state = state, modifier = modifier.animateItemPlacement())
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ internal class CryptoCurrencyStatusToTokenItemConverter(
|
|||
|
||||
override fun convert(value: CryptoCurrencyStatus): TokenItemState {
|
||||
return when (value.value) {
|
||||
is CryptoCurrencyStatus.Loading -> TokenItemState.Loading
|
||||
is CryptoCurrencyStatus.Loading -> TokenItemState.Loading(id = value.currency.id.value)
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
is CryptoCurrencyStatus.NoQuote,
|
||||
|
|
|
|||
|
|
@ -8,8 +8,14 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
internal object LoadingItemsProvider {
|
||||
|
||||
fun getLoadingMultiCurrencyTokens(): ImmutableList<WalletTokensListState.TokensListItemState.Token> {
|
||||
return buildList(capacity = 5) {
|
||||
add(WalletTokensListState.TokensListItemState.Token(state = TokenItemState.Loading))
|
||||
}.toImmutableList()
|
||||
val items = mutableListOf<WalletTokensListState.TokensListItemState.Token>()
|
||||
repeat(times = 5) {
|
||||
items.add(
|
||||
WalletTokensListState.TokensListItemState.Token(
|
||||
state = TokenItemState.Loading(id = "Loading#$it"),
|
||||
),
|
||||
)
|
||||
}
|
||||
return items.toImmutableList()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue