Updated on 2026-08-14

This commit is contained in:
Tangem 2023-11-16 11:33:30 +03:00
parent adf9e09ac1
commit 2f6c106bd7
9 changed files with 329 additions and 33 deletions

View file

@ -11,7 +11,6 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SpacerH28
import com.tangem.core.ui.components.rows.states.ActionRowState
import com.tangem.core.ui.res.TangemTheme
/**
@ -20,7 +19,7 @@ import com.tangem.core.ui.res.TangemTheme
* https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-807&mode=design&t=Ygv5sohTTHYAQcBS-4
*/
@Composable
fun SimpleActionRow(state: ActionRowState, modifier: Modifier = Modifier) {
fun SimpleActionRow(title: String, description: String, modifier: Modifier = Modifier) {
Box(
modifier = modifier
.background(color = TangemTheme.colors.background.action)
@ -34,12 +33,12 @@ fun SimpleActionRow(state: ActionRowState, modifier: Modifier = Modifier) {
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
) {
Text(
text = state.title,
text = title,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
)
Text(
text = state.description,
text = description,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
)
@ -59,20 +58,21 @@ fun SimpleActionRow(state: ActionRowState, modifier: Modifier = Modifier) {
@Preview
@Composable
private fun SimpleActionRowPreview() {
val state = ActionRowState(
title = "Title",
description = "Description",
onClick = {},
)
Column {
TangemTheme(isDark = false) {
SimpleActionRow(state)
SimpleActionRow(
title = "Title",
description = "Description",
)
}
SpacerH28()
TangemTheme(isDark = false) {
SimpleActionRow(state)
SimpleActionRow(
title = "Title",
description = "Description",
)
}
}
}

View file

@ -0,0 +1,160 @@
package com.tangem.core.ui.components.rows
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
@Composable
fun SelectorRowItem(
@StringRes titleRes: Int,
@DrawableRes iconRes: Int,
onSelect: () -> Unit,
modifier: Modifier = Modifier,
preEllipsize: TextReference? = null,
postEllipsize: TextReference? = null,
isSelected: Boolean = false,
showDivider: Boolean = true,
) {
val iconTint by animateColorAsState(
targetValue = if (isSelected) {
TangemTheme.colors.icon.accent
} else {
TangemTheme.colors.icon.informative
},
label = "Selector icon tint change",
)
val textStyle = if (isSelected) {
TangemTheme.typography.subtitle2
} else {
TangemTheme.typography.body2
}
Box(
modifier = modifier
.fillMaxWidth()
.clickable { onSelect() },
) {
Row(modifier = Modifier.fillMaxWidth()) {
Icon(
painter = painterResource(iconRes),
tint = iconTint,
contentDescription = null,
modifier = Modifier
.padding(
start = TangemTheme.dimens.spacing12,
top = TangemTheme.dimens.spacing12,
bottom = TangemTheme.dimens.spacing12,
),
)
Text(
text = stringResource(titleRes),
style = textStyle,
color = TangemTheme.colors.text.primary1,
modifier = Modifier
.padding(
start = TangemTheme.dimens.spacing8,
top = TangemTheme.dimens.spacing14,
bottom = TangemTheme.dimens.spacing14,
),
)
if (preEllipsize != null && postEllipsize != null) {
SelectorValueContent(
amount = preEllipsize,
symbol = postEllipsize,
textStyle = textStyle,
)
}
}
if (showDivider) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(TangemTheme.dimens.size1)
.padding(horizontal = TangemTheme.dimens.spacing12)
.background(TangemTheme.colors.stroke.primary)
.align(Alignment.BottomCenter),
)
}
}
}
@Composable
private fun RowScope.SelectorValueContent(amount: TextReference, symbol: TextReference, textStyle: TextStyle) {
Text(
text = amount.resolveReference(),
style = textStyle,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.End,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
modifier = Modifier
.weight(1f)
.padding(
start = TangemTheme.dimens.spacing4,
top = TangemTheme.dimens.spacing14,
bottom = TangemTheme.dimens.spacing14,
),
)
Text(
text = symbol.resolveReference(),
style = textStyle,
color = TangemTheme.colors.text.primary1,
modifier = Modifier
.padding(
start = TangemTheme.dimens.spacing1,
end = TangemTheme.dimens.spacing12,
top = TangemTheme.dimens.spacing14,
bottom = TangemTheme.dimens.spacing14,
),
)
}
@Preview
@Composable
private fun SelectorRowItemPreview_Light() {
TangemTheme {
SelectorRowItem(
titleRes = R.string.common_fee_selector_option_slow,
iconRes = R.drawable.ic_tortoise_24,
preEllipsize = TextReference.Str("1000"),
postEllipsize = TextReference.Str("$"),
isSelected = true,
onSelect = { },
)
}
}
@Preview
@Composable
private fun SelectorRowItemPreview_Dark() {
TangemTheme(isDark = true) {
SelectorRowItem(
titleRes = R.string.common_fee_selector_option_slow,
iconRes = R.drawable.ic_tortoise_24,
preEllipsize = TextReference.Str("1000"),
postEllipsize = TextReference.Str("$"),
isSelected = true,
onSelect = { },
)
}
}

View file

@ -1,7 +0,0 @@
package com.tangem.core.ui.components.rows.states
data class ActionRowState(
val title: String,
val description: String,
val onClick: () -> Unit,
)

View file

@ -1,6 +1,7 @@
package com.tangem.feature.swap.models
import com.tangem.core.ui.components.states.Item
import com.tangem.feature.swap.domain.models.ui.FeeType
import com.tangem.feature.swap.domain.models.ui.TxFee
data class UiActions(
@ -17,4 +18,7 @@ data class UiActions(
val hidePermissionBottomSheet: () -> Unit,
val onChangeApproveType: (ApproveType) -> Unit,
val onSelectItemFee: (Item<TxFee>) -> Unit,
// region new actions
val onClickFee: () -> Unit,
val onSelectFeeType: (FeeType) -> Unit,
)

View file

@ -1,5 +1,11 @@
package com.tangem.feature.swap.models.states
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.feature.swap.domain.models.ui.FeeType
import kotlinx.collections.immutable.ImmutableList
class ChooseFeeBottomSheetConfig : TangemBottomSheetConfigContent
class ChooseFeeBottomSheetConfig(
val selectedFee: FeeType,
val onSelectFeeType: (FeeType) -> Unit,
val feeItems: ImmutableList<FeeItemState>,
) : TangemBottomSheetConfigContent

View file

@ -1,8 +1,13 @@
package com.tangem.feature.swap.models.states
import com.tangem.core.ui.components.rows.states.ActionRowState
import com.tangem.feature.swap.domain.models.ui.FeeType
data class FeeItemState(
val id: String,
val actionRowState: ActionRowState,
val feeType: FeeType,
val title: String,
val amountCrypto: String,
val symbolCrypto: String,
val amountFiat: String,
val symbolFiat: String,
val onClick: () -> Unit,
)

View file

@ -2,12 +2,24 @@ package com.tangem.feature.swap.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.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.SpacerH24
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.rows.SelectorRowItem
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.swap.domain.models.ui.FeeType
import com.tangem.feature.swap.models.states.ChooseFeeBottomSheetConfig
import com.tangem.feature.swap.models.states.FeeItemState
import com.tangem.feature.swap.presentation.R
import kotlinx.collections.immutable.toImmutableList
@Composable
fun ChooseFeeBottomSheet(config: TangemBottomSheetConfig) {
@ -16,11 +28,121 @@ fun ChooseFeeBottomSheet(config: TangemBottomSheetConfig) {
}
}
@Suppress("UnusedPrivateMember")
@Composable
private fun ChooseFeeBottomSheetContent(content: ChooseFeeBottomSheetConfig) {
Column(
modifier = Modifier.background(TangemTheme.colors.background.primary),
modifier = Modifier.background(TangemTheme.colors.background.secondary),
) {
Text(
text = "Choose fee", // todo replace with strings
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
modifier = Modifier
.padding(top = TangemTheme.dimens.spacing10)
.align(Alignment.CenterHorizontally),
)
Column(
modifier = Modifier
.padding(TangemTheme.dimens.spacing16)
.background(
color = TangemTheme.colors.background.action,
shape = TangemTheme.shapes.roundedCornersXMedium,
),
) {
FeeItemsBlock(content)
}
Text(
text = "Network transaction fees are small charges paid to support network security, incentivize " +
"validators," +
" allocate resources, and determine transaction priority.", // todo replace with strings
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
modifier = Modifier
.padding(
vertical = TangemTheme.dimens.spacing8,
horizontal = TangemTheme.dimens.spacing16,
)
.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Start,
)
}
}
@Composable
private fun FeeItemsBlock(content: ChooseFeeBottomSheetConfig) {
content.feeItems.forEach { feeItem ->
val isSelected = feeItem.feeType == content.selectedFee
val preEllipsizeText = feeItem.amountCrypto
val postEllipsizeText = "${feeItem.symbolCrypto} (${feeItem.amountFiat}${feeItem.symbolFiat})"
when (feeItem.feeType) {
FeeType.NORMAL -> {
SelectorRowItem(
titleRes = R.string.common_fee_selector_option_market,
iconRes = R.drawable.ic_bird_24,
preEllipsize = TextReference.Str(preEllipsizeText),
postEllipsize = TextReference.Str(postEllipsizeText),
isSelected = isSelected,
onSelect = { content.onSelectFeeType(feeItem.feeType) },
)
}
FeeType.PRIORITY -> {
SelectorRowItem(
titleRes = R.string.common_fee_selector_option_fast,
iconRes = R.drawable.ic_hare_24,
preEllipsize = TextReference.Str(preEllipsizeText),
postEllipsize = TextReference.Str(postEllipsizeText),
isSelected = isSelected,
onSelect = { content.onSelectFeeType(feeItem.feeType) },
)
}
}
}
}
@Preview
@Composable
private fun ChooseFeeBottomSheetContent_Preview() {
val feeItems = listOf(
FeeItemState(
feeType = FeeType.NORMAL,
title = "Fee",
amountCrypto = "1000",
symbolCrypto = "MATIC",
amountFiat = "10",
symbolFiat = "$",
onClick = {},
),
FeeItemState(
feeType = FeeType.PRIORITY,
title = "Fee",
amountCrypto = "2000",
symbolCrypto = "MATIC",
amountFiat = "20",
symbolFiat = "$",
onClick = {},
),
).toImmutableList()
Column {
TangemTheme(isDark = true) {
ChooseFeeBottomSheetContent(
ChooseFeeBottomSheetConfig(
selectedFee = FeeType.NORMAL,
onSelectFeeType = {},
feeItems = feeItems,
),
)
}
SpacerH24()
TangemTheme(isDark = false) {
ChooseFeeBottomSheetContent(
ChooseFeeBottomSheetConfig(
selectedFee = FeeType.NORMAL,
onSelectFeeType = {},
feeItems = feeItems,
),
)
}
}
}

View file

@ -8,8 +8,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.SpacerH24
import com.tangem.core.ui.components.rows.SimpleActionRow
import com.tangem.core.ui.components.rows.states.ActionRowState
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.swap.domain.models.ui.FeeType
import com.tangem.feature.swap.models.states.FeeItemState
@Composable
@ -21,17 +21,19 @@ fun FeeItem(state: FeeItemState) {
shape = TangemTheme.shapes.roundedCornersXMedium,
)
.clickable(
onClick = state.actionRowState.onClick,
onClick = state.onClick,
)
.fillMaxWidth()
.defaultMinSize(minHeight = TangemTheme.dimens.size68),
) {
val description = "${state.amountCrypto}${state.symbolCrypto} (${state.amountFiat}${state.symbolFiat})"
SimpleActionRow(
modifier = Modifier.padding(
start = TangemTheme.dimens.spacing12,
top = TangemTheme.dimens.spacing12,
),
state = state.actionRowState,
title = state.title,
description = description,
)
}
}
@ -40,12 +42,13 @@ fun FeeItem(state: FeeItemState) {
@Composable
private fun FeeItemPreview() {
val state = FeeItemState(
id = "id",
actionRowState = ActionRowState(
title = "Title",
description = "Description",
onClick = {},
),
feeType = FeeType.NORMAL,
title = "Fee",
amountCrypto = "1000",
symbolCrypto = "MATIC",
amountFiat = "10",
symbolFiat = "$",
onClick = {},
)
Column {
TangemTheme(isDark = false) {

View file

@ -443,6 +443,7 @@ internal class SwapViewModel @Inject constructor(
}
}
@Suppress("LongMethod")
private fun createUiActions(): UiActions {
return UiActions(
onSearchEntered = { onSearchEntered(it) },
@ -510,6 +511,8 @@ internal class SwapViewModel @Inject constructor(
uiState = stateBuilder.updateFeeSelectedItem(uiState, feeItem, isFeeEnough)
}
},
onClickFee = {},
onSelectFeeType = {},
)
}