Updated on 2026-08-14

This commit is contained in:
Tangem 2024-12-18 22:57:32 +05:00
parent 7bc1381aa9
commit 1cc16506e1
7 changed files with 162 additions and 78 deletions

View file

@ -468,6 +468,7 @@ internal class DefaultOnrampRepository(
OnrampQuote.Error(
paymentMethod = paymentMethod,
provider = provider,
fromAmount = fromOnrampAmount,
error = onrampError,
)
}

View file

@ -6,11 +6,12 @@ sealed class OnrampQuote {
abstract val paymentMethod: OnrampPaymentMethod
abstract val provider: OnrampProvider
abstract val fromAmount: OnrampAmount
data class Data(
override val paymentMethod: OnrampPaymentMethod,
override val provider: OnrampProvider,
val fromAmount: OnrampAmount,
override val fromAmount: OnrampAmount,
val toAmount: OnrampAmount,
val minFromAmount: OnrampAmount,
val maxFromAmount: OnrampAmount,
@ -19,13 +20,14 @@ sealed class OnrampQuote {
data class AmountError(
override val paymentMethod: OnrampPaymentMethod,
override val provider: OnrampProvider,
val fromAmount: OnrampAmount,
override val fromAmount: OnrampAmount,
val error: OnrampError.AmountError,
) : OnrampQuote()
data class Error(
override val paymentMethod: OnrampPaymentMethod,
override val provider: OnrampProvider,
override val fromAmount: OnrampAmount,
val error: OnrampError,
) : OnrampQuote()
}

View file

@ -6,30 +6,42 @@ internal sealed interface ProviderListItemUM {
val providerId: String
val name: String
val imageUrl: String
data class Available(
override val providerId: String,
override val name: String,
val imageUrl: String,
val rate: String,
val isBestRate: Boolean,
val diffRate: TextReference?,
val isSelected: Boolean,
val onClick: () -> Unit,
) : ProviderListItemUM
sealed interface Available : ProviderListItemUM {
val onClick: () -> Unit
val providerResult: SelectProviderResult
val isBestRate: Boolean
val isSelected: Boolean
data class AvailableWithError(
override val providerId: String,
override val name: String,
val imageUrl: String,
val subtitle: TextReference,
val onClick: () -> Unit,
) : ProviderListItemUM
data class Content(
override val providerId: String,
override val name: String,
override val onClick: () -> Unit,
override val imageUrl: String,
override val providerResult: SelectProviderResult,
override val isBestRate: Boolean,
override val isSelected: Boolean,
val rate: String,
val diffRate: TextReference?,
) : Available
data class WithError(
override val providerId: String,
override val name: String,
override val onClick: () -> Unit,
override val imageUrl: String,
override val providerResult: SelectProviderResult,
override val isBestRate: Boolean,
override val isSelected: Boolean,
val subtitle: TextReference,
) : Available
}
data class Unavailable(
override val providerId: String,
override val name: String,
val imageUrl: String,
override val imageUrl: String,
val subtitle: TextReference,
) : ProviderListItemUM
}

View file

@ -4,13 +4,14 @@ import com.tangem.domain.onramp.model.OnrampPaymentMethod
import kotlinx.collections.immutable.ImmutableList
internal data class SelectPaymentAndProviderUM(
val paymentMethods: ImmutableList<OnrampPaymentMethod>,
val selectedPaymentMethod: SelectProviderUM,
val paymentMethods: ImmutableList<PaymentProviderUM>,
val selectedPaymentMethod: PaymentProviderUM,
val selectedProviderId: String,
val isPaymentMethodClickEnabled: Boolean,
val onPaymentMethodClick: () -> Unit,
)
internal data class SelectProviderUM(
internal data class PaymentProviderUM(
val paymentMethod: OnrampPaymentMethod,
val providers: ImmutableList<ProviderListItemUM>,
)

View file

@ -72,9 +72,21 @@ internal class SelectProviderModel @Inject constructor(
},
ifRight = { it },
)
val filteredEmptyMethods = methods.mapNotNull { method ->
val providers = getOnrampProviderWithQuoteUseCase(method).getOrNull()
if (!providers.isNullOrEmpty()) {
PaymentProviderUM(
paymentMethod = method,
providers = providers.toProvidersListItems(),
)
} else {
null
}
}
_state.value = state.value.copy(
paymentMethods = methods.toPersistentList(),
isPaymentMethodClickEnabled = methods.isNotEmpty(),
paymentMethods = filteredEmptyMethods.toPersistentList(),
isPaymentMethodClickEnabled = filteredEmptyMethods.isNotEmpty(),
)
}
}
@ -111,13 +123,19 @@ internal class SelectProviderModel @Inject constructor(
private fun getInitialState(): SelectPaymentAndProviderUM {
return SelectPaymentAndProviderUM(
paymentMethods = persistentListOf(params.selectedPaymentMethod),
paymentMethods = persistentListOf(
PaymentProviderUM(
paymentMethod = params.selectedPaymentMethod,
providers = emptyList<ProviderListItemUM>().toImmutableList(),
),
),
isPaymentMethodClickEnabled = false,
onPaymentMethodClick = ::openPaymentMethods,
selectedPaymentMethod = SelectProviderUM(
selectedPaymentMethod = PaymentProviderUM(
paymentMethod = params.selectedPaymentMethod,
providers = emptyList<ProviderListItemUM>().toImmutableList(),
),
selectedProviderId = params.selectedProviderId,
)
}
@ -131,16 +149,18 @@ internal class SelectProviderModel @Inject constructor(
)
}
private fun ImmutableList<OnrampPaymentMethod>.toPaymentUMList(): ImmutableList<PaymentMethodUM> = map { method ->
private fun ImmutableList<PaymentProviderUM>.toPaymentUMList(): ImmutableList<PaymentMethodUM> = map { method ->
PaymentMethodUM(
id = method.id,
imageUrl = method.imageUrl,
name = method.name,
id = method.paymentMethod.id,
imageUrl = method.paymentMethod.imageUrl,
name = method.paymentMethod.name,
onSelect = { onPaymentMethodSelected(method) },
)
}.toPersistentList()
private fun onPaymentMethodSelected(paymentMethod: OnrampPaymentMethod) {
private fun onPaymentMethodSelected(methodContainer: PaymentProviderUM) {
val paymentMethod = methodContainer.paymentMethod
val firstProvider = methodContainer.providers.firstOrNull()
analyticsEventHandler.send(OnrampAnalyticsEvent.OnPaymentMethodChosen(paymentMethod = paymentMethod.name))
modelScope.launch {
saveSelectedPaymentMethod.invoke(paymentMethod)
@ -151,6 +171,10 @@ internal class SelectProviderModel @Inject constructor(
),
)
}
if (firstProvider is ProviderListItemUM.Available) {
onProviderSelected(firstProvider.providerResult, firstProvider.isBestRate)
}
bottomSheetNavigation.dismiss()
}
}
@ -165,6 +189,8 @@ internal class SelectProviderModel @Inject constructor(
val hasBestProvider = isMultipleQuotes && isOtherQuotesHasData
return sorted.map { quote ->
val isSelectedProvider = state.value.selectedProviderId == quote.provider.id
when (quote) {
is OnrampProviderWithQuote.Data -> {
val rate = quote.toAmount.value.format {
@ -173,28 +199,29 @@ internal class SelectProviderModel @Inject constructor(
val rateDiff = bestProvider?.toAmount?.value?.let { bestRate ->
BigDecimal.ONE - quote.toAmount.value / bestRate
}
val isSelectedProvider = quote.provider.id == params.selectedProviderId
val isSelectedPayment = quote.paymentMethod.id == params.selectedPaymentMethod.id
val isBestProvider = quote == bestProvider && hasBestProvider
ProviderListItemUM.Available(
val providerResult = SelectProviderResult.ProviderWithQuote(
paymentMethod = quote.paymentMethod,
provider = quote.provider,
fromAmount = quote.fromAmount,
toAmount = quote.toAmount,
)
ProviderListItemUM.Available.Content(
providerId = quote.provider.id,
imageUrl = quote.provider.info.imageLarge,
name = quote.provider.info.name,
rate = rate,
isSelected = isSelectedProvider && isSelectedPayment,
isBestRate = isBestProvider,
diffRate = stringReference("$MINUS${rateDiff.format { percent() }}")
.takeIf { hasBestProvider },
providerResult = providerResult,
isSelected = isSelectedProvider,
onClick = {
onProviderSelected(
result = SelectProviderResult.ProviderWithQuote(
paymentMethod = quote.paymentMethod,
provider = quote.provider,
fromAmount = quote.fromAmount,
toAmount = quote.toAmount,
),
result = providerResult,
isBestRate = isBestProvider,
)
params.onDismiss()
},
)
}
@ -207,21 +234,25 @@ internal class SelectProviderModel @Inject constructor(
is OnrampError.AmountError.TooBigError -> R.string.express_provider_max_amount
is OnrampError.AmountError.TooSmallError -> R.string.express_provider_min_amount
}
ProviderListItemUM.AvailableWithError(
val providerResult = SelectProviderResult.ProviderWithError(
paymentMethod = quoteError.paymentMethod,
provider = quote.provider,
quoteError = quote.quoteError,
)
ProviderListItemUM.Available.WithError(
providerId = quote.provider.id,
imageUrl = quote.provider.info.imageLarge,
name = quote.provider.info.name,
subtitle = resourceReference(errorSubtitleRes, wrappedList(amount)),
providerResult = providerResult,
isBestRate = false,
isSelected = isSelectedProvider,
onClick = {
onProviderSelected(
result = SelectProviderResult.ProviderWithError(
paymentMethod = quoteError.paymentMethod,
provider = quote.provider,
quoteError = quote.quoteError,
),
result = providerResult,
isBestRate = bestProvider == quote,
)
params.onDismiss()
},
)
}
@ -247,8 +278,10 @@ internal class SelectProviderModel @Inject constructor(
tokenSymbol = params.cryptoCurrency.symbol,
),
)
_state.update {
it.copy(selectedProviderId = result.provider.id)
}
params.onProviderClick(result, isBestRate)
params.onDismiss()
}
private fun sendOnrampErrorEvent(error: OnrampError) {

View file

@ -2,59 +2,83 @@ package com.tangem.features.onramp.providers.model.previewData
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.onramp.model.OnrampPaymentMethod
import com.tangem.domain.onramp.model.PaymentMethodType
import com.tangem.domain.onramp.model.*
import com.tangem.features.onramp.providers.entity.PaymentProviderUM
import com.tangem.features.onramp.providers.entity.ProviderListItemUM
import com.tangem.features.onramp.providers.entity.SelectPaymentAndProviderUM
import com.tangem.features.onramp.providers.entity.SelectProviderUM
import com.tangem.features.onramp.providers.entity.SelectProviderResult
import kotlinx.collections.immutable.persistentListOf
import java.math.BigDecimal
internal object SelectProviderPreviewData {
private val paymentMethods = persistentListOf(
OnrampPaymentMethod(
id = "card",
name = "Card",
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
type = PaymentMethodType.CARD,
),
private val paymentMethod = OnrampPaymentMethod(
id = "card",
name = "Card",
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
type = PaymentMethodType.CARD,
)
private val providerListUM = SelectProviderUM(
paymentMethod = paymentMethods.first(),
private val provider = OnrampProvider(
id = "mercuryo",
info = OnrampProviderInfo(
name = "Mercuryo",
imageLarge = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/mercuryo1024.png",
termsOfUseLink = null,
privacyPolicyLink = null,
),
paymentMethods = listOf(),
)
private val providerListUM = PaymentProviderUM(
paymentMethod = paymentMethod,
providers = persistentListOf(
ProviderListItemUM.Available(
providerId = "mercuryo",
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/mercuryo1024.png",
name = "Mercuryo",
ProviderListItemUM.Available.Content(
providerId = provider.id,
imageUrl = provider.info.imageLarge,
name = provider.info.name,
rate = "1 BTC",
isSelected = true,
isBestRate = true,
diffRate = TextReference.EMPTY,
providerResult = SelectProviderResult.ProviderWithQuote(
paymentMethod = paymentMethod,
provider = provider,
fromAmount = OnrampAmount(symbol = "$", value = BigDecimal.ONE, decimals = 2),
toAmount = OnrampAmount(symbol = "BTC", value = BigDecimal.ONE, decimals = 18),
),
onClick = {},
),
ProviderListItemUM.Available(
providerId = "mercuryo",
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/mercuryo1024.png",
name = "Mercuryo",
ProviderListItemUM.Available.Content(
providerId = provider.id,
imageUrl = provider.info.imageLarge,
name = provider.info.name,
rate = "1 BTC",
isSelected = false,
isBestRate = false,
diffRate = stringReference("-0.03%"),
providerResult = SelectProviderResult.ProviderWithQuote(
paymentMethod = paymentMethod,
provider = provider,
fromAmount = OnrampAmount(symbol = "$", value = BigDecimal.ONE, decimals = 2),
toAmount = OnrampAmount(symbol = "BTC", value = BigDecimal.ONE, decimals = 18),
),
onClick = {},
),
ProviderListItemUM.Unavailable(
providerId = "mercuryo",
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/mercuryo1024.png",
name = "Mercuryo",
providerId = provider.id,
imageUrl = provider.info.imageLarge,
name = provider.info.name,
subtitle = stringReference("Avaiable from 0,00413 BTC"),
),
),
)
val state = SelectPaymentAndProviderUM(
paymentMethods = paymentMethods,
paymentMethods = persistentListOf(providerListUM),
selectedPaymentMethod = providerListUM,
selectedProviderId = provider.id,
isPaymentMethodClickEnabled = false,
onPaymentMethodClick = {},
)

View file

@ -124,16 +124,17 @@ private fun PaymentMethodBlock(
@Composable
private fun ProviderItem(state: ProviderListItemUM, modifier: Modifier = Modifier) {
when (state) {
is ProviderListItemUM.Available -> AvailableProviderItem(
is ProviderListItemUM.Available.Content -> AvailableProviderItem(
modifier = modifier,
state = state,
)
is ProviderListItemUM.AvailableWithError -> {
is ProviderListItemUM.Available.WithError -> {
UnavailableProviderItem(
modifier = modifier.clickable(onClick = state.onClick),
imageUrl = state.imageUrl,
providerName = state.name,
subtitle = state.subtitle,
isSelected = state.isSelected,
)
}
is ProviderListItemUM.Unavailable -> UnavailableProviderItem(
@ -141,12 +142,13 @@ private fun ProviderItem(state: ProviderListItemUM, modifier: Modifier = Modifie
imageUrl = state.imageUrl,
providerName = state.name,
subtitle = state.subtitle,
isSelected = false,
)
}
}
@Composable
private fun AvailableProviderItem(state: ProviderListItemUM.Available, modifier: Modifier = Modifier) {
private fun AvailableProviderItem(state: ProviderListItemUM.Available.Content, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.then(
@ -220,10 +222,19 @@ private fun UnavailableProviderItem(
imageUrl: String,
providerName: String,
subtitle: TextReference,
isSelected: Boolean,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier.padding(all = TangemTheme.dimens.spacing12),
modifier = modifier
.then(
if (isSelected) {
Modifier.selectedBorder()
} else {
Modifier.clip(RoundedCornerShape(16.dp))
},
)
.padding(all = TangemTheme.dimens.spacing12),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {