Updated on 2026-08-14
This commit is contained in:
parent
1a4e8d8f20
commit
17f89eebd5
23 changed files with 1001 additions and 501 deletions
|
|
@ -22,6 +22,7 @@ dependencies {
|
|||
implementation(deps.compose.ui.tooling)
|
||||
implementation(deps.compose.shimmer)
|
||||
implementation(deps.compose.accompanist.systemUiController)
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
package com.tangem.feature.wallet.presentation.common
|
||||
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.feature.wallet.presentation.common.state.NetworkGroupState
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.util.UUID
|
||||
|
||||
internal object WalletPreviewData {
|
||||
|
||||
val walletCardContent = WalletCardState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
balance = "8923,05 $",
|
||||
additionalInfo = "3 cards • Seed enabled",
|
||||
imageResId = R.drawable.ill_businessman_3d,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
val walletCardLoading = WalletCardState.Loading(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
additionalInfo = "3 cards • Seed enabled",
|
||||
imageResId = R.drawable.ill_businessman_3d,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
val walletCardHiddenContent = WalletCardState.HiddenContent(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
additionalInfo = "3 cards • Seed enabled",
|
||||
imageResId = R.drawable.ill_businessman_3d,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
val walletCardError = WalletCardState.Error(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
additionalInfo = "3 cards • Seed enabled",
|
||||
imageResId = R.drawable.ill_businessman_3d,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
val walletScreenState = WalletStateHolder(
|
||||
onBackClick = {},
|
||||
headerConfig = WalletStateHolder.HeaderConfig(
|
||||
wallets = persistentListOf(walletCardContent, walletCardLoading, walletCardHiddenContent, walletCardError),
|
||||
onScanCardClick = {},
|
||||
onMoreClick = {},
|
||||
),
|
||||
)
|
||||
|
||||
val tokenItemVisibleState = TokenItemState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
tokenIconUrl = null,
|
||||
tokenIconResId = R.drawable.img_polygon_22,
|
||||
networkIconResId = R.drawable.img_polygon_22,
|
||||
name = "Polygon",
|
||||
amount = "5,412 MATIC",
|
||||
hasPending = true,
|
||||
tokenOptions = TokenOptionsState.Visible(
|
||||
fiatAmount = "321 $",
|
||||
priceChange = TokenOptionsState.PriceChange(
|
||||
valuePercent = "2%",
|
||||
type = TokenOptionsState.PriceChange.Type.UP,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val tokenItemHiddenState = TokenItemState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
tokenIconUrl = null,
|
||||
tokenIconResId = R.drawable.img_polygon_22,
|
||||
networkIconResId = R.drawable.img_polygon_22,
|
||||
name = "Polygon",
|
||||
amount = "5,412 MATIC",
|
||||
hasPending = true,
|
||||
tokenOptions = TokenOptionsState.Hidden(
|
||||
priceChange = TokenOptionsState.PriceChange(
|
||||
valuePercent = "2%",
|
||||
type = TokenOptionsState.PriceChange.Type.UP,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val tokenItemDragState = TokenItemState.Draggable(
|
||||
id = UUID.randomUUID().toString(),
|
||||
tokenIconUrl = null,
|
||||
tokenIconResId = R.drawable.img_polygon_22,
|
||||
networkIconResId = R.drawable.img_polygon_22,
|
||||
name = "Polygon",
|
||||
fiatAmount = "3 172,14 $",
|
||||
)
|
||||
|
||||
val tokenItemUnreachableState = TokenItemState.Unreachable(
|
||||
id = UUID.randomUUID().toString(),
|
||||
tokenIconUrl = null,
|
||||
tokenIconResId = R.drawable.img_polygon_22,
|
||||
networkIconResId = R.drawable.img_polygon_22,
|
||||
name = "Polygon",
|
||||
)
|
||||
|
||||
val loadingTokenItemState = TokenItemState.Loading(id = UUID.randomUUID().toString())
|
||||
|
||||
private val draggableTokenList = persistentListOf(
|
||||
tokenItemDragState.copy(
|
||||
id = "token_1",
|
||||
name = "Ethereum",
|
||||
tokenIconResId = R.drawable.img_eth_22,
|
||||
networkIconResId = null,
|
||||
fiatAmount = "3 172,14 $",
|
||||
),
|
||||
tokenItemDragState.copy(
|
||||
id = "token_2",
|
||||
networkIconResId = R.drawable.img_eth_22,
|
||||
fiatAmount = "803,65 $",
|
||||
),
|
||||
tokenItemDragState.copy(
|
||||
id = "token_3",
|
||||
name = "USDT",
|
||||
tokenIconResId = R.drawable.img_arbitrum_22,
|
||||
networkIconResId = R.drawable.img_eth_22,
|
||||
fiatAmount = "88,01 $",
|
||||
),
|
||||
)
|
||||
|
||||
val networkGroup = NetworkGroupState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
networkName = "Ethereum",
|
||||
tokens = persistentListOf(
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_1",
|
||||
name = "Ethereum",
|
||||
tokenIconResId = R.drawable.img_eth_22,
|
||||
networkIconResId = null,
|
||||
amount = "1,89340821 ETH",
|
||||
),
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_2",
|
||||
networkIconResId = R.drawable.img_eth_22,
|
||||
amount = "733,71097 MATIC",
|
||||
),
|
||||
tokenItemVisibleState.copy(
|
||||
id = "token_3",
|
||||
name = "USDT",
|
||||
tokenIconResId = R.drawable.img_arbitrum_22,
|
||||
networkIconResId = R.drawable.img_eth_22,
|
||||
amount = "0,25404523 ARB",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val draggableNetworkGroup = NetworkGroupState.Draggable(
|
||||
id = UUID.randomUUID().toString(),
|
||||
networkName = "Ethereum",
|
||||
tokens = draggableTokenList,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package com.tangem.feature.wallet.presentation.common.component
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.common.state.NetworkGroupState
|
||||
|
||||
@Composable
|
||||
internal fun NetworkGroupItem(state: NetworkGroupState, modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.background(TangemTheme.colors.background.primary)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing12)
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = TangemTheme.dimens.size48),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.wallet_network_group_title, state.networkName),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
if (state is NetworkGroupState.Draggable) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_group_drop_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
Column {
|
||||
state.tokens.forEach { token ->
|
||||
key(token.id) {
|
||||
TokenItem(state = token)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun NetworkGroupItemPreview_Light(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) {
|
||||
TangemTheme {
|
||||
NetworkGroupItem(group)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Composable
|
||||
private fun NetworkGroupItemPreview_Dark(@PreviewParameter(NetworkGroupProvider::class) group: NetworkGroupState) {
|
||||
TangemTheme(isDark = true) {
|
||||
NetworkGroupItem(group)
|
||||
}
|
||||
}
|
||||
|
||||
private class NetworkGroupProvider : CollectionPreviewParameterProvider<NetworkGroupState>(
|
||||
collection = listOf(
|
||||
WalletPreviewData.networkGroup,
|
||||
WalletPreviewData.draggableNetworkGroup,
|
||||
),
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -0,0 +1,412 @@
|
|||
package com.tangem.feature.wallet.presentation.common.component
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.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.constraintlayout.compose.ConstrainedLayoutReference
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.ConstraintLayoutScope
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemTypography
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState
|
||||
|
||||
private const val DOTS = "•••"
|
||||
|
||||
@Composable
|
||||
internal fun TokenItem(state: TokenItemState, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is TokenItemState.Content -> ContentTokenItem(state, modifier)
|
||||
is TokenItemState.Loading -> LoadingTokenItem(modifier)
|
||||
is TokenItemState.Draggable -> DraggableTokenItem(state, modifier)
|
||||
is TokenItemState.Unreachable -> UnreachableTokenItem(state, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContentTokenItem(content: TokenItemState.Content, modifier: Modifier = Modifier) {
|
||||
InternalTokenItem(
|
||||
modifier = modifier,
|
||||
name = content.name,
|
||||
tokenIconUrl = content.tokenIconUrl,
|
||||
tokenIconResId = content.tokenIconResId,
|
||||
networkIconResId = content.networkIconResId,
|
||||
amount = if (content.tokenOptions is TokenOptionsState.Hidden) DOTS else content.amount,
|
||||
hasPending = content.hasPending,
|
||||
options = { ref ->
|
||||
TokenOptionsBlock(
|
||||
modifier = Modifier.constrainAsOptionsItem(scope = this, ref),
|
||||
state = content.tokenOptions,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun DraggableTokenItem(state: TokenItemState.Draggable, modifier: Modifier = Modifier) {
|
||||
InternalTokenItem(
|
||||
modifier = modifier,
|
||||
name = state.name,
|
||||
tokenIconUrl = state.tokenIconUrl,
|
||||
tokenIconResId = state.tokenIconResId,
|
||||
networkIconResId = state.networkIconResId,
|
||||
amount = state.fiatAmount,
|
||||
hasPending = false,
|
||||
options = { ref ->
|
||||
Icon(
|
||||
modifier = Modifier.constrainAsOptionsItem(scope = this, ref),
|
||||
painter = painterResource(id = R.drawable.ic_drag_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun UnreachableTokenItem(state: TokenItemState.Unreachable, modifier: Modifier = Modifier) {
|
||||
InternalTokenItem(
|
||||
modifier = modifier,
|
||||
name = state.name,
|
||||
tokenIconUrl = state.tokenIconUrl,
|
||||
tokenIconResId = state.tokenIconResId,
|
||||
networkIconResId = state.networkIconResId,
|
||||
amount = null,
|
||||
hasPending = false,
|
||||
options = { ref ->
|
||||
Text(
|
||||
modifier = Modifier.constrainAsOptionsItem(scope = this, ref),
|
||||
text = "Unreachable", // TODO (conform this text)
|
||||
style = TangemTypography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoadingTokenItem(modifier: Modifier = Modifier) {
|
||||
BaseSurface(modifier) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
CircleShimmer(modifier = Modifier.size(size = TangemTheme.dimens.size42))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(
|
||||
width = TangemTheme.dimens.size72,
|
||||
height = TangemTheme.dimens.size12,
|
||||
),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(
|
||||
width = TangemTheme.dimens.size50,
|
||||
height = TangemTheme.dimens.size12,
|
||||
),
|
||||
)
|
||||
}
|
||||
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(
|
||||
width = TangemTheme.dimens.size40,
|
||||
height = TangemTheme.dimens.size12,
|
||||
),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(
|
||||
width = TangemTheme.dimens.size40,
|
||||
height = TangemTheme.dimens.size12,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Block for end part of token item
|
||||
* shows status is reachable, is drag, hidden or show balance
|
||||
*/
|
||||
@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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
private fun InternalTokenItem(
|
||||
name: String,
|
||||
tokenIconUrl: String?,
|
||||
@DrawableRes tokenIconResId: Int,
|
||||
@DrawableRes networkIconResId: Int?,
|
||||
amount: String?,
|
||||
hasPending: Boolean,
|
||||
options: @Composable ConstraintLayoutScope.(ref: ConstrainedLayoutReference) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BaseSurface(modifier) {
|
||||
ConstraintLayout(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
) {
|
||||
val (iconItem, tokenNameItem, optionsItem) = createRefs()
|
||||
|
||||
TokenIcon(
|
||||
modifier = Modifier.constrainAs(iconItem) {
|
||||
centerVerticallyTo(parent)
|
||||
start.linkTo(parent.start)
|
||||
},
|
||||
tokenIconUrl = tokenIconUrl,
|
||||
tokenIconResId = tokenIconResId,
|
||||
networkIconRes = networkIconResId,
|
||||
)
|
||||
|
||||
TokenTitleAmountBlock(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing12)
|
||||
.constrainAs(tokenNameItem) {
|
||||
centerVerticallyTo(parent)
|
||||
start.linkTo(iconItem.end)
|
||||
end.linkTo(optionsItem.start)
|
||||
width = Dimension.fillToConstraints
|
||||
},
|
||||
title = name,
|
||||
amount = amount,
|
||||
hasPending = hasPending,
|
||||
)
|
||||
|
||||
options(optionsItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Stable
|
||||
private fun Modifier.constrainAsOptionsItem(scope: ConstraintLayoutScope, ref: ConstrainedLayoutReference): Modifier {
|
||||
return with(scope) {
|
||||
this@constrainAsOptionsItem.constrainAs(ref) {
|
||||
centerVerticallyTo(parent)
|
||||
end.linkTo(parent.end)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BaseSurface(
|
||||
modifier: Modifier = Modifier,
|
||||
onClick: (() -> Unit)? = null,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Surface(
|
||||
modifier = modifier.defaultMinSize(minHeight = TangemTheme.dimens.size68),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
onClick = onClick ?: {},
|
||||
enabled = onClick != null,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenTitleAmountBlock(title: String, amount: String?, hasPending: Boolean, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
|
||||
Text(
|
||||
text = title,
|
||||
style = TangemTypography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
if (hasPending) {
|
||||
Image(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
painter = painterResource(id = R.drawable.img_loader_15),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!amount.isNullOrBlank()) {
|
||||
Text(
|
||||
text = amount,
|
||||
style = TangemTypography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenFiatPercentageBlock(
|
||||
fiatAmount: String,
|
||||
priceChange: TokenOptionsState.PriceChange,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier.requiredWidth(IntrinsicSize.Max)) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
text = fiatAmount,
|
||||
style = TangemTypography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerH2()
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
val iconChangeArrow: Int
|
||||
val changeTextColor: Color
|
||||
when (priceChange.type) {
|
||||
TokenOptionsState.PriceChange.Type.UP -> {
|
||||
iconChangeArrow = R.drawable.img_arrow_up_8
|
||||
changeTextColor = TangemTheme.colors.text.accent
|
||||
}
|
||||
TokenOptionsState.PriceChange.Type.DOWN -> {
|
||||
iconChangeArrow = R.drawable.img_arrow_down_8
|
||||
changeTextColor = TangemTheme.colors.text.warning
|
||||
}
|
||||
}
|
||||
Image(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
painter = painterResource(id = iconChangeArrow),
|
||||
contentDescription = null,
|
||||
)
|
||||
SpacerW4()
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
text = priceChange.valuePercent,
|
||||
style = TangemTypography.body2,
|
||||
color = changeTextColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenIcon(
|
||||
tokenIconUrl: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
@DrawableRes tokenIconResId: Int? = null,
|
||||
@DrawableRes networkIconRes: Int? = null,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(end = TangemTheme.dimens.spacing16)
|
||||
.size(TangemTheme.dimens.size42),
|
||||
) {
|
||||
val tokenImageModifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.size(TangemTheme.dimens.size36)
|
||||
|
||||
val data = if (tokenIconUrl.isNullOrEmpty()) tokenIconResId else tokenIconUrl
|
||||
SubcomposeAsyncImage(
|
||||
modifier = tokenImageModifier,
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(data)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
loading = { CircleShimmer(modifier = tokenImageModifier) },
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
if (networkIconRes != null) {
|
||||
Box(
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region preview
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_Tokens_LightTheme(@PreviewParameter(TokenConfigProvider::class) state: TokenItemState) {
|
||||
TangemTheme(isDark = false) {
|
||||
TokenItem(state)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun Preview_Tokens_DarkTheme(@PreviewParameter(TokenConfigProvider::class) state: TokenItemState) {
|
||||
TangemTheme(isDark = true) {
|
||||
TokenItem(state)
|
||||
}
|
||||
}
|
||||
|
||||
private class TokenConfigProvider : CollectionPreviewParameterProvider<TokenItemState>(
|
||||
collection = listOf(
|
||||
WalletPreviewData.tokenItemVisibleState,
|
||||
WalletPreviewData.tokenItemUnreachableState,
|
||||
WalletPreviewData.tokenItemDragState,
|
||||
WalletPreviewData.tokenItemHiddenState,
|
||||
WalletPreviewData.loadingTokenItemState,
|
||||
),
|
||||
)
|
||||
|
||||
// endregion preview
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package com.tangem.feature.wallet.presentation.common.state
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState.PriceChange.Type
|
||||
|
||||
/** Token item state */
|
||||
@Immutable
|
||||
internal sealed interface TokenItemState {
|
||||
|
||||
/** Token id */
|
||||
val id: String
|
||||
|
||||
/**
|
||||
* Loading token state
|
||||
*
|
||||
* @property id token id
|
||||
*/
|
||||
data class Loading(override val id: String) : TokenItemState
|
||||
|
||||
/**
|
||||
* Content token state
|
||||
*
|
||||
* @property id token 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 amount amount of token
|
||||
* @property hasPending pending tx in blockchain
|
||||
* @property tokenOptions state for token options
|
||||
*/
|
||||
data class Content(
|
||||
override val id: String,
|
||||
val tokenIconUrl: String?,
|
||||
@DrawableRes val tokenIconResId: Int,
|
||||
@DrawableRes val networkIconResId: Int?,
|
||||
val name: String,
|
||||
val amount: String,
|
||||
val hasPending: Boolean,
|
||||
val tokenOptions: TokenOptionsState,
|
||||
) : TokenItemState
|
||||
|
||||
/**
|
||||
* Draggable token state
|
||||
*
|
||||
* @property id token 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
|
||||
*/
|
||||
data class Draggable(
|
||||
override val id: String,
|
||||
val tokenIconUrl: String?,
|
||||
@DrawableRes val tokenIconResId: Int,
|
||||
@DrawableRes val networkIconResId: Int?,
|
||||
val name: String,
|
||||
val fiatAmount: String,
|
||||
) : TokenItemState
|
||||
|
||||
/**
|
||||
* Unreachable token state
|
||||
*
|
||||
* @property id token 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
|
||||
*/
|
||||
data class Unreachable(
|
||||
override val id: String,
|
||||
val tokenIconUrl: String?,
|
||||
@DrawableRes val tokenIconResId: Int,
|
||||
@DrawableRes val networkIconResId: Int?,
|
||||
val name: String,
|
||||
) : TokenItemState
|
||||
|
||||
/** Token options state */
|
||||
sealed interface TokenOptionsState {
|
||||
|
||||
/**
|
||||
* Visible token options state
|
||||
*
|
||||
* @property fiatAmount fiat amount of token
|
||||
* @property priceChange value of price changing
|
||||
*/
|
||||
data class Visible(val fiatAmount: String, val priceChange: PriceChange) : TokenOptionsState
|
||||
|
||||
/**
|
||||
* Hidden token options state
|
||||
*
|
||||
* @property priceChange value of price changing
|
||||
*/
|
||||
data class Hidden(val priceChange: PriceChange) : TokenOptionsState
|
||||
|
||||
/**
|
||||
* Price changing option state
|
||||
*
|
||||
* @property valuePercent value in percent
|
||||
* @property type type [Type]
|
||||
*/
|
||||
data class PriceChange(val valuePercent: String, val type: Type) {
|
||||
|
||||
/** Price changing type */
|
||||
enum class Type {
|
||||
UP, DOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.tangem.feature.wallet.presentation.common.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
internal sealed interface TokenListState {
|
||||
|
||||
data class GroupedByNetwork(
|
||||
val groups: ImmutableList<NetworkGroupState>,
|
||||
) : TokenListState
|
||||
|
||||
data class Ungrouped(
|
||||
val tokens: ImmutableList<TokenItemState>,
|
||||
) : TokenListState
|
||||
}
|
||||
|
||||
@Immutable
|
||||
internal sealed interface NetworkGroupState {
|
||||
val id: String
|
||||
val networkName: String
|
||||
val tokens: ImmutableList<TokenItemState>
|
||||
|
||||
data class Draggable(
|
||||
override val id: String,
|
||||
override val networkName: String,
|
||||
override val tokens: ImmutableList<TokenItemState.Draggable>,
|
||||
) : NetworkGroupState
|
||||
|
||||
data class Content(
|
||||
override val id: String,
|
||||
override val networkName: String,
|
||||
override val tokens: ImmutableList<TokenItemState>,
|
||||
) : NetworkGroupState
|
||||
}
|
||||
|
|
@ -10,8 +10,8 @@ import androidx.navigation.compose.composable
|
|||
import androidx.navigation.compose.rememberNavController
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.presentation.WalletFragment
|
||||
import com.tangem.feature.wallet.presentation.ui.WalletScreen
|
||||
import com.tangem.feature.wallet.presentation.viewmodels.WalletViewModel
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
/** Default implementation of wallet feature router */
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
package com.tangem.feature.wallet.presentation.router
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import com.tangem.features.wallet.navigation.WalletRouter
|
||||
|
||||
/**
|
||||
* Interface of inner wallet feature router
|
||||
*
|
||||
* Annotated as [Stable] because implementation of this interface passed as argument to [Initialize] method by compose
|
||||
* compiler
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Stable
|
||||
internal interface InnerWalletRouter : WalletRouter {
|
||||
|
||||
/** Initialize router */
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.state
|
||||
|
||||
internal enum class TokenUIState {
|
||||
LOADED, LOADING
|
||||
}
|
||||
|
||||
internal enum class TokenOptionsUIState {
|
||||
VISIBLE, HIDDEN, DRAG, UNREACHABLE
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.state
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
/**
|
||||
* Token config
|
||||
*
|
||||
* @property name of token/coin
|
||||
* @property amount of token
|
||||
* @property fiatAmount amount in fiat
|
||||
* @property priceChange value of price changing
|
||||
* @property iconUrl
|
||||
* @property icon token/coin icon
|
||||
* @property networkIcon
|
||||
* @property tokenUIState token state [TokenUIState] loading etc.
|
||||
* @property tokenOptionsUIState state for token options like 'unreachable', 'hidden' etc
|
||||
* @property hasPending pending tx in blockchain
|
||||
*/
|
||||
internal data class TokenV2Config(
|
||||
val name: String,
|
||||
val amount: String,
|
||||
val fiatAmount: String,
|
||||
val priceChange: PriceChange,
|
||||
val iconUrl: String?,
|
||||
@DrawableRes val icon: Int?,
|
||||
@DrawableRes val networkIcon: Int,
|
||||
val tokenUIState: TokenUIState,
|
||||
val tokenOptionsUIState: TokenOptionsUIState,
|
||||
val hasPending: Boolean,
|
||||
)
|
||||
|
||||
data class PriceChange(
|
||||
val valuePercent: String,
|
||||
val type: PriceChangeType,
|
||||
)
|
||||
|
||||
enum class PriceChangeType {
|
||||
UP, DOWN
|
||||
}
|
||||
|
|
@ -1,352 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.ui
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemTypography
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.state.*
|
||||
|
||||
private const val DOTS = "•••"
|
||||
|
||||
@Composable
|
||||
internal fun TokenItemV2(config: TokenV2Config) {
|
||||
when (config.tokenUIState) {
|
||||
TokenUIState.LOADED -> LoadedTokenState(config)
|
||||
TokenUIState.LOADING -> LoadingTokenState()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoadedTokenState(config: TokenV2Config) {
|
||||
BaseSurface {
|
||||
ConstraintLayout(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
) {
|
||||
val (iconItem, tokenNameItem, fiatItem) = createRefs()
|
||||
TokenIcon(
|
||||
modifier = Modifier.constrainAs(iconItem) {
|
||||
centerVerticallyTo(parent)
|
||||
start.linkTo(parent.start)
|
||||
},
|
||||
tokenIconUrl = config.iconUrl,
|
||||
icon = config.icon,
|
||||
networkIconRes = config.networkIcon,
|
||||
)
|
||||
TokenTitleAmountBlock(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing12)
|
||||
.constrainAs(tokenNameItem) {
|
||||
centerVerticallyTo(parent)
|
||||
start.linkTo(iconItem.end)
|
||||
end.linkTo(fiatItem.start)
|
||||
width = Dimension.fillToConstraints
|
||||
},
|
||||
title = config.name,
|
||||
amount = config.amount,
|
||||
hasPending = config.hasPending,
|
||||
)
|
||||
TokenOptionsBlock(
|
||||
config = config,
|
||||
modifier = Modifier.constrainAs(fiatItem) {
|
||||
centerVerticallyTo(parent)
|
||||
end.linkTo(parent.end)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Block for end part of token item
|
||||
* shows status is reachable, is drag, hidden or show balance
|
||||
*/
|
||||
@Composable
|
||||
private fun TokenOptionsBlock(config: TokenV2Config, modifier: Modifier = Modifier) {
|
||||
when (config.tokenOptionsUIState) {
|
||||
TokenOptionsUIState.VISIBLE -> TokenFiatPercentageBlock(
|
||||
modifier = modifier,
|
||||
fiatAmount = config.fiatAmount,
|
||||
priceChange = config.priceChange,
|
||||
)
|
||||
TokenOptionsUIState.UNREACHABLE -> Text(
|
||||
modifier = modifier,
|
||||
text = "Unreachable", // TODO (conform this text)
|
||||
style = TangemTypography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
TokenOptionsUIState.HIDDEN -> TokenFiatPercentageBlock(
|
||||
modifier = modifier,
|
||||
fiatAmount = DOTS,
|
||||
priceChange = config.priceChange,
|
||||
)
|
||||
TokenOptionsUIState.DRAG -> Icon(
|
||||
modifier = modifier,
|
||||
painter = painterResource(id = R.drawable.ic_drag_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LoadingTokenState() {
|
||||
BaseSurface {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
vertical = TangemTheme.dimens.spacing4,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
CircleShimmer(modifier = Modifier.size(size = TangemTheme.dimens.size42))
|
||||
SpacerW12()
|
||||
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
|
||||
ShimmerRectangle(
|
||||
modifier = Modifier.size(
|
||||
width = TangemTheme.dimens.size72,
|
||||
height = TangemTheme.dimens.size12,
|
||||
),
|
||||
)
|
||||
ShimmerRectangle(
|
||||
modifier = Modifier.size(
|
||||
width = TangemTheme.dimens.size50,
|
||||
height = TangemTheme.dimens.size12,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
ShimmerRectangle(
|
||||
modifier = Modifier.size(
|
||||
width = TangemTheme.dimens.size40,
|
||||
height = TangemTheme.dimens.size12,
|
||||
),
|
||||
)
|
||||
ShimmerRectangle(
|
||||
modifier = Modifier.size(
|
||||
width = TangemTheme.dimens.size40,
|
||||
height = TangemTheme.dimens.size12,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BaseSurface(onClick: (() -> Unit)? = null, content: @Composable () -> Unit) {
|
||||
Surface(
|
||||
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size68),
|
||||
color = TangemTheme.colors.background.primary,
|
||||
onClick = onClick ?: {},
|
||||
enabled = onClick != null,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenTitleAmountBlock(title: String, amount: String, hasPending: Boolean, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
|
||||
Text(
|
||||
text = title,
|
||||
style = TangemTypography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
if (hasPending) {
|
||||
Image(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
painter = painterResource(id = R.drawable.img_loader_15),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = amount,
|
||||
style = TangemTypography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenFiatPercentageBlock(fiatAmount: String, priceChange: PriceChange, modifier: Modifier = Modifier) {
|
||||
Column(modifier = modifier.requiredWidth(IntrinsicSize.Max)) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
text = fiatAmount,
|
||||
style = TangemTypography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
SpacerH2()
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
val iconChangeArrow: Int
|
||||
val changeTextColor: Color
|
||||
when (priceChange.type) {
|
||||
PriceChangeType.UP -> {
|
||||
iconChangeArrow = R.drawable.img_arrow_up_8
|
||||
changeTextColor = TangemTheme.colors.text.accent
|
||||
}
|
||||
PriceChangeType.DOWN -> {
|
||||
iconChangeArrow = R.drawable.img_arrow_down_8
|
||||
changeTextColor = TangemTheme.colors.text.warning
|
||||
}
|
||||
}
|
||||
Image(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
painter = painterResource(id = iconChangeArrow),
|
||||
contentDescription = null,
|
||||
)
|
||||
SpacerW4()
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
text = priceChange.valuePercent,
|
||||
style = TangemTypography.body2,
|
||||
color = changeTextColor,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenIcon(
|
||||
tokenIconUrl: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
@DrawableRes icon: Int? = null,
|
||||
@DrawableRes networkIconRes: Int? = null,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(end = TangemTheme.dimens.spacing16)
|
||||
.size(TangemTheme.dimens.size42),
|
||||
) {
|
||||
val tokenImageModifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.size(TangemTheme.dimens.size36)
|
||||
|
||||
val data = if (tokenIconUrl.isNullOrEmpty()) {
|
||||
icon
|
||||
} else {
|
||||
tokenIconUrl
|
||||
}
|
||||
SubcomposeAsyncImage(
|
||||
modifier = tokenImageModifier,
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(data)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
loading = { CircleShimmer(modifier = tokenImageModifier) },
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
if (networkIconRes != null) {
|
||||
Box(
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region preview
|
||||
|
||||
@Composable
|
||||
private fun TokensPreview() {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing18),
|
||||
) {
|
||||
val config = TokenV2Config(
|
||||
name = "Polygon",
|
||||
amount = "5,412 MATIC",
|
||||
fiatAmount = "321 $",
|
||||
priceChange = PriceChange(
|
||||
valuePercent = "2%",
|
||||
type = PriceChangeType.UP,
|
||||
),
|
||||
iconUrl = null,
|
||||
icon = R.drawable.img_polygon_22,
|
||||
networkIcon = R.drawable.img_polygon_22,
|
||||
tokenUIState = TokenUIState.LOADED,
|
||||
tokenOptionsUIState = TokenOptionsUIState.VISIBLE,
|
||||
hasPending = true,
|
||||
)
|
||||
// Loaded item
|
||||
TokenItemV2(config)
|
||||
// Unreachable item
|
||||
TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.UNREACHABLE))
|
||||
// Drag item
|
||||
TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.DRAG))
|
||||
// Hidden item
|
||||
TokenItemV2(config.copy(tokenOptionsUIState = TokenOptionsUIState.UNREACHABLE))
|
||||
// Loading item
|
||||
TokenItemV2(
|
||||
config.copy(
|
||||
tokenUIState = TokenUIState.LOADING,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_Tokens_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
TokensPreview()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_Tokens_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
TokensPreview()
|
||||
}
|
||||
}
|
||||
|
||||
// endregion preview
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.ui
|
||||
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.feature.wallet.presentation.state.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.state.WalletStateHolder
|
||||
import java.util.UUID
|
||||
|
||||
internal object WalletPreviewData {
|
||||
|
||||
val walletCardContent = WalletCardState.Content(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
balance = "8923,05 $",
|
||||
additionalInfo = "3 cards • Seed enabled",
|
||||
imageResId = R.drawable.ill_businessman_3d,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
val walletCardLoading = WalletCardState.Loading(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
additionalInfo = "3 cards • Seed enabled",
|
||||
imageResId = R.drawable.ill_businessman_3d,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
val walletCardHiddenContent = WalletCardState.HiddenContent(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
additionalInfo = "3 cards • Seed enabled",
|
||||
imageResId = R.drawable.ill_businessman_3d,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
val walletCardError = WalletCardState.Error(
|
||||
id = UUID.randomUUID().toString(),
|
||||
title = "Wallet 1",
|
||||
additionalInfo = "3 cards • Seed enabled",
|
||||
imageResId = R.drawable.ill_businessman_3d,
|
||||
onClick = {},
|
||||
)
|
||||
|
||||
val walletScreenState = WalletStateHolder(
|
||||
onBackClick = {},
|
||||
headerConfig = WalletStateHolder.HeaderConfig(
|
||||
wallets = listOf(walletCardContent, walletCardLoading, walletCardHiddenContent, walletCardError),
|
||||
onScanCardClick = {},
|
||||
onMoreClick = {},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.feature.wallet.presentation.state
|
||||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.state
|
||||
package com.tangem.feature.wallet.presentation.wallet.state
|
||||
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
/**
|
||||
* Wallet state holder
|
||||
|
|
@ -21,7 +23,7 @@ internal data class WalletStateHolder(
|
|||
* @property onMoreClick lambda be invoked when more button is clicked
|
||||
*/
|
||||
data class HeaderConfig(
|
||||
val wallets: List<WalletCardState>,
|
||||
val wallets: ImmutableList<WalletCardState>,
|
||||
val onScanCardClick: () -> Unit,
|
||||
val onMoreClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package com.tangem.feature.wallet.presentation.ui
|
||||
package com.tangem.feature.wallet.presentation.wallet.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.tangem.feature.wallet.presentation.state.WalletStateHolder
|
||||
import com.tangem.feature.wallet.presentation.ui.components.WalletHeader
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
|
||||
import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletHeader
|
||||
|
||||
/**
|
||||
* Wallet screen
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.feature.wallet.presentation.ui.components
|
||||
package com.tangem.feature.wallet.presentation.wallet.ui.components
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
|
|
@ -17,12 +17,12 @@ import androidx.compose.ui.unit.sp
|
|||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import com.tangem.core.ui.components.FontSizeRange
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.ResizableText
|
||||
import com.tangem.core.ui.components.ShimmerRectangle
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.state.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.ui.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState
|
||||
|
||||
private const val DOTS = "•••"
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ private fun Balance(state: WalletCardState) {
|
|||
)
|
||||
}
|
||||
is WalletCardState.Loading -> {
|
||||
ShimmerRectangle(
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(
|
||||
width = TangemTheme.dimens.size102,
|
||||
height = TangemTheme.dimens.size24,
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.feature.wallet.presentation.ui.components
|
||||
package com.tangem.feature.wallet.presentation.wallet.ui.components
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
|
|
@ -21,9 +21,10 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.state.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.state.WalletStateHolder
|
||||
import com.tangem.feature.wallet.presentation.ui.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
/**
|
||||
* Wallet screen header
|
||||
|
|
@ -109,7 +110,7 @@ private fun Preview_WalletHeader_DarkTheme(
|
|||
private class WalletHeaderProvider : CollectionPreviewParameterProvider<WalletStateHolder.HeaderConfig>(
|
||||
collection = listOf(
|
||||
WalletStateHolder.HeaderConfig(
|
||||
wallets = listOf(
|
||||
wallets = persistentListOf(
|
||||
WalletPreviewData.walletCardContent,
|
||||
WalletPreviewData.walletCardLoading,
|
||||
WalletPreviewData.walletCardHiddenContent,
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
package com.tangem.feature.wallet.presentation.viewmodels
|
||||
package com.tangem.feature.wallet.presentation.wallet.viewmodels
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.feature.wallet.presentation.state.WalletStateHolder
|
||||
import com.tangem.feature.wallet.presentation.ui.WalletPreviewData
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
Loading…
Add table
Add a link
Reference in a new issue