Updated on 2026-08-14

This commit is contained in:
Tangem 2024-02-13 18:00:34 +03:00
parent ed8419b45b
commit b97ddcb3f2
17 changed files with 379 additions and 147 deletions

View file

@ -39,47 +39,57 @@ fun HorizontalActionChips(
}
}
@Preview
// region Preview
@Preview(widthDp = 360)
@Composable
private fun Preview_HorizontalActionChips_Light(
@PreviewParameter(ActionButtonConfigProvider::class) buttons: ImmutableList<ActionButtonConfig>,
@PreviewParameter(ActionButtonConfigProvider::class) buttons: HorizontalActionChips,
) {
TangemTheme(isDark = false) {
HorizontalActionChips(buttons = buttons)
HorizontalActionChips(buttons = buttons.buttons)
}
}
@Preview
@Preview(widthDp = 360)
@Composable
private fun Preview_HorizontalActionChips_Dark(
@PreviewParameter(ActionButtonConfigProvider::class) buttons: ImmutableList<ActionButtonConfig>,
@PreviewParameter(ActionButtonConfigProvider::class) buttons: HorizontalActionChips,
) {
TangemTheme(isDark = true) {
HorizontalActionChips(buttons = buttons)
HorizontalActionChips(buttons = buttons.buttons)
}
}
private class ActionButtonConfigProvider : CollectionPreviewParameterProvider<ActionButtonConfig>(
collection = persistentListOf(
ActionButtonConfig(
text = TextReference.Str(value = "Buy"),
iconResId = R.drawable.ic_plus_24,
onClick = {},
),
ActionButtonConfig(
text = TextReference.Str(value = "Send"),
iconResId = R.drawable.ic_arrow_up_24,
onClick = {},
),
ActionButtonConfig(
text = TextReference.Str(value = "Receive"),
iconResId = R.drawable.ic_arrow_down_24,
onClick = {},
),
ActionButtonConfig(
text = TextReference.Str(value = "Exchange"),
iconResId = R.drawable.ic_exchange_vertical_24,
onClick = {},
private class ActionButtonConfigProvider : CollectionPreviewParameterProvider<HorizontalActionChips>(
collection = listOf(
HorizontalActionChips(
buttons = persistentListOf(
ActionButtonConfig(
text = TextReference.Str(value = "Buy"),
iconResId = R.drawable.ic_plus_24,
onClick = {},
),
ActionButtonConfig(
text = TextReference.Str(value = "Send"),
iconResId = R.drawable.ic_arrow_up_24,
onClick = {},
),
ActionButtonConfig(
text = TextReference.Str(value = "Receive"),
iconResId = R.drawable.ic_arrow_down_24,
onClick = {},
),
ActionButtonConfig(
text = TextReference.Str(value = "Exchange"),
iconResId = R.drawable.ic_exchange_vertical_24,
onClick = {},
),
),
),
),
)
)
private data class HorizontalActionChips(
val buttons: ImmutableList<ActionButtonConfig>,
)
// endregion Preview

View file

@ -11,7 +11,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
@ -87,7 +86,6 @@ private fun Button(
Row(
modifier = modifier
.heightIn(min = TangemTheme.dimens.size36)
.clip(shape)
.background(color = backgroundColor, shape = shape)
.clickable(enabled = config.enabled, onClick = config.onClick)
.padding(start = TangemTheme.dimens.spacing16, end = TangemTheme.dimens.spacing24)

View file

@ -3,7 +3,6 @@ package com.tangem.core.ui.components.marketprice
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.*
@ -35,39 +34,60 @@ import com.tangem.core.ui.utils.BigDecimalFormatter
*/
@Composable
fun MarketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier) {
var rootWidth by remember { mutableIntStateOf(value = 0) }
Column(
Row(
modifier = modifier
.background(
color = TangemTheme.colors.background.primary,
shape = RoundedCornerShape(TangemTheme.dimens.radius14),
shape = TangemTheme.shapes.roundedCornersXMedium,
)
.heightIn(min = TangemTheme.dimens.size70)
.fillMaxWidth()
.padding(all = TangemTheme.dimens.spacing14)
.onSizeChanged { rootWidth = it.width },
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing6),
horizontalAlignment = Alignment.Start,
.heightIn(min = TangemTheme.dimens.size72)
.padding(all = TangemTheme.dimens.spacing12),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Title(currencyName = state.currencySymbol)
var rootWidth by remember { mutableIntStateOf(value = 0) }
Content(state = state, rootWidth = rootWidth)
Column(
modifier = Modifier.onSizeChanged { rootWidth = it.width },
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
horizontalAlignment = Alignment.Start,
) {
Title(currencyName = state.currencySymbol)
Content(state = state, rootWidth = rootWidth)
}
Icon(
modifier = Modifier.size(TangemTheme.dimens.size20),
painter = painterResource(id = R.drawable.ic_chevron_right_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
)
}
}
@Composable
private fun Title(currencyName: String) {
Text(
text = stringResource(id = R.string.wallet_marketplace_block_title, currencyName),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.subtitle2,
)
private fun Title(currencyName: String, modifier: Modifier = Modifier) {
Box(
modifier = modifier.heightIn(min = TangemTheme.dimens.size24),
contentAlignment = Alignment.CenterStart,
) {
Text(
text = stringResource(id = R.string.wallet_marketplace_block_title, currencyName),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.subtitle2,
)
}
}
@Composable
private fun Content(state: MarketPriceBlockState, rootWidth: Int) {
AnimatedContent(targetState = state, label = "Update the content") { marketPriceBlockState ->
private fun Content(state: MarketPriceBlockState, rootWidth: Int, modifier: Modifier = Modifier) {
AnimatedContent(
modifier = modifier.heightIn(min = TangemTheme.dimens.size20),
targetState = state,
contentAlignment = Alignment.CenterStart,
label = "Update the content",
) { marketPriceBlockState ->
when (marketPriceBlockState) {
is MarketPriceBlockState.Content,
is MarketPriceBlockState.Error,
@ -97,6 +117,7 @@ private fun PriceContent(state: MarketPriceBlockState, priceWidthDp: Dp) {
@Composable
private fun PriceBlock(state: MarketPriceBlockState, priceWidthDp: Dp) {
val priceModifier = Modifier.widthIn(max = priceWidthDp)
AnimatedContent(targetState = state, label = "Update the price block") { marketPriceBlockState ->
if (marketPriceBlockState is MarketPriceBlockState.Content) {
Row(
@ -127,12 +148,17 @@ private fun Price(price: String, modifier: Modifier = Modifier) {
@Composable
private fun PriceChangeInPercent(config: PriceChangeState.Content) {
AnimatedContent(targetState = config.type, label = "Update price change") { type ->
AnimatedContent(
targetState = config.type,
contentAlignment = Alignment.CenterStart,
label = "Update price change",
) { type ->
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4),
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing2),
) {
Icon(
modifier = Modifier.size(TangemTheme.dimens.size8),
painter = painterResource(
id = when (type) {
PriceChangeType.UP -> R.drawable.ic_arrow_up_8
@ -184,7 +210,8 @@ private fun QuoteTimeStatus() {
)
}
@Preview
// region Preview
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun Preview_MarketPriceBlock_Light(
@PreviewParameter(WalletMarketPriceBlockStateProvider::class)
@ -195,7 +222,7 @@ private fun Preview_MarketPriceBlock_Light(
}
}
@Preview
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun Preview_MarketPriceBlock_Dark(
@PreviewParameter(WalletMarketPriceBlockStateProvider::class)
@ -227,4 +254,5 @@ private class WalletMarketPriceBlockStateProvider : CollectionPreviewParameterPr
MarketPriceBlockState.Loading(currencySymbol = "BTC"),
MarketPriceBlockState.Error(currencySymbol = "BTC"),
),
)
)
// endregion Preview

View file

@ -53,12 +53,16 @@ fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Mod
Surface(
modifier = modifier
.background(TangemTheme.colors.background.primary)
.defaultMinSize(minHeight = TangemTheme.dimens.size56)
.clickable(
enabled = state is TransactionState.Content,
onClick = (state as? TransactionState.Content)?.onClick ?: {},
)
.padding(horizontal = TangemTheme.dimens.spacing12, vertical = TangemTheme.dimens.spacing10),
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size56)
.padding(
vertical = TangemTheme.dimens.spacing8,
horizontal = TangemTheme.dimens.spacing12,
),
color = TangemTheme.colors.background.primary,
) {
@Suppress("DestructuringDeclarationWithTooManyEntries")
@ -81,12 +85,13 @@ fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Mod
modifier = Modifier
.padding(
start = TangemTheme.dimens.spacing12,
end = TangemTheme.dimens.spacing6,
end = TangemTheme.dimens.spacing4,
)
.constrainAs(titleItem) {
top.linkTo(parent.top)
bottom.linkTo(subtitleItem.top)
start.linkTo(iconItem.end)
end.linkTo(amountItem.start)
top.linkTo(iconItem.top)
width = Dimension.fillToConstraints
},
)
@ -94,11 +99,14 @@ fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Mod
Subtitle(
state = state,
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing6)
.padding(horizontal = TangemTheme.dimens.spacing12)
.padding(
start = TangemTheme.dimens.spacing12,
end = TangemTheme.dimens.spacing4,
)
.constrainAs(subtitleItem) {
top.linkTo(titleItem.bottom)
bottom.linkTo(parent.bottom)
start.linkTo(iconItem.end)
top.linkTo(amountItem.bottom)
end.linkTo(timestampItem.start)
width = Dimension.fillToConstraints
},
@ -108,8 +116,9 @@ fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Mod
state = state,
isBalanceHidden = isBalanceHidden,
modifier = Modifier.constrainAs(amountItem) {
top.linkTo(parent.top)
bottom.linkTo(timestampItem.top)
start.linkTo(titleItem.end)
top.linkTo(titleItem.top)
end.linkTo(parent.end)
width = Dimension.fillToConstraints
},
@ -117,12 +126,11 @@ fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Mod
Timestamp(
state = state,
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing6)
.constrainAs(timestampItem) {
top.linkTo(amountItem.bottom)
end.linkTo(parent.end)
},
modifier = Modifier.constrainAs(timestampItem) {
top.linkTo(amountItem.bottom)
bottom.linkTo(parent.bottom)
end.linkTo(parent.end)
},
)
}
}

View file

@ -1,12 +1,14 @@
package com.tangem.core.ui.components.transactions
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState
import com.tangem.core.ui.res.TangemTheme
@ -20,19 +22,23 @@ import java.util.UUID
*/
@Composable
internal fun TxHistoryGroupTitle(config: TxHistoryItemState.GroupTitle, modifier: Modifier = Modifier) {
Text(
text = config.title,
Box(
modifier = modifier
.background(TangemTheme.colors.background.primary)
.fillMaxWidth()
.padding(
horizontal = TangemTheme.dimens.spacing16,
vertical = TangemTheme.dimens.spacing14,
),
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.Start,
style = TangemTheme.typography.body2,
)
vertical = TangemTheme.dimens.spacing8,
horizontal = TangemTheme.dimens.spacing12,
)
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size24),
contentAlignment = Alignment.CenterStart,
) {
Text(
text = config.title,
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.body2,
)
}
}
@Preview

View file

@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@ -24,9 +25,11 @@ internal fun TxHistoryTitle(onExploreClick: () -> Unit, modifier: Modifier = Mod
Row(
modifier = modifier
.background(TangemTheme.colors.background.primary)
.fillMaxWidth()
.padding(top = TangemTheme.dimens.spacing12)
.padding(horizontal = TangemTheme.dimens.spacing16),
.padding(horizontal = TangemTheme.dimens.spacing12)
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size24),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
@ -37,13 +40,14 @@ internal fun TxHistoryTitle(onExploreClick: () -> Unit, modifier: Modifier = Mod
Row(
modifier = Modifier.clickable(onClick = onExploreClick),
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing2),
) {
Icon(
modifier = Modifier.size(size = TangemTheme.dimens.size20),
painter = painterResource(id = R.drawable.ic_compass_24),
contentDescription = null,
modifier = Modifier.size(size = TangemTheme.dimens.size18),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
)
Text(
text = stringResource(id = R.string.common_explorer),

View file

@ -1,8 +1,8 @@
package com.tangem.core.ui.components.transactions.empty
import androidx.compose.foundation.Image
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.ui.Alignment
@ -33,9 +33,10 @@ fun EmptyTransactionBlock(state: EmptyTransactionsBlockState, modifier: Modifier
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
Icon(
modifier = Modifier.size(TangemTheme.dimens.size64),
painter = painterResource(id = state.iconRes),
tint = TangemTheme.colors.icon.inactive,
contentDescription = null,
)
@ -44,7 +45,7 @@ fun EmptyTransactionBlock(state: EmptyTransactionsBlockState, modifier: Modifier
textAlign = TextAlign.Center,
text = state.text.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
color = TangemTheme.colors.text.tertiary,
)
Buttons(
@ -84,8 +85,8 @@ private fun PairButtons(state: EmptyTransactionsBlockState.ButtonsState.PairButt
}
}
@Preview
@Composable
@Preview(widthDp = 360, showBackground = true)
private fun EmptyTransactionBlock_Light(
@PreviewParameter(EmptyTransactionBlockStateProvider::class) state: EmptyTransactionsBlockState,
) {
@ -94,8 +95,8 @@ private fun EmptyTransactionBlock_Light(
}
}
@Preview
@Composable
@Preview(widthDp = 360, showBackground = true)
private fun EmptyTransactionBlock_Dark(
@PreviewParameter(EmptyTransactionBlockStateProvider::class) state: EmptyTransactionsBlockState,
) {

View file

@ -19,7 +19,7 @@ fun Modifier.roundedShapeItemDecoration(
modifier
.then(
if (addDefaultPadding) {
Modifier.padding(top = TangemTheme.dimens.spacing14)
Modifier.padding(top = TangemTheme.dimens.spacing12)
} else {
Modifier
},
@ -30,7 +30,7 @@ fun Modifier.roundedShapeItemDecoration(
modifier
.then(
if (addDefaultPadding) {
Modifier.padding(top = TangemTheme.dimens.spacing14)
Modifier.padding(top = TangemTheme.dimens.spacing12)
} else {
Modifier
},

View file

@ -68,6 +68,7 @@ data class TangemDimens internal constructor(
val size50: Dp = 50.dp,
val size52: Dp = 52.dp,
val size56: Dp = 56.dp,
val size60: Dp = 60.dp,
val size62: Dp = 62.dp,
val size64: Dp = 64.dp,
val size68: Dp = 68.dp,

View file

@ -3,22 +3,8 @@
android:height="64dp"
android:viewportWidth="64"
android:viewportHeight="64">
<group>
<clip-path android:pathData="M0,0h64v64h-64z" />
<path
android:pathData="M37.84,55.346C50.625,52.278 58.482,39.342 55.389,26.453C52.296,13.564 39.424,5.603 26.639,8.671C20.067,10.248 14.797,14.433 11.647,19.879M11.647,19.879L22.5,19.879M11.647,19.879L11.647,8.001"
android:strokeWidth="2.5"
android:fillColor="#00000000"
android:strokeColor="#C9C9C9"
android:strokeLineCap="round" />
<path
android:pathData="M9.945,22.612C10.593,22.85 10.926,23.568 10.688,24.216L10.651,24.317C10.416,24.966 9.699,25.302 9.05,25.067C8.401,24.832 8.065,24.115 8.3,23.466L8.341,23.354C8.579,22.706 9.297,22.374 9.945,22.612ZM8.025,31.53C8.715,31.509 9.291,32.051 9.312,32.742L9.314,32.795L9.315,32.84C9.339,33.53 8.799,34.108 8.109,34.132C7.419,34.156 6.84,33.616 6.816,32.926L6.815,32.877L6.813,32.817C6.792,32.127 7.335,31.551 8.025,31.53ZM9.036,38.993C9.686,38.759 10.401,39.097 10.634,39.747L10.665,39.83C10.9,40.479 10.565,41.196 9.917,41.432C9.268,41.667 8.551,41.333 8.315,40.684L8.282,40.591C8.048,39.942 8.386,39.226 9.036,38.993ZM12.309,45.763C12.854,45.34 13.639,45.439 14.062,45.984L14.116,46.054C14.542,46.598 14.446,47.383 13.902,47.808C13.358,48.234 12.572,48.138 12.147,47.594L12.087,47.516C11.664,46.971 11.763,46.186 12.309,45.763ZM17.512,51.191C17.901,50.621 18.679,50.474 19.249,50.863L19.322,50.912C19.894,51.299 20.044,52.076 19.658,52.648C19.271,53.22 18.494,53.37 17.922,52.983L17.841,52.928C17.271,52.539 17.124,51.762 17.512,51.191ZM24.135,54.753C24.328,54.09 25.022,53.709 25.685,53.902L25.769,53.927C26.433,54.117 26.817,54.81 26.626,55.473C26.436,56.137 25.743,56.52 25.08,56.33L24.986,56.303C24.323,56.11 23.942,55.416 24.135,54.753ZM31.537,56.082C31.516,55.392 32.058,54.815 32.748,54.794L32.792,54.792L32.837,54.791C33.527,54.767 34.105,55.307 34.129,55.997C34.153,56.687 33.613,57.266 32.923,57.29L32.874,57.291L32.825,57.293C32.134,57.314 31.558,56.772 31.537,56.082Z"
android:pathData="M11.7,6.8C12.3,6.8 12.9,7.3 12.9,8V15.9C16.2,11.8 20.9,8.8 26.4,7.5C39.8,4.2 53.4,12.6 56.6,26.2C59.9,39.7 51.6,53.3 38.1,56.6C37.5,56.7 36.8,56.3 36.6,55.6C36.5,55 36.9,54.3 37.6,54.1C49.7,51.2 57.1,39 54.2,26.8C51.2,14.5 39,7 26.9,9.9C21.5,11.2 17,14.4 13.9,18.6L22.5,18.6C23.2,18.6 23.8,19.2 23.8,19.9C23.8,20.6 23.2,21.1 22.5,21.1H10.4V8C10.4,7.3 11,6.8 11.7,6.8ZM21.6,38.1C21,37.2 21,35.7 21.8,33.7C22.7,31.5 24.5,29.1 27,27C29.6,24.8 32.3,23.5 34.6,23C36.9,22.5 38.5,23 39.2,23.8C40,24.7 40.1,26.3 39.2,28.6C38.3,30.7 36.6,33.2 34,35.3C31.5,37.5 28.8,38.8 26.5,39.3C24.5,39.7 23.1,39.5 22.2,38.8L21.6,38.1L21.6,38.1ZM20.5,40.6C20.3,40.5 20.1,40.3 19.9,40.1C18.3,38.1 18.4,35.3 19.5,32.8C20.5,30.2 22.6,27.4 25.4,25.1C28.2,22.7 31.3,21.2 34,20.6C36.7,20 39.5,20.3 41.1,22.2L41.2,22.3L41.2,22.2L43.7,25.2C45.5,27.3 45.5,30.2 44.5,32.9C43.5,35.6 41.5,38.5 38.7,40.8C35.9,43.2 32.7,44.7 29.9,45.2C27,45.6 24.2,45.1 22.5,43.1L20.5,40.6ZM25,42C26,42.7 27.5,43 29.5,42.7C31.8,42.3 34.6,41 37.1,38.9C37.4,38.7 37.7,38.4 37.9,38.2L36.3,36.6C36.1,36.8 35.9,37 35.6,37.2C32.8,39.6 29.7,41.1 27,41.7C26.3,41.9 25.6,42 25,42ZM38.1,34.8L39.6,36.4C40.2,35.7 40.7,35 41.1,34.3L39.6,32.9C39.2,33.6 38.7,34.2 38.1,34.8ZM42.2,27.4C42.8,28.5 42.8,30 42.2,31.9L41,30.7C41.2,30.3 41.4,29.9 41.5,29.5C41.8,28.8 42,28.1 42.2,27.4ZM10.7,24.2C10.9,23.6 10.6,22.9 10,22.6C9.3,22.4 8.6,22.7 8.3,23.4L8.3,23.5C8.1,24.1 8.4,24.8 9.1,25.1C9.7,25.3 10.4,25 10.7,24.3L10.7,24.2ZM9.3,32.7C9.3,32.1 8.7,31.5 8,31.5C7.3,31.6 6.8,32.1 6.8,32.8L6.8,32.9L6.8,32.9C6.8,33.6 7.4,34.2 8.1,34.1C8.8,34.1 9.3,33.5 9.3,32.8L9.3,32.8L9.3,32.7ZM10.6,39.8C10.4,39.1 9.7,38.8 9,39C8.4,39.2 8.1,39.9 8.3,40.6L8.3,40.7C8.6,41.3 9.3,41.7 9.9,41.4C10.6,41.2 10.9,40.5 10.7,39.8L10.6,39.8ZM14.1,46C13.6,45.4 12.9,45.3 12.3,45.8C11.8,46.2 11.7,47 12.1,47.5L12.2,47.6C12.6,48.1 13.4,48.2 13.9,47.8C14.5,47.4 14.5,46.6 14.1,46.1L14.1,46ZM19.3,50.9C18.7,50.5 17.9,50.6 17.5,51.2C17.1,51.8 17.3,52.5 17.8,52.9L17.9,53C18.5,53.4 19.3,53.2 19.7,52.7C20,52.1 19.9,51.3 19.3,50.9L19.3,50.9ZM25.7,53.9C25,53.7 24.3,54.1 24.1,54.8C23.9,55.4 24.3,56.1 25,56.3L25.1,56.3C25.7,56.5 26.4,56.1 26.6,55.5C26.8,54.8 26.4,54.1 25.8,53.9L25.7,53.9ZM32.8,54.8C32.1,54.8 31.5,55.4 31.5,56.1C31.6,56.8 32.1,57.3 32.8,57.3L32.9,57.3L32.9,57.3C33.6,57.3 34.2,56.7 34.1,56C34.1,55.3 33.5,54.8 32.8,54.8L32.8,54.8L32.8,54.8Z"
android:fillColor="#C9C9C9"
android:fillType="evenOdd" />
<path
android:pathData="M23.528,30.572L32.5,19.036L41.472,30.572L32.5,44.672L23.528,30.572Z"
android:strokeWidth="2.5"
android:fillColor="#00000000"
android:strokeColor="#C9C9C9" />
</group>
</vector>

View file

@ -4,7 +4,7 @@ import java.math.BigDecimal
import java.math.RoundingMode
import java.text.DecimalFormat
import java.text.NumberFormat
import java.util.*
import java.util.Locale
// todo determine where to place this extensions
fun BigDecimal.toFormattedString(
@ -38,7 +38,7 @@ fun BigDecimal.toFormattedCurrencyString(
decimals = decimalsForRounding,
roundingMode = roundingMode,
)
val formattedCurrency = currency?.let { " $it " } ?: ""
val formattedCurrency = currency?.let { " $it" } ?: ""
return "$formattedAmount$formattedCurrency"
}

View file

@ -1,10 +1,15 @@
package com.tangem.feature.tokendetails.presentation.tokendetails
import androidx.compose.ui.graphics.Color
import androidx.paging.PagingData
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
@ -76,8 +81,8 @@ internal object TokenDetailsPreviewData {
val balanceLoading = TokenDetailsBalanceBlockState.Loading(actionButtons = actionButtons)
val balanceContent = TokenDetailsBalanceBlockState.Content(
actionButtons = actionButtons,
fiatBalance = "123,00$",
cryptoBalance = "866,96 USDT",
fiatBalance = "91,50$",
cryptoBalance = "966,96 XLM",
)
val balanceError = TokenDetailsBalanceBlockState.Error(actionButtons = actionButtons)
@ -88,7 +93,131 @@ internal object TokenDetailsPreviewData {
onRefresh = {},
)
val tokenDetailsState = TokenDetailsState(
private val txHistoryItems = listOf(
TxHistoryState.TxHistoryItemState.Title(onExploreClick = {}),
TxHistoryState.TxHistoryItemState.GroupTitle(
title = "Today",
itemKey = "Today",
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "1",
amount = "-0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.OUTGOING,
onClick = {},
iconRes = R.drawable.ic_arrow_up_24,
title = stringReference(value = "Sending"),
subtitle = stringReference(value = "to: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "2",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_arrow_down_24,
title = stringReference(value = "Receiving"),
subtitle = stringReference(value = "from: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "3",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_doc_24,
title = stringReference(value = "Approving"),
subtitle = stringReference(value = "from: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "4",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_exchange_vertical_24,
title = stringReference(value = "Swapping"),
subtitle = stringReference(value = "contract: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.GroupTitle(
title = "Yesterday",
itemKey = "Yesterday",
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "5",
amount = "-0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.OUTGOING,
onClick = {},
iconRes = R.drawable.ic_arrow_up_24,
title = stringReference(value = "Sending"),
subtitle = stringReference(value = "to: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "6",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_arrow_down_24,
title = stringReference(value = "Receiving"),
subtitle = stringReference(value = "from: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "7",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_doc_24,
title = stringReference(value = "Approving"),
subtitle = stringReference(value = "from: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "8",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_exchange_vertical_24,
title = stringReference(value = "Swapping"),
subtitle = stringReference(value = "contract: 33BddS...ga2B"),
timestamp = 0,
),
),
)
val tokenDetailsState_1 = TokenDetailsState(
topAppBarConfig = tokenDetailsTopAppBarConfig,
tokenInfoBlockState = tokenInfoBlockState,
tokenBalanceBlockState = balanceLoading,
@ -108,4 +237,41 @@ internal object TokenDetailsPreviewData {
isMarketPriceAvailable = false,
event = consumedEvent(),
)
val tokenDetailsState_2 = TokenDetailsState(
topAppBarConfig = tokenDetailsTopAppBarConfig,
tokenInfoBlockState = tokenInfoBlockState.copy(
name = "Stellar",
),
tokenBalanceBlockState = balanceContent,
marketPriceBlockState = MarketPriceBlockState.Content(
currencySymbol = "XLM",
price = "0.11$",
priceChangeConfig = PriceChangeState.Content(
valueInPercent = "5,16%",
type = PriceChangeType.UP,
),
),
notifications = persistentListOf(),
txHistoryState = TxHistoryState.NotSupported(
onExploreClick = {},
pendingTransactions = persistentListOf(),
),
dialogConfig = null,
pendingTxs = persistentListOf(),
swapTxs = persistentListOf(),
pullToRefreshConfig = pullToRefreshConfig,
bottomSheetConfig = null,
isBalanceHidden = false,
isMarketPriceAvailable = true,
event = consumedEvent(),
)
val tokenDetailsState_3 = tokenDetailsState_2.copy(
txHistoryState = TxHistoryState.Content(
contentItems = MutableStateFlow(
value = PagingData.from(txHistoryItems),
),
),
)
}

View file

@ -3,6 +3,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
@ -20,6 +21,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.paging.compose.collectAsLazyPagingItems
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig
@ -82,14 +85,12 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) {
.pullRefresh(pullRefreshState),
) {
LazyColumn(
modifier = Modifier
.fillMaxSize(),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(bottom = TangemTheme.dimens.spacing16),
) {
item {
TokenInfoBlock(
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing4)
.padding(horizontal = horizontalPadding),
modifier = Modifier.padding(horizontal = horizontalPadding),
state = state.tokenInfoBlockState,
)
}
@ -181,18 +182,32 @@ internal fun TokenDetailsEventEffect(snackbarHostState: SnackbarHostState, event
)
}
@Preview
// region Preview
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun Preview_TokenDetailsScreen_LightTheme() {
TangemTheme(isDark = false) {
TokenDetailsScreen(state = TokenDetailsPreviewData.tokenDetailsState)
private fun TokenDetailsScreenPreview_Light(
@PreviewParameter(TokenDetailsScreenParameterProvider::class) state: TokenDetailsState,
) {
TangemTheme {
TokenDetailsScreen(state)
}
}
@Preview
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun Preview_TokenDetailsScreen_DarkTheme() {
private fun TokenDetailsScreenPreview_Dark(
@PreviewParameter(TokenDetailsScreenParameterProvider::class) state: TokenDetailsState,
) {
TangemTheme(isDark = true) {
TokenDetailsScreen(state = TokenDetailsPreviewData.tokenDetailsState)
TokenDetailsScreen(state)
}
}
}
private class TokenDetailsScreenParameterProvider : CollectionPreviewParameterProvider<TokenDetailsState>(
collection = listOf(
TokenDetailsPreviewData.tokenDetailsState_1,
TokenDetailsPreviewData.tokenDetailsState_2,
TokenDetailsPreviewData.tokenDetailsState_3,
),
)
// endregion Preview

View file

@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.*
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.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
@ -32,18 +33,21 @@ internal fun TokenDetailsBalanceBlock(
color = TangemTheme.colors.background.primary,
) {
Column {
Text(
Box(
modifier = Modifier
.padding(
top = TangemTheme.dimens.spacing12,
start = TangemTheme.dimens.spacing12,
end = TangemTheme.dimens.spacing12,
),
text = stringResource(id = R.string.common_balance_title),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.subtitle2,
maxLines = 1,
)
.padding(top = TangemTheme.dimens.spacing12)
.padding(horizontal = TangemTheme.dimens.spacing12)
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.spacing24),
contentAlignment = Alignment.CenterStart,
) {
Text(
text = stringResource(id = R.string.common_balance_title),
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.subtitle2,
maxLines = 1,
)
}
FiatBalance(
state = state,
isBalanceHidden = isBalanceHidden,
@ -124,7 +128,7 @@ private fun CryptoBalance(
}
}
@Preview
@Preview(widthDp = 328, heightDp = 152)
@Composable
private fun Preview_TokenDetailsBalanceBlock_LightTheme(
@PreviewParameter(TokenDetailsBalanceBlockStateProvider::class) state: TokenDetailsBalanceBlockState,
@ -134,7 +138,7 @@ private fun Preview_TokenDetailsBalanceBlock_LightTheme(
}
}
@Preview
@Preview(widthDp = 328, heightDp = 152)
@Composable
private fun Preview_TokenDetailsBalanceBlock_DarkTheme(
@PreviewParameter(TokenDetailsBalanceBlockStateProvider::class) state: TokenDetailsBalanceBlockState,

View file

@ -14,7 +14,6 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
@ -29,9 +28,15 @@ private const val NORMAL_ALPHA = 1f
@Composable
internal fun TokenInfoBlock(state: TokenInfoBlockState, modifier: Modifier = Modifier) {
Row(modifier = modifier.fillMaxWidth()) {
Row(
modifier = modifier
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size60),
) {
Column(
modifier = Modifier.weight(1F),
modifier = Modifier
.align(Alignment.CenterVertically)
.weight(1F),
) {
Text(
text = state.name,
@ -88,7 +93,7 @@ private fun NetworkInfoText(currency: TokenInfoBlockState.Currency) {
)
Text(
text = state.boldText,
style = TangemTheme.typography.caption2.copy(fontWeight = FontWeight.Medium),
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.primary1,
)
}

View file

@ -126,7 +126,7 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier
Layout(content = content, modifier = modifier) { measurables, constraints ->
val layoutWidth = constraints.maxWidth
val horizontalPadding = with(density) { dimens.size14.roundToPx() }
val horizontalPadding = with(density) { dimens.size12.roundToPx() }
val verticalPadding = with(density) { dimens.size16.roundToPx() }
val layoutWidthWithoutPaddings = layoutWidth - 2 * horizontalPadding
@ -137,7 +137,7 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier
/*
* Title width take the whole REMAINING space.
* If FiatAmount took the whole free space, then Title will has min width.
* If FiatAmount took the whole free space, then Title will have min width.
*/
val title: Placeable
@ -149,7 +149,7 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier
/*
* PriceChange width take the whole REMAINING space.
* If CryptoAmount took the whole free space, then PriceChange will has min width.
* If CryptoAmount took the whole free space, then PriceChange will have min width.
*/
val priceChange: Placeable?

View file

@ -103,7 +103,7 @@ private fun TokenList(
)
val listContentPadding = PaddingValues(
top = TangemTheme.dimens.spacing12,
top = TangemTheme.dimens.spacing4,
bottom = TangemTheme.dimens.spacing92,
start = TangemTheme.dimens.spacing16,
end = TangemTheme.dimens.spacing16,
@ -245,7 +245,7 @@ private fun TopBar(
Row(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = TangemTheme.dimens.size56),
.padding(bottom = TangemTheme.dimens.spacing8),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
) {