Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-26 11:51:22 +05:00
commit a66b1bfb14
11 changed files with 700 additions and 53 deletions

View file

@ -54,7 +54,7 @@ fun ProviderChooseCrypto(providerChooseUM: ProviderChooseUM, onClick: () -> Unit
.fillMaxWidth()
.padding(all = 12.dp),
) {
val (iconRef, titleRef, badgeRef, infoRef, labelRef) = createRefs()
val (iconRef, titleRef, extraRef, infoRef, labelRef) = createRefs()
val titleEndRef = createStartBarrier(infoRef, labelRef)
IconContent(
@ -72,19 +72,19 @@ fun ProviderChooseCrypto(providerChooseUM: ProviderChooseUM, onClick: () -> Unit
start.linkTo(iconRef.end, 12.dp)
end.linkTo(titleEndRef, 4.dp)
top.linkTo(parent.top)
bottom.linkTo(badgeRef.top)
bottom.linkTo(extraRef.top)
width = Dimension.fillToConstraints
},
)
BadgeContent(
ExtraContent(
providerChooseUM = providerChooseUM,
modifier = Modifier.constrainAs(badgeRef) {
modifier = Modifier.constrainAs(extraRef) {
start.linkTo(iconRef.end, 12.dp)
end.linkTo(labelRef.start, 4.dp)
top.linkTo(titleRef.bottom, 4.dp)
top.linkTo(titleRef.bottom)
bottom.linkTo(parent.bottom)
width = Dimension.fillToConstraints
visibility = if (providerChooseUM.badgeList.isEmpty()) {
visibility = if (providerChooseUM.extraUM is ProviderChooseUM.ExtraUM.Empty) {
Visibility.Gone
} else {
Visibility.Visible
@ -165,16 +165,36 @@ private fun InfoContent(text: TextReference, modifier: Modifier = Modifier) {
}
@Composable
private fun BadgeContent(providerChooseUM: ProviderChooseUM, modifier: Modifier = Modifier) {
private fun ExtraContent(providerChooseUM: ProviderChooseUM, modifier: Modifier = Modifier) {
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
modifier = modifier,
) {
providerChooseUM.badgeList.fastForEach { badge ->
Badge(
iconRes = badge.iconRes,
text = badge.text,
when (val extraUM = providerChooseUM.extraUM) {
is ProviderChooseUM.ExtraUM.Action -> Text(
text = extraUM.text.resolveReference(),
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier
.padding(top = 4.dp)
.clip(RoundedCornerShape(4.dp))
.background(TangemTheme.colors.button.secondary)
.padding(horizontal = 6.dp, vertical = 1.dp),
)
is ProviderChooseUM.ExtraUM.Badges -> extraUM.badgeList.fastForEach { badge ->
Badge(
iconRes = badge.iconRes,
text = badge.text,
modifier = Modifier.padding(top = 4.dp),
)
}
is ProviderChooseUM.ExtraUM.Error -> Text(
text = extraUM.text.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier.padding(top = 6.dp),
)
else -> Unit
}
}
}
@ -219,18 +239,20 @@ private class ProviderChooseCryptoPreviewProvider : PreviewParameterProvider<Pro
subtitle = stringReference("CEX"),
infoText = stringReference("1 800,00 POL"),
iconUrl = "",
badgeList = persistentListOf(
BadgeUM(
text = stringReference("4.9"),
iconRes = R.drawable.ic_star_outline_24,
),
BadgeUM(
text = stringReference("5 mins"),
iconRes = R.drawable.ic_speed_24,
),
BadgeUM(
text = stringReference("$3.45"),
iconRes = R.drawable.ic_gas_24,
extraUM = ProviderChooseUM.ExtraUM.Badges(
persistentListOf(
BadgeUM(
text = stringReference("4.9"),
iconRes = R.drawable.ic_star_outline_24,
),
BadgeUM(
text = stringReference("5 mins"),
iconRes = R.drawable.ic_speed_24,
),
BadgeUM(
text = stringReference("$3.45"),
iconRes = R.drawable.ic_gas_24,
),
),
),
isSelected = true,
@ -246,19 +268,8 @@ private class ProviderChooseCryptoPreviewProvider : PreviewParameterProvider<Pro
subtitle = stringReference("CEX"),
infoText = stringReference("1 799,12 POL"),
iconUrl = "",
badgeList = persistentListOf(
BadgeUM(
text = stringReference("4.9"),
iconRes = R.drawable.ic_star_outline_24,
),
BadgeUM(
text = stringReference("5 mins"),
iconRes = R.drawable.ic_speed_24,
),
BadgeUM(
text = stringReference("$3.45"),
iconRes = R.drawable.ic_gas_24,
),
extraUM = ProviderChooseUM.ExtraUM.Action(
text = resourceReference(R.string.express_provider_permission_needed),
),
isSelected = false,
labelUM = ProviderChooseUM.LabelUM.Text(
@ -270,7 +281,9 @@ private class ProviderChooseCryptoPreviewProvider : PreviewParameterProvider<Pro
subtitle = stringReference("CEX"),
infoText = stringReference("1 799,12 POL"),
iconUrl = "",
badgeList = persistentListOf(),
extraUM = ProviderChooseUM.ExtraUM.Error(
text = resourceReference(R.string.express_provider_min_amount, wrappedList("1 POL")),
),
isSelected = false,
labelUM = ProviderChooseUM.LabelUM.Text(
text = stringReference("0.2%"),
@ -281,7 +294,7 @@ private class ProviderChooseCryptoPreviewProvider : PreviewParameterProvider<Pro
subtitle = stringReference("CEX"),
infoText = stringReference("1 POL"),
iconUrl = "",
badgeList = persistentListOf(),
extraUM = ProviderChooseUM.ExtraUM.Empty,
isSelected = false,
labelUM = ProviderChooseUM.LabelUM.Text(
text = stringReference("0.2%"),
@ -292,7 +305,7 @@ private class ProviderChooseCryptoPreviewProvider : PreviewParameterProvider<Pro
subtitle = stringReference("CEX"),
infoText = stringReference("1 POL"),
iconUrl = "",
badgeList = persistentListOf(),
extraUM = ProviderChooseUM.ExtraUM.Empty,
isSelected = false,
labelUM = null,
),
@ -301,18 +314,20 @@ private class ProviderChooseCryptoPreviewProvider : PreviewParameterProvider<Pro
subtitle = stringReference("CEX"),
infoText = stringReference("1 POL"),
iconUrl = "",
badgeList = persistentListOf(
BadgeUM(
text = stringReference("4.9"),
iconRes = R.drawable.ic_star_outline_24,
),
BadgeUM(
text = stringReference("5 mins"),
iconRes = R.drawable.ic_speed_24,
),
BadgeUM(
text = stringReference("$3.45"),
iconRes = R.drawable.ic_gas_24,
extraUM = ProviderChooseUM.ExtraUM.Badges(
persistentListOf(
BadgeUM(
text = stringReference("4.9"),
iconRes = R.drawable.ic_star_outline_24,
),
BadgeUM(
text = stringReference("5 mins"),
iconRes = R.drawable.ic_speed_24,
),
BadgeUM(
text = stringReference("$3.45"),
iconRes = R.drawable.ic_gas_24,
),
),
),
isSelected = false,

View file

@ -14,7 +14,7 @@ import kotlinx.collections.immutable.ImmutableList
* @property infoText info text
* @property iconUrl icon url
* @property isSelected indicated whether component is selected (border outline)
* @property badgeList list of badge components
* @property extraUM list of extra components
* @property labelUM label component (whether AuditLabel or text)
*/
data class ProviderChooseUM(
@ -23,7 +23,7 @@ data class ProviderChooseUM(
val infoText: TextReference,
val iconUrl: String,
val isSelected: Boolean,
val badgeList: ImmutableList<BadgeUM>,
val extraUM: ExtraUM,
val labelUM: LabelUM?,
) {
@Immutable
@ -36,4 +36,20 @@ data class ProviderChooseUM(
val text: TextReference,
) : LabelUM()
}
@Immutable
sealed class ExtraUM {
data object Empty : ExtraUM()
data class Badges(
val badgeList: ImmutableList<BadgeUM>,
) : ExtraUM()
data class Action(
val text: TextReference,
) : ExtraUM()
data class Error(
val text: TextReference,
) : ExtraUM()
}
}

View file

@ -0,0 +1,61 @@
package com.tangem.features.swap.v2.impl.chooseprovider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.express.models.ExpressProvider
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.swap.v2.impl.chooseprovider.model.SwapChooseProviderModel
import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderBottomSheet
import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderContent
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import kotlinx.collections.immutable.ImmutableList
internal class SwapChooseProviderComponent(
context: AppComponentContext,
private val params: Params,
) : ComposableBottomSheetComponent, AppComponentContext by context {
private val model: SwapChooseProviderModel = getOrCreateModel(params = params)
override fun dismiss() {
params.onDismiss()
}
@Composable
override fun BottomSheet() {
val state = model.uiState.collectAsStateWithLifecycle()
val bottomSheetConfig = remember(key1 = this) {
TangemBottomSheetConfig(
isShown = true,
onDismissRequest = ::dismiss,
content = TangemBottomSheetConfigContent.Empty,
)
}
SwapChooseProviderBottomSheet(config = bottomSheetConfig) {
SwapChooseProviderContent(
providerList = state.value.providerList,
onProviderClick = model::onProviderClick,
)
}
}
data class Params(
val cryptoCurrency: CryptoCurrency,
val selectedProvider: ExpressProvider,
val providers: ImmutableList<SwapQuoteUM>,
val callback: ModelCallback,
val onDismiss: () -> Unit,
)
interface ModelCallback {
fun onProviderResult(quoteUM: SwapQuoteUM)
}
}

View file

@ -0,0 +1,20 @@
package com.tangem.features.swap.v2.impl.chooseprovider.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
import com.tangem.features.swap.v2.impl.chooseprovider.model.SwapChooseProviderModel
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@InstallIn(ModelComponent::class)
@Module
internal interface SwapChooseProviderModule {
@Binds
@IntoMap
@ClassKey(SwapChooseProviderModel::class)
fun provideSwapChooseProviderModel(impl: SwapChooseProviderModel): Model
}

View file

@ -0,0 +1,16 @@
package com.tangem.features.swap.v2.impl.chooseprovider.entity
import com.tangem.core.ui.components.provider.entity.ProviderChooseUM
import com.tangem.domain.express.models.ExpressProvider
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import kotlinx.collections.immutable.ImmutableList
internal data class SwapChooseProviderBottomSheetContent(
val providerList: ImmutableList<SwapProviderListItem>,
val selectedProvider: ExpressProvider,
)
internal data class SwapProviderListItem(
val providerUM: ProviderChooseUM,
val quote: SwapQuoteUM,
)

View file

@ -0,0 +1,46 @@
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.features.swap.v2.impl.chooseprovider.SwapChooseProviderComponent
import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapChooseProviderBottomSheetContent
import com.tangem.features.swap.v2.impl.chooseprovider.model.converter.SwapProviderListItemConverter
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
@ModelScoped
internal class SwapChooseProviderModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
) : Model() {
private val params: SwapChooseProviderComponent.Params = paramsContainer.require()
val uiState: StateFlow<SwapChooseProviderBottomSheetContent>
field: MutableStateFlow<SwapChooseProviderBottomSheetContent> = MutableStateFlow(getInitialState())
private val swapProviderListItemConverter by lazy(LazyThreadSafetyMode.NONE) {
SwapProviderListItemConverter(
cryptoCurrency = params.cryptoCurrency,
selectedProvider = params.selectedProvider,
)
}
fun onProviderClick(quoteUM: SwapQuoteUM) {
params.callback.onProviderResult(quoteUM)
}
private fun getInitialState(): SwapChooseProviderBottomSheetContent {
return SwapChooseProviderBottomSheetContent(
providerList = swapProviderListItemConverter.convertList(params.providers)
.filterNotNull()
.toPersistentList(),
selectedProvider = params.selectedProvider,
)
}
}

View file

@ -0,0 +1,85 @@
package com.tangem.features.swap.v2.impl.chooseprovider.model.converter
import com.tangem.core.ui.components.audits.AuditLabelUM
import com.tangem.core.ui.components.provider.entity.ProviderChooseUM
import com.tangem.core.ui.components.provider.entity.ProviderChooseUM.LabelUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
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.SwapProviderListItem
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import com.tangem.utils.converter.Converter
internal class SwapProviderListItemConverter(
private val cryptoCurrency: CryptoCurrency,
private val selectedProvider: ExpressProvider,
) : Converter<SwapQuoteUM, SwapProviderListItem?> {
override fun convert(value: SwapQuoteUM): SwapProviderListItem? {
val provider = value.provider ?: return null
return SwapProviderListItem(
providerUM = ProviderChooseUM(
title = stringReference(provider.name),
subtitle = stringReference(provider.type.typeName),
infoText = when (value) {
is SwapQuoteUM.Content -> value.quoteAmountValue
else -> TextReference.EMPTY
},
iconUrl = provider.imageLarge,
isSelected = provider.providerId == selectedProvider.providerId,
extraUM = getExtras(value = value),
labelUM = getLabel(value = value),
),
quote = value,
)
}
private fun getExtras(value: SwapQuoteUM) = when (value) {
is SwapQuoteUM.Allowance -> ProviderChooseUM.ExtraUM.Action(
text = resourceReference(R.string.express_provider_permission_needed),
)
is SwapQuoteUM.Error -> {
ProviderChooseUM.ExtraUM.Error(
when (val error = value.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
},
)
}
SwapQuoteUM.Empty,
SwapQuoteUM.Loading,
is SwapQuoteUM.Content,
-> ProviderChooseUM.ExtraUM.Empty
}
private fun getLabel(value: SwapQuoteUM) = when (val diff = (value as? SwapQuoteUM.Content)?.diffPercent) {
SwapQuoteUM.Content.DifferencePercent.Best -> LabelUM.Info(
AuditLabelUM(
text = resourceReference(R.string.express_provider_best_rate),
type = AuditLabelUM.Type.Info,
),
)
is SwapQuoteUM.Content.DifferencePercent.Diff -> LabelUM.Text(diff.percent)
else -> null
}
}

View file

@ -0,0 +1,124 @@
package com.tangem.features.swap.v2.impl.chooseprovider.ui
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
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.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
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.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.provider.ProviderChooseCrypto
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.resourceReference
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.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.ui.preview.SwapChooseProviderContentPreview
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import kotlinx.collections.immutable.ImmutableList
@Composable
internal fun SwapChooseProviderBottomSheet(config: TangemBottomSheetConfig, content: @Composable () -> Unit) {
TangemModalBottomSheet<TangemBottomSheetConfigContent>(
config = config,
title = {
TangemModalBottomSheetTitle(
title = resourceReference(R.string.express_choose_provider),
endIconRes = R.drawable.ic_close_24,
onEndClick = config.onDismissRequest,
)
},
) {
content()
}
}
@Composable
internal fun SwapChooseProviderContent(
providerList: ImmutableList<SwapProviderListItem>,
onProviderClick: (SwapQuoteUM) -> Unit,
modifier: Modifier = Modifier,
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(horizontal = 13.dp),
) {
providerList.fastForEachIndexed { index, provider ->
ProviderChooseCrypto(
providerChooseUM = provider.providerUM,
onClick = { onProviderClick(provider.quote) },
modifier = modifier
.conditional(index == 0) { padding(top = 6.dp) },
)
}
Icon(
painter = painterResource(id = R.drawable.ic_lightning_16),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
modifier = Modifier.padding(
top = 16.dp,
start = 3.dp,
end = 3.dp,
),
)
Text(
text = stringResourceSafe(R.string.express_more_providers_soon),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.icon.informative,
modifier = Modifier
.padding(
top = 4.dp,
bottom = 24.dp,
start = 3.dp,
end = 3.dp,
),
textAlign = TextAlign.Center,
)
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SwapChooseProviderContent_Preview(
@PreviewParameter(ProviderChooseCryptoPreviewProvider::class) params: SwapChooseProviderBottomSheetContent,
) {
TangemThemePreview {
SwapChooseProviderBottomSheet(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = {},
content = TangemBottomSheetConfigContent.Empty,
),
) {
SwapChooseProviderContent(
providerList = params.providerList,
onProviderClick = {},
)
}
}
}
private class ProviderChooseCryptoPreviewProvider : PreviewParameterProvider<SwapChooseProviderBottomSheetContent> {
override val values: Sequence<SwapChooseProviderBottomSheetContent>
get() = sequenceOf(
SwapChooseProviderContentPreview.state,
)
}
// endregion

View file

@ -0,0 +1,128 @@
package com.tangem.features.swap.v2.impl.chooseprovider.ui
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.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.ripple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.vectorResource
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.SpacerWMax
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.domain.express.models.ExpressProvider
import com.tangem.domain.express.models.ExpressProviderType
import com.tangem.domain.express.models.ExpressRateType
import com.tangem.features.swap.v2.impl.R
@Composable
fun SwapChooseProviderContent(expressProvider: ExpressProvider?, onClick: () -> Unit, modifier: Modifier = Modifier) {
Column(
modifier = modifier.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = ripple(),
onClick = onClick,
),
) {
HorizontalDivider(
thickness = 0.5.dp,
color = TangemTheme.colors.stroke.primary,
modifier = Modifier.padding(horizontal = 12.dp),
)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = modifier.padding(12.dp),
) {
Icon(
painter = rememberVectorPainter(
ImageVector.vectorResource(R.drawable.ic_exchange_horizontal_24),
),
tint = TangemTheme.colors.icon.accent,
contentDescription = null,
)
Text(
text = stringResourceSafe(R.string.express_provider),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.padding(start = 8.dp),
)
SpacerWMax()
SubcomposeAsyncImage(
modifier = modifier
.size(20.dp)
.clip(RoundedCornerShape(4.dp))
.background(TangemColorPalette.Light1),
model = ImageRequest.Builder(context = LocalContext.current)
.data(expressProvider?.imageLarge)
.crossfade(enable = true)
.allowHardware(false)
.build(),
contentDescription = null,
)
Text(
text = expressProvider?.name.orEmpty(), // todo provider error
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier.padding(start = 6.dp),
)
Icon(
painter = rememberVectorPainter(
ImageVector.vectorResource(R.drawable.ic_chevron_24),
),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
modifier = Modifier.padding(start = 4.dp),
)
}
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SwapChooseProviderContent_Preview() {
TangemThemePreview {
Box(
modifier = Modifier.background(TangemTheme.colors.background.tertiary),
) {
SwapChooseProviderContent(
expressProvider = ExpressProvider(
providerId = "changelly",
rateTypes = listOf(ExpressRateType.Fixed),
name = "Changelly",
type = ExpressProviderType.CEX,
imageLarge = "",
termsOfUse = "",
privacyPolicy = "",
isRecommended = false,
slippage = null,
),
onClick = {},
)
}
}
}
// endregion

View file

@ -0,0 +1,90 @@
package com.tangem.features.swap.v2.impl.chooseprovider.ui.preview
import com.tangem.core.ui.components.audits.AuditLabelUM
import com.tangem.core.ui.components.provider.entity.ProviderChooseUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.express.models.ExpressProvider
import com.tangem.domain.express.models.ExpressProviderType
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.common.entity.SwapQuoteUM
import kotlinx.collections.immutable.persistentListOf
import java.math.BigDecimal
internal object SwapChooseProviderContentPreview {
private val provider1 = ExpressProvider(
providerId = "changenow",
rateTypes = listOf(ExpressRateType.Float),
name = "ChangeNow",
type = ExpressProviderType.CEX,
imageLarge = "",
termsOfUse = "",
privacyPolicy = "",
isRecommended = true,
slippage = BigDecimal.ZERO,
)
private val provider2 = provider1.copy(
providerId = "changelly",
name = "Changelly",
)
private val quote1 = SwapQuoteUM.Content(
provider = provider1,
quoteAmount = "123".toBigDecimal(),
quoteAmountValue = stringReference("123"),
diffPercent = SwapQuoteUM.Content.DifferencePercent.Best,
)
private val quote2 = SwapQuoteUM.Content(
provider = provider2,
quoteAmount = "13.12".toBigDecimal(),
quoteAmountValue = stringReference("13.12"),
diffPercent = SwapQuoteUM.Content.DifferencePercent.Empty,
)
val state = SwapChooseProviderBottomSheetContent(
providerList = persistentListOf(
SwapProviderListItem(
providerUM = ProviderChooseUM(
title = stringReference(provider1.name),
subtitle = stringReference(provider1.type.typeName),
infoText = stringReference("1800 POL"),
iconUrl = "",
isSelected = true,
extraUM = ProviderChooseUM.ExtraUM.Empty,
labelUM = ProviderChooseUM.LabelUM.Info(
AuditLabelUM(
text = resourceReference(R.string.express_provider_best_rate),
type = AuditLabelUM.Type.Info,
),
),
),
quote = quote1,
),
SwapProviderListItem(
providerUM = ProviderChooseUM(
title = stringReference(provider2.name),
subtitle = stringReference(provider2.type.typeName),
infoText = stringReference("1800 POL"),
iconUrl = "",
isSelected = false,
extraUM = ProviderChooseUM.ExtraUM.Action(
text = resourceReference(R.string.express_provider_permission_needed),
),
labelUM = ProviderChooseUM.LabelUM.Info(
AuditLabelUM(
text = resourceReference(R.string.express_provider_best_rate),
type = AuditLabelUM.Type.Info,
),
),
),
quote = quote2,
),
),
selectedProvider = provider1,
)
}

View file

@ -0,0 +1,46 @@
package com.tangem.features.swap.v2.impl.common.entity
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.express.models.ExpressError
import com.tangem.domain.express.models.ExpressProvider
import java.math.BigDecimal
@Immutable
internal sealed class SwapQuoteUM {
abstract val provider: ExpressProvider?
data object Empty : SwapQuoteUM() {
override val provider = null
}
data object Loading : SwapQuoteUM() {
override val provider = null
}
data class Error(
override val provider: ExpressProvider,
val expressError: ExpressError,
) : SwapQuoteUM()
data class Allowance(
override val provider: ExpressProvider,
val allowanceContract: String,
) : SwapQuoteUM()
data class Content(
override val provider: ExpressProvider,
val quoteAmount: BigDecimal,
val quoteAmountValue: TextReference,
val diffPercent: DifferencePercent,
) : SwapQuoteUM() {
sealed class DifferencePercent {
data object Empty : DifferencePercent()
data object Best : DifferencePercent()
data class Diff(
val percent: TextReference,
) : DifferencePercent()
}
}
}