Updated on 2026-08-14

This commit is contained in:
Tangem 2024-12-17 11:10:17 +05:00
parent c48ddbff25
commit cf53970ba8
12 changed files with 54 additions and 29 deletions

View file

@ -12,7 +12,7 @@ data class OnrampCurrencyDTO(
val code: String,
@Json(name = "image")
val image: String,
val image: String?,
@Json(name = "precision")
val precision: Int,

View file

@ -465,7 +465,7 @@ internal class DefaultOnrampRepository(
) = if (error is ApiResponseError.HttpException) {
val onrampError = onrampErrorConverter.convert(value = error.errorBody.orEmpty())
if (onrampError is OnrampError.AmountError) {
OnrampQuote.Error(
OnrampQuote.AmountError(
paymentMethod = paymentMethod,
provider = provider,
fromAmount = fromOnrampAmount,
@ -473,7 +473,10 @@ internal class DefaultOnrampRepository(
)
} else {
Timber.w(error, "Unable to fetch onramp quotes for ${provider.id}. $error")
null
OnrampQuote.Error(
paymentMethod = paymentMethod,
provider = provider,
)
}
} else {
Timber.w(error, "Unable to fetch onramp quotes for ${provider.id}. $error")

View file

@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable
data class OnrampCurrency(
val name: String,
val code: String,
val image: String,
val image: String?,
val precision: Int,
val unit: String,
)

View file

@ -23,7 +23,7 @@ sealed interface OnrampProviderWithQuote {
data class Error(
override val provider: OnrampProvider,
val quoteError: OnrampQuote.Error,
val quoteError: OnrampQuote.AmountError,
) : Unavailable
}
}

View file

@ -16,10 +16,15 @@ sealed class OnrampQuote {
val maxFromAmount: OnrampAmount,
) : OnrampQuote()
data class Error(
data class AmountError(
override val paymentMethod: OnrampPaymentMethod,
override val provider: OnrampProvider,
val fromAmount: OnrampAmount,
val error: OnrampError.AmountError,
) : OnrampQuote()
data class Error(
override val paymentMethod: OnrampPaymentMethod,
override val provider: OnrampProvider,
) : OnrampQuote()
}

View file

@ -31,8 +31,8 @@ class GetOnrampProviderWithQuoteUseCase(
private fun List<OnrampQuote>.quoteWithProvider(
provider: OnrampProvider,
selectedPaymentMethod: OnrampPaymentMethod,
): OnrampProviderWithQuote {
val matchedQuote = firstOrNull { it.paymentMethod == selectedPaymentMethod }
): OnrampProviderWithQuote? {
val matchedQuote = firstOrNull { it.paymentMethod.id == selectedPaymentMethod.id }
return when (matchedQuote) {
is OnrampQuote.Data -> OnrampProviderWithQuote.Data(
provider = matchedQuote.provider,
@ -40,14 +40,23 @@ class GetOnrampProviderWithQuoteUseCase(
toAmount = matchedQuote.toAmount,
fromAmount = matchedQuote.fromAmount,
)
is OnrampQuote.Error -> Unavailable.Error(
is OnrampQuote.AmountError -> Unavailable.Error(
provider = matchedQuote.provider,
quoteError = matchedQuote,
)
null -> Unavailable.NotSupportedPaymentMethod(
provider = provider,
availablePaymentMethods = provider.paymentMethods,
)
is OnrampQuote.Error -> null
null -> {
val availablePaymentMethods = getAvailablePaymentMethods(provider)
Unavailable.NotSupportedPaymentMethod(
provider = provider,
availablePaymentMethods = availablePaymentMethods,
).takeIf { availablePaymentMethods.isNotEmpty() }
}
}
}
private fun List<OnrampQuote>.getAvailablePaymentMethods(provider: OnrampProvider) = provider.paymentMethods
.filter { pm ->
filter { it !is OnrampQuote.Error }.any { it.paymentMethod.id == pm.id }
}
}

View file

@ -47,9 +47,9 @@ class GetOnrampQuotesUseCase(
grouped.value.sortedByDescending {
when (it) {
is OnrampQuote.Data -> it.toAmount.value
is OnrampQuote.Error -> null
// negative difference to sort both when data and unavailable is present
is OnrampQuote.Error -> {
is OnrampQuote.AmountError -> {
when (val error = it.error) {
is OnrampError.AmountError.TooSmallError -> it.fromAmount.value - error.requiredAmount
is OnrampError.AmountError.TooBigError -> error.requiredAmount - it.fromAmount.value

View file

@ -10,7 +10,12 @@ internal data class OnrampAmountBlockUM(
val secondaryFieldModel: OnrampAmountSecondaryFieldUM,
)
internal data class OnrampCurrencyUM(val code: String, val iconUrl: String, val precision: Int, val onClick: () -> Unit)
internal data class OnrampCurrencyUM(
val code: String,
val iconUrl: String?,
val precision: Int,
val onClick: () -> Unit,
)
@Immutable
internal sealed interface OnrampAmountSecondaryFieldUM {

View file

@ -75,7 +75,7 @@ internal class OnrampAmountStateFactory(
return currentState.copy(
amountBlockState = amountState.copy(
secondaryFieldModel = quote.toSecondaryFieldUiModel(amountState),
secondaryFieldModel = quote.toSecondaryFieldUiModel(amountState) ?: amountState.secondaryFieldModel,
),
providerBlockState = quote.toProviderBlockState(isBestRate),
buyButtonConfig = currentState.buyButtonConfig.copy(
@ -175,19 +175,20 @@ internal class OnrampAmountStateFactory(
)
}
private fun OnrampQuote.toSecondaryFieldUiModel(amountState: OnrampAmountBlockUM): OnrampAmountSecondaryFieldUM {
private fun OnrampQuote.toSecondaryFieldUiModel(amountState: OnrampAmountBlockUM): OnrampAmountSecondaryFieldUM? {
return when (this) {
is OnrampQuote.Error -> null
is OnrampQuote.Data -> {
val amount = toAmount.value.format {
crypto(symbol = toAmount.symbol, decimals = toAmount.decimals)
}
OnrampAmountSecondaryFieldUM.Content(stringReference(amount))
}
is OnrampQuote.Error -> this.toSecondaryFieldUiModel(amountState)
is OnrampQuote.AmountError -> this.toSecondaryFieldUiModel(amountState)
}
}
private fun OnrampQuote.Error.toSecondaryFieldUiModel(
private fun OnrampQuote.AmountError.toSecondaryFieldUiModel(
amountState: OnrampAmountBlockUM,
): OnrampAmountSecondaryFieldUM.Error {
val amount = error.requiredAmount.format {

View file

@ -196,6 +196,8 @@ internal class OnrampMainComponentModel @Inject constructor(
private fun subscribeToQuotesUpdate() {
getOnrampQuotesUseCase.invoke()
.distinctUntilChanged()
.conflate()
.onEach { maybeQuotes ->
maybeQuotes.fold(
ifLeft = ::handleOnrampError,
@ -269,7 +271,7 @@ internal class OnrampMainComponentModel @Inject constructor(
}
private fun handleQuoteResult(quotes: List<OnrampQuote>) {
quotes.filterIsInstance<OnrampQuote.Error>().forEach { errorState ->
quotes.filterIsInstance<OnrampQuote.AmountError>().forEach { errorState ->
sendOnrampErrorAnalytic(errorState.error)
}
@ -299,12 +301,12 @@ internal class OnrampMainComponentModel @Inject constructor(
paymentMethod = quote.paymentMethod.name,
),
)
}
_state.update {
amountStateFactory.getAmountSecondaryUpdatedState(
quote = quote,
isBestRate = isBestProvider,
)
_state.update {
amountStateFactory.getAmountSecondaryUpdatedState(
quote = quote,
isBestRate = isBestProvider,
)
}
}
}

View file

@ -20,6 +20,6 @@ sealed class SelectProviderResult {
data class ProviderWithError(
override val paymentMethod: OnrampPaymentMethod,
override val provider: OnrampProvider,
val quoteError: OnrampQuote.Error,
val quoteError: OnrampQuote.AmountError,
) : SelectProviderResult()
}

View file

@ -11,7 +11,7 @@ sealed class OnrampSuccessComponentUM {
data class Content(
val txId: String,
val timestamp: Long,
val currencyImageUrl: String,
val currencyImageUrl: String?,
val fromAmount: TextReference,
val toAmount: TextReference,
val statusBlock: ExpressStatusUM,