Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-21 15:00:24 +04:00
parent c6fc1ab4da
commit 56992e4431
3 changed files with 91 additions and 294 deletions

View file

@ -1,58 +1,26 @@
package com.tangem.features.markets.portfolio.impl.ui
import android.content.res.Configuration
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.currency.icon.CoinIcon
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.res.LocalHapticManager
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import com.tangem.core.ui.components.token.TokenItem
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.markets.impl.R
import com.tangem.features.markets.portfolio.impl.ui.preview.PreviewMyPortfolioUMProvider
import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioTokenUM
import com.tangem.utils.StringsSigns
import com.tangem.utils.StringsSigns.DASH_SIGN
import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState as TokenFiatAmountState
// TODO add rest of the balance states ([REDACTED_TASK_KEY] [Markets] Portfolio token item UI Improvement)
@OptIn(ExperimentalFoundationApi::class)
@Composable
internal fun PortfolioItem(state: PortfolioTokenUM, lastInList: Boolean, modifier: Modifier = Modifier) {
val hapticManager = LocalHapticManager.current
Column(modifier) {
Row(
modifier = Modifier
.combinedClickable(
onClick = {
hapticManager.perform(TangemHapticEffect.View.ContextClick)
state.onClick()
},
onLongClick = {
hapticManager.perform(TangemHapticEffect.View.LongPress)
state.onLongTap()
},
)
.clip(TangemTheme.shapes.roundedCornersXMedium)
.padding(
vertical = TangemTheme.dimens.spacing15,
horizontal = TangemTheme.dimens.spacing12,
),
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
verticalAlignment = Alignment.CenterVertically,
) {
Content(state)
}
TokenItem(state = state.tokenItemState, isBalanceHidden = state.isBalanceHidden)
PortfolioQuickActions(
modifier = Modifier.padding(
@ -68,225 +36,77 @@ internal fun PortfolioItem(state: PortfolioTokenUM, lastInList: Boolean, modifie
}
}
@Composable
private fun RowScope.Content(state: PortfolioTokenUM) {
// TODO add custom token
CoinIcon(
modifier = Modifier.size(TangemTheme.dimens.size36),
url = state.iconUrl,
alpha = 1f, // TODO add disabled state
colorFilter = null,
fallbackResId = R.drawable.ic_custom_token_44,
)
Column(
modifier = Modifier.align(Alignment.CenterVertically),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),
) {
when (state.balanceContent) {
is PortfolioTokenUM.BalanceContent.Disabled -> {
Disabled(
state = state,
disabledText = state.balanceContent.text.resolveReference(),
)
}
PortfolioTokenUM.BalanceContent.Loading -> {
Loading(state = state)
}
is PortfolioTokenUM.BalanceContent.TokenBalance -> {
TokenBalance(
state = state,
content = state.balanceContent,
)
}
}
}
}
@Composable
private fun ColumnScope.TokenBalance(state: PortfolioTokenUM, content: PortfolioTokenUM.BalanceContent.TokenBalance) {
val balance = if (content.hidden) {
StringsSigns.STARS
} else {
content.balance
}
val tokenAmount = if (content.hidden) {
StringsSigns.STARS
} else {
content.tokenAmount
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
modifier = Modifier.alignByBaseline(),
text = state.title,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
)
Text(
modifier = Modifier.alignByBaseline(),
text = balance,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = state.subtitle,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
Text(
text = tokenAmount,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
}
@Composable
private fun ColumnScope.Loading(state: PortfolioTokenUM) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
modifier = Modifier.alignByBaseline(),
text = state.title,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
)
TextShimmer(
modifier = Modifier
.width(TangemTheme.dimens.size40)
.alignByBaseline(),
style = TangemTheme.typography.body2,
textSizeHeight = true,
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = state.subtitle,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
TextShimmer(
modifier = Modifier
.width(TangemTheme.dimens.size40)
.alignByBaseline(),
style = TangemTheme.typography.caption2,
textSizeHeight = true,
)
}
}
@Composable
private fun Disabled(state: PortfolioTokenUM, disabledText: String, modifier: Modifier = Modifier) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
verticalAlignment = Alignment.CenterVertically,
) {
Column(
Modifier.weight(1f),
) {
Text(
text = state.title,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
)
Text(
text = state.subtitle,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
Text(
text = disabledText,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun Preview() {
private fun Preview(@PreviewParameter(PortfolioTokenUMProvider::class) tokenUM: PortfolioTokenUM) {
TangemThemePreview {
var quickActionsShown by remember { mutableStateOf(false) }
var quickActionsShown2 by remember { mutableStateOf(false) }
val sampleToken = PreviewMyPortfolioUMProvider().sampleToken
var quickActionsShown by remember { mutableStateOf(value = false) }
Box(
Modifier
.fillMaxSize()
.background(TangemTheme.colors.background.primary),
) {
Column {
PortfolioItem(
state = sampleToken
.copy(
onClick = {
if (quickActionsShown2) {
quickActionsShown2 = false
}
quickActionsShown = quickActionsShown.not()
},
isQuickActionsShown = quickActionsShown,
),
lastInList = true,
)
PortfolioItem(
state = sampleToken
.copy(
onClick = {
if (quickActionsShown) {
quickActionsShown = false
}
quickActionsShown2 = quickActionsShown2.not()
},
isQuickActionsShown = quickActionsShown2,
),
lastInList = true,
)
PortfolioItem(
state = sampleToken
.copy(
balanceContent = (
sampleToken.balanceContent
as PortfolioTokenUM.BalanceContent.TokenBalance
)
.copy(hidden = true),
),
lastInList = true,
)
PortfolioItem(
state = sampleToken
.copy(
balanceContent = PortfolioTokenUM.BalanceContent.Disabled(
stringReference("No Address"),
),
),
lastInList = true,
)
PortfolioItem(
state = sampleToken
.copy(balanceContent = PortfolioTokenUM.BalanceContent.Loading),
lastInList = true,
)
}
val onItemClick = {
quickActionsShown = quickActionsShown.not()
}
PortfolioItem(
state = tokenUM.copy(
tokenItemState = when (tokenUM.tokenItemState) {
is TokenItemState.Content -> tokenUM.tokenItemState.copy(onItemClick = onItemClick)
is TokenItemState.Unreachable -> tokenUM.tokenItemState.copy(onItemClick = onItemClick)
else -> tokenUM.tokenItemState
},
isQuickActionsShown = quickActionsShown,
),
lastInList = true,
)
}
}
private class PortfolioTokenUMProvider : CollectionPreviewParameterProvider<PortfolioTokenUM>(
collection = listOf(
tokenUM.copy(
tokenItemState = (tokenUM.tokenItemState as TokenItemState.Content).copy(
fiatAmountState = contentFiatAmount.copy(hasStaked = true),
),
),
tokenUM.copy(
tokenItemState = tokenUM.tokenItemState.copy(
fiatAmountState = contentFiatAmount.copy(text = DASH_SIGN),
cryptoAmountState = tokenUM.tokenItemState.cryptoAmountState.copy(text = DASH_SIGN),
),
),
tokenUM.copy(isBalanceHidden = true),
tokenUM.copy(
tokenItemState = TokenItemState.Unreachable(
id = tokenUM.tokenItemState.id,
iconState = tokenUM.tokenItemState.iconState,
titleState = tokenUM.tokenItemState.titleState,
subtitleState = tokenUM.tokenItemState.subtitleState,
onItemClick = {},
onItemLongClick = {},
),
),
tokenUM.copy(
tokenItemState = TokenItemState.NoAddress(
id = tokenUM.tokenItemState.id,
iconState = tokenUM.tokenItemState.iconState,
titleState = tokenUM.tokenItemState.titleState,
subtitleState = tokenUM.tokenItemState.subtitleState,
onItemLongClick = {},
),
),
tokenUM.copy(
tokenItemState = TokenItemState.Loading(
id = tokenUM.tokenItemState.id,
iconState = tokenUM.tokenItemState.iconState,
titleState = tokenUM.tokenItemState.titleState as TokenItemState.TitleState.Content,
subtitleState = tokenUM.tokenItemState.subtitleState,
),
),
),
) {
companion object {
val tokenUM = PreviewMyPortfolioUMProvider().sampleToken
val contentFiatAmount = tokenUM.tokenItemState.fiatAmountState as TokenFiatAmountState.Content
}
}

View file

@ -1,6 +1,8 @@
package com.tangem.features.markets.portfolio.impl.ui.preview
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM
import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioTokenUM
import kotlinx.collections.immutable.persistentListOf
@ -32,19 +34,18 @@ internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider<MyPortfol
)
val sampleToken = PortfolioTokenUM(
id = "",
networkId = "",
iconUrl = "",
balanceContent = PortfolioTokenUM.BalanceContent.TokenBalance(
balance = "486,65 \$",
tokenAmount = "733,71097 MATIC",
hidden = false,
tokenItemState = TokenItemState.Content(
id = "",
iconState = CurrencyIconState.Locked,
titleState = TokenItemState.TitleState.Content(text = "My wallet"),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "486,65 \$"),
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = "733,71097 MATIC"),
subtitleState = TokenItemState.SubtitleState.TextContent(value = "XRP Ledger token"),
onItemClick = {},
onItemLongClick = {},
),
title = "My wallet",
subtitle = "XRP Ledger token",
onClick = {},
onLongTap = {},
isQuickActionsShown = false,
onQuickActionClick = {},
isBalanceHidden = false,
)
}

View file

@ -1,34 +1,10 @@
package com.tangem.features.markets.portfolio.impl.ui.state
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.components.token.state.TokenItemState
internal data class PortfolioTokenUM(
val id: String,
val networkId: String,
val iconUrl: String,
val title: String,
val subtitle: String,
val balanceContent: BalanceContent,
val onClick: () -> Unit,
val onLongTap: () -> Unit,
val tokenItemState: TokenItemState,
val isBalanceHidden: Boolean,
val isQuickActionsShown: Boolean,
val onQuickActionClick: (QuickActionUM) -> Unit,
) {
// TODO add rest of the balance states ([REDACTED_TASK_KEY] [Markets] Portfolio token item UI Improvement)
@Immutable
sealed class BalanceContent {
data class TokenBalance( // TODO Add stacking ([REDACTED_TASK_KEY] [Markets] Add staking info to portfolio token item)
val balance: String,
val tokenAmount: String,
val hidden: Boolean,
) : BalanceContent()
data class Disabled(
val text: TextReference,
) : BalanceContent()
data object Loading : BalanceContent()
}
}
)