Updated on 2026-08-14
This commit is contained in:
commit
56a3a67eb7
12 changed files with 443 additions and 20 deletions
|
|
@ -23,6 +23,7 @@ import androidx.constraintlayout.compose.Visibility
|
|||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.audits.AuditLabel
|
||||
import com.tangem.core.ui.components.audits.AuditLabelUM
|
||||
import com.tangem.core.ui.components.badge.Badge
|
||||
|
|
@ -50,7 +51,6 @@ private const val DISABLED_ICON_ALPHA = 0.4f
|
|||
fun ProviderChooseCrypto(providerChooseUM: ProviderChooseUM, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
ConstraintLayout(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.selectedBorder(isSelected = providerChooseUM.isSelected)
|
||||
.clickable(
|
||||
|
|
@ -133,13 +133,23 @@ private fun IconContent(iconUrl: String, modifier: Modifier = Modifier) {
|
|||
SubcomposeAsyncImage(
|
||||
modifier = modifier
|
||||
.size(40.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(TangemColorPalette.Light1),
|
||||
.clip(RoundedCornerShape(8.dp)),
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(iconUrl)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(false)
|
||||
.build(),
|
||||
loading = {
|
||||
RectangleShimmer(radius = 8.dp)
|
||||
},
|
||||
error = {
|
||||
Box(
|
||||
modifier = Modifier.background(
|
||||
color = TangemColorPalette.Light1,
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
),
|
||||
)
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ internal class SwapAmountSetQuotesTransformer(
|
|||
val percent = quote.quoteAmount / bestQuote.quoteAmount - BigDecimal.ONE
|
||||
quote.copy(
|
||||
diffPercent = DifferencePercent.Diff(
|
||||
isPositive = percent.isPositive(),
|
||||
percent = stringReference(
|
||||
if (percent.isPositive()) {
|
||||
"${StringsSigns.PLUS}${percent.format { percent() }}"
|
||||
|
|
|
|||
|
|
@ -13,5 +13,6 @@ internal data class SwapChooseProviderBottomSheetContent(
|
|||
|
||||
internal data class SwapProviderListItem(
|
||||
val providerUM: ProviderChooseUM,
|
||||
val swapProviderState: SwapProviderState,
|
||||
val quote: SwapQuoteUM,
|
||||
)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.tangem.features.swap.v2.impl.chooseprovider.entity
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM.Content.DifferencePercent
|
||||
|
||||
@Deprecated("Use ProviderChooseUM with new design")
|
||||
@Immutable
|
||||
internal sealed class SwapProviderState {
|
||||
|
||||
abstract val isSelected: Boolean
|
||||
|
||||
data object Empty : SwapProviderState() {
|
||||
override val isSelected = false
|
||||
}
|
||||
|
||||
data class Content(
|
||||
override val isSelected: Boolean,
|
||||
val name: String,
|
||||
val type: String,
|
||||
val iconUrl: String,
|
||||
val subtitle: TextReference,
|
||||
val additionalBadge: AdditionalBadge,
|
||||
val diffPercent: DifferencePercent,
|
||||
) : SwapProviderState()
|
||||
|
||||
@Immutable
|
||||
sealed class AdditionalBadge {
|
||||
data object FCAWarningList : AdditionalBadge()
|
||||
data object BestTrade : AdditionalBadge()
|
||||
data object Empty : AdditionalBadge()
|
||||
data object PermissionRequired : AdditionalBadge()
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.swap.v2.impl.chooseprovider.model
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.express.models.ExpressError
|
||||
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
|
||||
import com.tangem.features.swap.v2.impl.chooseprovider.SwapChooseProviderComponent
|
||||
import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapChooseProviderBottomSheetContent
|
||||
|
|
@ -10,6 +11,7 @@ import com.tangem.features.swap.v2.impl.chooseprovider.model.converter.SwapProvi
|
|||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.features.swap.v2.impl.common.isRestrictedByFCA
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.isSingleItem
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -30,6 +32,7 @@ internal class SwapChooseProviderModel @Inject constructor(
|
|||
cryptoCurrency = params.cryptoCurrency,
|
||||
selectedProvider = params.selectedProvider,
|
||||
needApplyFCARestrictions = needApplyFCARestrictions,
|
||||
needBestRateBadge = params.providers.filterIsInstance<SwapQuoteUM.Content>().isSingleItem().not(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -42,9 +45,14 @@ internal class SwapChooseProviderModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun getInitialState(): SwapChooseProviderBottomSheetContent {
|
||||
val filteredProviderList = params.providers.filter {
|
||||
it is SwapQuoteUM.Content ||
|
||||
it is SwapQuoteUM.Allowance ||
|
||||
(it as? SwapQuoteUM.Error)?.expressError is ExpressError.AmountError
|
||||
}
|
||||
return SwapChooseProviderBottomSheetContent(
|
||||
isApplyFCARestrictions = needApplyFCARestrictions && params.selectedProvider.isRestrictedByFCA(),
|
||||
providerList = swapProviderListItemConverter.convertList(params.providers)
|
||||
providerList = swapProviderListItemConverter.convertList(filteredProviderList)
|
||||
.filterNotNull()
|
||||
.toPersistentList(),
|
||||
selectedProvider = params.selectedProvider,
|
||||
|
|
|
|||
|
|
@ -22,11 +22,21 @@ internal class SwapProviderListItemConverter(
|
|||
private val cryptoCurrency: CryptoCurrency,
|
||||
private val selectedProvider: ExpressProvider,
|
||||
private val needApplyFCARestrictions: Boolean,
|
||||
needBestRateBadge: Boolean,
|
||||
) : Converter<SwapQuoteUM, SwapProviderListItem?> {
|
||||
|
||||
private val providerStateConverter = SwapProviderStateConverter(
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
selectedProvider = selectedProvider,
|
||||
needApplyFCARestrictions = needApplyFCARestrictions,
|
||||
isNeedBestRateBadge = needBestRateBadge,
|
||||
)
|
||||
|
||||
override fun convert(value: SwapQuoteUM): SwapProviderListItem? {
|
||||
val provider = value.provider ?: return null
|
||||
|
||||
return SwapProviderListItem(
|
||||
swapProviderState = providerStateConverter.convert(value),
|
||||
providerUM = ProviderChooseUM(
|
||||
title = stringReference(provider.name),
|
||||
subtitle = stringReference(provider.type.typeName),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.features.swap.v2.impl.chooseprovider.model.converter
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.express.models.ExpressError
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.swap.v2.impl.R
|
||||
import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState
|
||||
import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState.AdditionalBadge
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.features.swap.v2.impl.common.isRestrictedByFCA
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
@Deprecated("Remove with new design")
|
||||
internal class SwapProviderStateConverter(
|
||||
private val cryptoCurrency: CryptoCurrency,
|
||||
private val selectedProvider: ExpressProvider,
|
||||
private val isNeedBestRateBadge: Boolean,
|
||||
private val needApplyFCARestrictions: Boolean,
|
||||
) : Converter<SwapQuoteUM, SwapProviderState> {
|
||||
|
||||
override fun convert(value: SwapQuoteUM): SwapProviderState {
|
||||
return when (value) {
|
||||
is SwapQuoteUM.Content -> value.convertToContent()
|
||||
is SwapQuoteUM.Error -> value.convertToErrorContent()
|
||||
is SwapQuoteUM.Allowance,
|
||||
SwapQuoteUM.Empty,
|
||||
SwapQuoteUM.Loading,
|
||||
-> SwapProviderState.Empty
|
||||
}
|
||||
}
|
||||
|
||||
private fun SwapQuoteUM.Content.convertToContent(): SwapProviderState {
|
||||
val isBestRate = when (diffPercent) {
|
||||
SwapQuoteUM.Content.DifferencePercent.Best -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
val additionalBadge = when {
|
||||
needApplyFCARestrictions && provider.isRestrictedByFCA() -> AdditionalBadge.FCAWarningList
|
||||
isNeedBestRateBadge && isBestRate && !needApplyFCARestrictions -> AdditionalBadge.BestTrade
|
||||
else -> AdditionalBadge.Empty
|
||||
}
|
||||
|
||||
return SwapProviderState.Content(
|
||||
name = provider.name,
|
||||
iconUrl = provider.imageLarge,
|
||||
type = provider.type.typeName,
|
||||
subtitle = quoteAmountValue,
|
||||
additionalBadge = additionalBadge,
|
||||
diffPercent = diffPercent,
|
||||
isSelected = provider == selectedProvider,
|
||||
)
|
||||
}
|
||||
|
||||
private fun SwapQuoteUM.Error.convertToErrorContent(): SwapProviderState {
|
||||
val additionalBadge = when {
|
||||
needApplyFCARestrictions && provider.isRestrictedByFCA() -> AdditionalBadge.FCAWarningList
|
||||
else -> AdditionalBadge.Empty
|
||||
}
|
||||
|
||||
return SwapProviderState.Content(
|
||||
name = provider.name,
|
||||
iconUrl = provider.imageLarge,
|
||||
type = provider.type.typeName,
|
||||
subtitle = when (val error = expressError) {
|
||||
is ExpressError.AmountError.TooSmallError -> resourceReference(
|
||||
id = R.string.express_provider_min_amount,
|
||||
formatArgs = wrappedList(
|
||||
error.amount.format { crypto(cryptoCurrency) },
|
||||
),
|
||||
)
|
||||
is ExpressError.AmountError.NotEnoughAllowanceError,
|
||||
is ExpressError.AmountError.TooBigError,
|
||||
-> resourceReference(
|
||||
id = R.string.express_provider_max_amount,
|
||||
formatArgs = wrappedList(
|
||||
error.amount.format { crypto(cryptoCurrency) },
|
||||
),
|
||||
)
|
||||
else -> TextReference.EMPTY
|
||||
},
|
||||
additionalBadge = additionalBadge,
|
||||
diffPercent = SwapQuoteUM.Content.DifferencePercent.Empty,
|
||||
isSelected = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,13 +2,16 @@ package com.tangem.features.swap.v2.impl.chooseprovider.ui
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
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.draw.clip
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -16,14 +19,14 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.components.provider.ProviderChooseCrypto
|
||||
import com.tangem.core.ui.extensions.conditional
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.selectedBorder
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
|
@ -57,13 +60,14 @@ internal fun SwapChooseProviderContent(
|
|||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(horizontal = 13.dp),
|
||||
modifier = modifier.padding(horizontal = 13.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.onramp_choose_provider_title_hint),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(bottom = 4.dp),
|
||||
)
|
||||
AnimatedVisibility(
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
|
|
@ -75,12 +79,18 @@ internal fun SwapChooseProviderContent(
|
|||
iconTint = TangemTheme.colors.icon.warning,
|
||||
)
|
||||
}
|
||||
SpacerH12()
|
||||
contentUM.providerList.fastForEachIndexed { index, provider ->
|
||||
ProviderChooseCrypto(
|
||||
providerChooseUM = provider.providerUM,
|
||||
onClick = { onProviderClick(provider.quote) },
|
||||
modifier = modifier
|
||||
.conditional(index == 0) { padding(top = 24.dp) },
|
||||
SwapProviderItem(
|
||||
state = provider.swapProviderState,
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(14.dp))
|
||||
.selectedBorder(isSelected = provider.swapProviderState.isSelected)
|
||||
.clickable(
|
||||
enabled = provider.quote !is SwapQuoteUM.Error,
|
||||
onClick = { onProviderClick(provider.quote) },
|
||||
)
|
||||
.padding(12.dp),
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
|
|
|
|||
|
|
@ -4,11 +4,7 @@ import android.content.res.Configuration
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
|
|
@ -27,6 +23,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
|
|
@ -57,7 +54,7 @@ fun SwapChooseProviderContent(expressProvider: ExpressProvider?, onClick: () ->
|
|||
) {
|
||||
Icon(
|
||||
painter = rememberVectorPainter(
|
||||
ImageVector.vectorResource(R.drawable.ic_exchange_horizontal_24),
|
||||
ImageVector.vectorResource(R.drawable.ic_stack_new_24),
|
||||
),
|
||||
tint = TangemTheme.colors.icon.accent,
|
||||
contentDescription = null,
|
||||
|
|
@ -72,13 +69,21 @@ fun SwapChooseProviderContent(expressProvider: ExpressProvider?, onClick: () ->
|
|||
SubcomposeAsyncImage(
|
||||
modifier = modifier
|
||||
.size(20.dp)
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.background(TangemColorPalette.Light1),
|
||||
.clip(RoundedCornerShape(4.dp)),
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(expressProvider?.imageLarge)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(false)
|
||||
.build(),
|
||||
loading = { RectangleShimmer(radius = 4.dp) },
|
||||
error = {
|
||||
Box(
|
||||
modifier = Modifier.background(
|
||||
color = TangemColorPalette.Light1,
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
),
|
||||
)
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,232 @@
|
|||
package com.tangem.features.swap.v2.impl.chooseprovider.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
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
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
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 coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
|
||||
@Deprecated("Use ProviderChooseCrypto with new design")
|
||||
@Composable
|
||||
internal fun SwapProviderItem(state: SwapProviderState, modifier: Modifier = Modifier) {
|
||||
when (state) {
|
||||
is SwapProviderState.Content -> ProviderContentState(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
)
|
||||
is SwapProviderState.Empty -> { /* no-op */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun ProviderContentState(state: SwapProviderState.Content, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
SubcomposeAsyncImage(
|
||||
modifier = Modifier
|
||||
.size(size = 40.dp)
|
||||
.clip(TangemTheme.shapes.roundedCorners8),
|
||||
model = ImageRequest.Builder(context = LocalContext.current).data(state.iconUrl)
|
||||
.crossfade(enable = true).allowHardware(false).build(),
|
||||
loading = { RectangleShimmer(radius = 8.dp) },
|
||||
error = {
|
||||
ErrorProviderIcon(Modifier.size(size = 40.dp))
|
||||
},
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Column(modifier = Modifier.padding(start = 12.dp)) {
|
||||
Row {
|
||||
Text(
|
||||
text = state.name,
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = state.type,
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
)
|
||||
val badgeModifier = Modifier.padding(start = 4.dp)
|
||||
when (state.additionalBadge) {
|
||||
SwapProviderState.AdditionalBadge.FCAWarningList -> FCABadgeItem(badgeModifier)
|
||||
SwapProviderState.AdditionalBadge.BestTrade -> BestTradeItem(badgeModifier)
|
||||
SwapProviderState.AdditionalBadge.PermissionRequired -> PermissionBadgeItem(badgeModifier)
|
||||
SwapProviderState.AdditionalBadge.Empty -> Unit
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.padding(top = 2.dp),
|
||||
) {
|
||||
Text(
|
||||
text = state.subtitle.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
|
||||
if (state.diffPercent is SwapQuoteUM.Content.DifferencePercent.Diff) {
|
||||
val textColor = if (state.diffPercent.isPositive) {
|
||||
TangemTheme.colors.icon.accent
|
||||
} else {
|
||||
TangemTheme.colors.text.warning
|
||||
}
|
||||
|
||||
Text(
|
||||
text = state.diffPercent.percent.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = textColor,
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ErrorProviderIcon(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier.background(
|
||||
color = TangemTheme.colors.background.secondary,
|
||||
shape = TangemTheme.shapes.roundedCorners8,
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.matchParentSize(),
|
||||
painter = painterResource(id = R.drawable.ic_custom_token_44),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestTradeItem(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier.background(
|
||||
color = TangemTheme.colors.icon.accent.copy(alpha = 0.1f),
|
||||
shape = TangemTheme.shapes.roundedCornersLarge,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.express_provider_best_rate),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.icon.accent,
|
||||
modifier = Modifier.padding(horizontal = 6.dp),
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PermissionBadgeItem(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier.background(
|
||||
color = TangemTheme.colors.background.secondary,
|
||||
shape = TangemTheme.shapes.roundedCornersLarge,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.express_provider_permission_needed),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.padding(horizontal = 6.dp),
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FCABadgeItem(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier.background(
|
||||
color = TangemTheme.colors.background.secondary,
|
||||
shape = TangemTheme.shapes.roundedCornersLarge,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.express_provider_fca_warning_list),
|
||||
style = TangemTheme.typography.caption1,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.padding(horizontal = 6.dp),
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun ProviderItemPreview(
|
||||
@PreviewParameter(ProviderItemParameterProvider::class) state: Pair<SwapProviderState, Boolean>,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
SwapProviderItem(
|
||||
modifier = Modifier.background(TangemTheme.colors.background.action),
|
||||
state = state.first,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ProviderItemParameterProvider : CollectionPreviewParameterProvider<Pair<SwapProviderState, Boolean>>(
|
||||
collection = buildList {
|
||||
val contentState = SwapProviderState.Content(
|
||||
name = "1inch",
|
||||
type = "DEX",
|
||||
iconUrl = "",
|
||||
subtitle = stringReference(value = "0,64554846 DAI ≈ 1 MATIC"),
|
||||
additionalBadge = SwapProviderState.AdditionalBadge.Empty,
|
||||
diffPercent = SwapQuoteUM.Content.DifferencePercent.Diff(
|
||||
isPositive = false,
|
||||
percent = stringReference("-10%"),
|
||||
),
|
||||
isSelected = true,
|
||||
)
|
||||
val contentState2 = contentState.copy(
|
||||
subtitle = stringReference(value = "1 132,46 MATIC"),
|
||||
additionalBadge = SwapProviderState.AdditionalBadge.PermissionRequired,
|
||||
diffPercent = SwapQuoteUM.Content.DifferencePercent.Diff(
|
||||
isPositive = true,
|
||||
percent = stringReference("+10%"),
|
||||
),
|
||||
)
|
||||
add(contentState to true)
|
||||
add(contentState to false)
|
||||
|
||||
add(contentState2 to true)
|
||||
add(contentState2 to false)
|
||||
},
|
||||
)
|
||||
// endregion Preview
|
||||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.express.models.ExpressRateType
|
|||
import com.tangem.features.swap.v2.impl.R
|
||||
import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapChooseProviderBottomSheetContent
|
||||
import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderListItem
|
||||
import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -65,6 +66,15 @@ internal object SwapChooseProviderContentPreview {
|
|||
),
|
||||
),
|
||||
),
|
||||
swapProviderState = SwapProviderState.Content(
|
||||
name = provider1.name,
|
||||
type = provider1.type.typeName,
|
||||
iconUrl = "",
|
||||
subtitle = stringReference("1800 POL"),
|
||||
additionalBadge = SwapProviderState.AdditionalBadge.BestTrade,
|
||||
diffPercent = SwapQuoteUM.Content.DifferencePercent.Best,
|
||||
isSelected = true,
|
||||
),
|
||||
quote = quote1,
|
||||
),
|
||||
SwapProviderListItem(
|
||||
|
|
@ -85,6 +95,15 @@ internal object SwapChooseProviderContentPreview {
|
|||
),
|
||||
),
|
||||
quote = quote2,
|
||||
swapProviderState = SwapProviderState.Content(
|
||||
name = provider1.name,
|
||||
type = provider1.type.typeName,
|
||||
iconUrl = "",
|
||||
subtitle = stringReference("1800 POL"),
|
||||
additionalBadge = SwapProviderState.AdditionalBadge.BestTrade,
|
||||
diffPercent = SwapQuoteUM.Content.DifferencePercent.Best,
|
||||
isSelected = true,
|
||||
),
|
||||
),
|
||||
),
|
||||
selectedProvider = provider1,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ internal sealed class SwapQuoteUM {
|
|||
data object Empty : DifferencePercent()
|
||||
data object Best : DifferencePercent()
|
||||
data class Diff(
|
||||
val isPositive: Boolean,
|
||||
val percent: TextReference,
|
||||
) : DifferencePercent()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue