From 2f6c106bd7bbe13691e916811ecfea99718a6b5b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 16 Nov 2023 11:33:30 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../core/ui/components/rows/ActionRow.kt | 22 +-- .../ui/components/rows/SelectorRowItem.kt | 160 ++++++++++++++++++ .../components/rows/states/ActionRowState.kt | 7 - .../tangem/feature/swap/models/UiActions.kt | 4 + .../states/ChooseFeeBottomSheetConfig.kt | 8 +- .../swap/models/states/FeeItemState.kt | 11 +- .../feature/swap/ui/ChooseFeeBottomSheet.kt | 126 +++++++++++++- .../com/tangem/feature/swap/ui/FeeItem.kt | 21 ++- .../feature/swap/viewmodels/SwapViewModel.kt | 3 + 9 files changed, 329 insertions(+), 33 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/rows/SelectorRowItem.kt delete mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/rows/states/ActionRowState.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/ActionRow.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/ActionRow.kt index 4dd409c78f..5bdaa8f68b 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/ActionRow.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/ActionRow.kt @@ -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", + ) } } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/SelectorRowItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/SelectorRowItem.kt new file mode 100644 index 0000000000..667ec44c08 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/rows/SelectorRowItem.kt @@ -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 = { }, + ) + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/rows/states/ActionRowState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/rows/states/ActionRowState.kt deleted file mode 100644 index 35c6097d3a..0000000000 --- a/core/ui/src/main/java/com/tangem/core/ui/components/rows/states/ActionRowState.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.tangem.core.ui.components.rows.states - -data class ActionRowState( - val title: String, - val description: String, - val onClick: () -> Unit, -) \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt index d250e06df6..0dc67646d0 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt @@ -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) -> Unit, + // region new actions + val onClickFee: () -> Unit, + val onSelectFeeType: (FeeType) -> Unit, ) \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ChooseFeeBottomSheetConfig.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ChooseFeeBottomSheetConfig.kt index f12f22bd79..82a1008a27 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ChooseFeeBottomSheetConfig.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ChooseFeeBottomSheetConfig.kt @@ -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 \ No newline at end of file +class ChooseFeeBottomSheetConfig( + val selectedFee: FeeType, + val onSelectFeeType: (FeeType) -> Unit, + val feeItems: ImmutableList, +) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/FeeItemState.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/FeeItemState.kt index be4e3a3366..beed7112a6 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/FeeItemState.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/FeeItemState.kt @@ -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, ) \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt index 15d2f05826..f967baef74 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt @@ -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, + ), + ) + } } } \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/FeeItem.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/FeeItem.kt index 4f895f7d34..c1d2976921 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/FeeItem.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/FeeItem.kt @@ -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) { diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index c9c2686539..71c151673d 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -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 = {}, ) }