Updated on 2026-08-14
This commit is contained in:
parent
f941f6f1b9
commit
6b4ddb1845
10 changed files with 151 additions and 34 deletions
|
|
@ -23,12 +23,16 @@ sealed interface OnrampProviderWithQuote {
|
|||
|
||||
data class AvailableFrom(
|
||||
override val provider: OnrampProvider,
|
||||
val paymentMethod: OnrampPaymentMethod,
|
||||
val quoteError: OnrampQuote.Error,
|
||||
val fromAmount: OnrampAmount,
|
||||
val requiredAmount: OnrampAmount,
|
||||
) : Unavailable
|
||||
|
||||
data class AvailableUpTo(
|
||||
override val provider: OnrampProvider,
|
||||
val paymentMethod: OnrampPaymentMethod,
|
||||
val quoteError: OnrampQuote.Error,
|
||||
val fromAmount: OnrampAmount,
|
||||
val requiredAmount: OnrampAmount,
|
||||
) : Unavailable
|
||||
|
|
|
|||
|
|
@ -54,11 +54,15 @@ class GetOnrampProviderWithQuoteUseCase(private val repository: OnrampRepository
|
|||
when (amountError) {
|
||||
is OnrampQuote.Error.AmountTooBigError -> Unavailable.AvailableUpTo(
|
||||
provider = amountError.provider,
|
||||
paymentMethod = amountError.paymentMethod,
|
||||
quoteError = amountError,
|
||||
fromAmount = amountError.fromAmount,
|
||||
requiredAmount = amountError.requiredAmount,
|
||||
)
|
||||
is OnrampQuote.Error.AmountTooSmallError -> Unavailable.AvailableFrom(
|
||||
provider = amountError.provider,
|
||||
paymentMethod = amountError.paymentMethod,
|
||||
quoteError = amountError,
|
||||
fromAmount = amountError.fromAmount,
|
||||
requiredAmount = amountError.requiredAmount,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.domain.onramp.model.OnrampQuote
|
|||
import com.tangem.domain.tokens.model.AmountType
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.main.entity.*
|
||||
import com.tangem.features.onramp.providers.entity.SelectProviderResult
|
||||
import com.tangem.utils.Provider
|
||||
|
||||
internal class OnrampAmountStateFactory(
|
||||
|
|
@ -91,30 +92,47 @@ internal class OnrampAmountStateFactory(
|
|||
}
|
||||
|
||||
fun getAmountSecondaryUpdatedState(
|
||||
quoteWithProvider: OnrampProviderWithQuote.Data,
|
||||
providerResult: SelectProviderResult,
|
||||
isBestRate: Boolean,
|
||||
): OnrampMainComponentUM {
|
||||
val currentState = currentStateProvider()
|
||||
if (currentState !is OnrampMainComponentUM.Content) return currentState
|
||||
|
||||
val amountState = currentState.amountBlockState
|
||||
val amount = quoteWithProvider.toAmount.value.format {
|
||||
crypto(symbol = quoteWithProvider.toAmount.symbol, decimals = quoteWithProvider.toAmount.decimals)
|
||||
val secondaryField = when (providerResult) {
|
||||
is SelectProviderResult.ProviderWithError -> {
|
||||
providerResult.quoteError.toSecondaryFieldUiModel(amountState)
|
||||
}
|
||||
is SelectProviderResult.ProviderWithQuote -> {
|
||||
val amount = providerResult.toAmount.value.format {
|
||||
crypto(symbol = providerResult.toAmount.symbol, decimals = providerResult.toAmount.decimals)
|
||||
}
|
||||
OnrampAmountSecondaryFieldUM.Content(stringReference(amount))
|
||||
}
|
||||
}
|
||||
return currentState.copy(
|
||||
amountBlockState = amountState.copy(
|
||||
secondaryFieldModel = OnrampAmountSecondaryFieldUM.Content(stringReference(amount)),
|
||||
),
|
||||
amountBlockState = amountState.copy(secondaryFieldModel = secondaryField),
|
||||
providerBlockState = OnrampProviderBlockUM.Content(
|
||||
paymentMethod = quoteWithProvider.paymentMethod,
|
||||
providerId = quoteWithProvider.provider.id,
|
||||
providerName = quoteWithProvider.provider.info.name,
|
||||
paymentMethod = providerResult.paymentMethod,
|
||||
providerId = providerResult.provider.id,
|
||||
providerName = providerResult.provider.info.name,
|
||||
isBestRate = isBestRate,
|
||||
onClick = onrampIntents::openProviders,
|
||||
),
|
||||
buyButtonConfig = currentState.buyButtonConfig.copy(
|
||||
enabled = true,
|
||||
onClick = { onrampIntents.onBuyClick(quoteWithProvider) },
|
||||
enabled = providerResult is SelectProviderResult.ProviderWithQuote,
|
||||
onClick = {
|
||||
if (providerResult is SelectProviderResult.ProviderWithQuote) {
|
||||
onrampIntents.onBuyClick(
|
||||
OnrampProviderWithQuote.Data(
|
||||
provider = providerResult.provider,
|
||||
paymentMethod = providerResult.paymentMethod,
|
||||
toAmount = providerResult.toAmount,
|
||||
fromAmount = providerResult.fromAmount,
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -137,6 +155,14 @@ internal class OnrampAmountStateFactory(
|
|||
}
|
||||
OnrampAmountSecondaryFieldUM.Content(stringReference(amount))
|
||||
}
|
||||
is OnrampQuote.Error -> this.toSecondaryFieldUiModel(amountState)
|
||||
}
|
||||
}
|
||||
|
||||
private fun OnrampQuote.Error.toSecondaryFieldUiModel(
|
||||
amountState: OnrampAmountBlockUM,
|
||||
): OnrampAmountSecondaryFieldUM.Error {
|
||||
return when (this) {
|
||||
is OnrampQuote.Error.AmountTooBigError -> {
|
||||
val amount = requiredAmount.value.format {
|
||||
fiat(
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import com.tangem.features.onramp.main.entity.OnrampMainComponentUM
|
|||
import com.tangem.features.onramp.main.entity.OnrampProviderBlockUM
|
||||
import com.tangem.features.onramp.main.entity.factory.OnrampStateFactory
|
||||
import com.tangem.features.onramp.main.entity.factory.amount.OnrampAmountStateFactory
|
||||
import com.tangem.features.onramp.providers.entity.SelectProviderResult
|
||||
import com.tangem.features.onramp.utils.InputManager
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -104,8 +105,8 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
subscribeToQuotesUpdate()
|
||||
}
|
||||
|
||||
fun onProviderSelected(providerWithQuote: OnrampProviderWithQuote.Data, isBestRate: Boolean) {
|
||||
_state.update { amountStateFactory.getAmountSecondaryUpdatedState(providerWithQuote, isBestRate) }
|
||||
fun onProviderSelected(result: SelectProviderResult, isBestRate: Boolean) {
|
||||
_state.update { amountStateFactory.getAmountSecondaryUpdatedState(result, isBestRate) }
|
||||
}
|
||||
|
||||
private fun checkResidenceCountry() {
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ package com.tangem.features.onramp.providers
|
|||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.domain.onramp.model.OnrampPaymentMethod
|
||||
import com.tangem.domain.onramp.model.OnrampProviderWithQuote
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.features.onramp.providers.entity.SelectProviderResult
|
||||
|
||||
internal interface SelectProviderComponent : ComposableBottomSheetComponent {
|
||||
|
||||
data class Params(
|
||||
val onProviderClick: (OnrampProviderWithQuote.Data, Boolean) -> Unit,
|
||||
val onProviderClick: (result: SelectProviderResult, isBestRate: Boolean) -> Unit,
|
||||
val onDismiss: () -> Unit,
|
||||
val selectedPaymentMethod: OnrampPaymentMethod,
|
||||
val selectedProviderId: String,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,14 @@ internal sealed interface ProviderListItemUM {
|
|||
val onClick: () -> Unit,
|
||||
) : ProviderListItemUM
|
||||
|
||||
data class AvailableWithError(
|
||||
override val providerId: String,
|
||||
val imageUrl: String,
|
||||
val name: String,
|
||||
val subtitle: TextReference,
|
||||
val onClick: () -> Unit,
|
||||
) : ProviderListItemUM
|
||||
|
||||
data class Unavailable(
|
||||
override val providerId: String,
|
||||
val imageUrl: String,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.features.onramp.providers.entity
|
||||
|
||||
import com.tangem.domain.onramp.model.OnrampAmount
|
||||
import com.tangem.domain.onramp.model.OnrampPaymentMethod
|
||||
import com.tangem.domain.onramp.model.OnrampProvider
|
||||
import com.tangem.domain.onramp.model.OnrampQuote
|
||||
|
||||
sealed class SelectProviderResult {
|
||||
|
||||
abstract val paymentMethod: OnrampPaymentMethod
|
||||
abstract val provider: OnrampProvider
|
||||
|
||||
data class ProviderWithQuote(
|
||||
override val paymentMethod: OnrampPaymentMethod,
|
||||
override val provider: OnrampProvider,
|
||||
val fromAmount: OnrampAmount,
|
||||
val toAmount: OnrampAmount,
|
||||
) : SelectProviderResult()
|
||||
|
||||
data class ProviderWithError(
|
||||
override val paymentMethod: OnrampPaymentMethod,
|
||||
override val provider: OnrampProvider,
|
||||
val quoteError: OnrampQuote.Error,
|
||||
) : SelectProviderResult()
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ import com.tangem.features.onramp.providers.entity.ProviderListItemUM
|
|||
import com.tangem.features.onramp.providers.entity.ProviderListPaymentMethodUM
|
||||
import com.tangem.features.onramp.providers.entity.ProviderListUM
|
||||
import com.tangem.utils.StringsSigns.MINUS
|
||||
import com.tangem.features.onramp.providers.entity.*
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -133,6 +134,7 @@ internal class SelectProviderModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun List<OnrampProviderWithQuote>.toProvidersListItems(): ImmutableList<ProviderListItemUM> {
|
||||
val sorted = sortByRate()
|
||||
|
||||
|
|
@ -157,14 +159,15 @@ internal class SelectProviderModel @Inject constructor(
|
|||
isBestRate = quote == bestProvider,
|
||||
diffRate = stringReference("$MINUS${rateDiff.format { percent() }}"),
|
||||
onClick = {
|
||||
analyticsEventHandler.send(
|
||||
OnrampAnalyticsEvent.OnProviderChosen(
|
||||
providerName = quote.provider.info.name,
|
||||
tokenSymbol = params.cryptoCurrency.symbol,
|
||||
onProviderSelected(
|
||||
result = SelectProviderResult.ProviderWithQuote(
|
||||
paymentMethod = quote.paymentMethod,
|
||||
provider = quote.provider,
|
||||
fromAmount = quote.fromAmount,
|
||||
toAmount = quote.toAmount,
|
||||
),
|
||||
isBestRate = bestProvider == quote,
|
||||
)
|
||||
params.onProviderClick(quote, bestProvider == quote)
|
||||
params.onDismiss()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -172,22 +175,42 @@ internal class SelectProviderModel @Inject constructor(
|
|||
val amount = quote.requiredAmount.value.format {
|
||||
crypto(symbol = quote.requiredAmount.symbol, decimals = quote.requiredAmount.decimals)
|
||||
}
|
||||
ProviderListItemUM.Unavailable(
|
||||
ProviderListItemUM.AvailableWithError(
|
||||
providerId = quote.provider.id,
|
||||
imageUrl = quote.provider.info.imageLarge,
|
||||
name = quote.provider.info.name,
|
||||
subtitle = resourceReference(R.string.express_provider_min_amount, wrappedList(amount)),
|
||||
onClick = {
|
||||
onProviderSelected(
|
||||
result = SelectProviderResult.ProviderWithError(
|
||||
paymentMethod = quote.paymentMethod,
|
||||
provider = quote.provider,
|
||||
quoteError = quote.quoteError,
|
||||
),
|
||||
isBestRate = bestProvider == quote,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
is OnrampProviderWithQuote.Unavailable.AvailableUpTo -> {
|
||||
val amount = quote.requiredAmount.value.format {
|
||||
crypto(symbol = quote.requiredAmount.symbol, decimals = quote.requiredAmount.decimals)
|
||||
}
|
||||
ProviderListItemUM.Unavailable(
|
||||
ProviderListItemUM.AvailableWithError(
|
||||
providerId = quote.provider.id,
|
||||
imageUrl = quote.provider.info.imageLarge,
|
||||
name = quote.provider.info.name,
|
||||
subtitle = resourceReference(R.string.express_provider_max_amount, wrappedList(amount)),
|
||||
onClick = {
|
||||
onProviderSelected(
|
||||
result = SelectProviderResult.ProviderWithError(
|
||||
paymentMethod = quote.paymentMethod,
|
||||
provider = quote.provider,
|
||||
quoteError = quote.quoteError,
|
||||
),
|
||||
isBestRate = bestProvider == quote,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
is OnrampProviderWithQuote.Unavailable.NotSupportedPaymentMethod -> {
|
||||
|
|
@ -195,8 +218,9 @@ internal class SelectProviderModel @Inject constructor(
|
|||
providerId = quote.provider.id,
|
||||
imageUrl = quote.provider.info.imageLarge,
|
||||
name = quote.provider.info.name,
|
||||
subtitle = stringReference(
|
||||
"Available with ${quote.availablePaymentMethods.joinToString { it.name }}",
|
||||
subtitle = resourceReference(
|
||||
id = R.string.onramp_avaiable_with_payment_methods,
|
||||
formatArgs = wrappedList(quote.availablePaymentMethods.joinToString { it.name }),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -204,6 +228,17 @@ internal class SelectProviderModel @Inject constructor(
|
|||
}.toImmutableList()
|
||||
}
|
||||
|
||||
private fun onProviderSelected(result: SelectProviderResult, isBestRate: Boolean) {
|
||||
analyticsEventHandler.send(
|
||||
OnrampAnalyticsEvent.OnProviderChosen(
|
||||
providerName = result.provider.info.name,
|
||||
tokenSymbol = params.cryptoCurrency.symbol,
|
||||
),
|
||||
)
|
||||
params.onProviderClick(result, isBestRate)
|
||||
params.onDismiss()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorting providers by rule:
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ internal object SelectProviderPreviewData {
|
|||
name = "Card",
|
||||
imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png",
|
||||
onClick = {},
|
||||
enabled = false,
|
||||
),
|
||||
providers = persistentListOf(
|
||||
ProviderListItemUM.Available(
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
package com.tangem.features.onramp.providers.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -30,6 +27,7 @@ import com.tangem.core.ui.components.RectangleShimmer
|
|||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -119,9 +117,19 @@ private fun ProviderItem(state: ProviderListItemUM, modifier: Modifier = Modifie
|
|||
modifier = modifier,
|
||||
state = state,
|
||||
)
|
||||
is ProviderListItemUM.AvailableWithError -> {
|
||||
UnavailableProviderItem(
|
||||
modifier = modifier.clickable(onClick = state.onClick),
|
||||
imageUrl = state.imageUrl,
|
||||
providerName = state.name,
|
||||
subtitle = state.subtitle,
|
||||
)
|
||||
}
|
||||
is ProviderListItemUM.Unavailable -> UnavailableProviderItem(
|
||||
modifier = modifier,
|
||||
state = state,
|
||||
imageUrl = state.imageUrl,
|
||||
providerName = state.name,
|
||||
subtitle = state.subtitle,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -194,7 +202,12 @@ private fun AvailableProviderItem(state: ProviderListItemUM.Available, modifier:
|
|||
private const val UNAVAILABLE_ALPHA = 0.4F
|
||||
|
||||
@Composable
|
||||
private fun UnavailableProviderItem(state: ProviderListItemUM.Unavailable, modifier: Modifier = Modifier) {
|
||||
private fun UnavailableProviderItem(
|
||||
imageUrl: String,
|
||||
providerName: String,
|
||||
subtitle: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier.padding(all = TangemTheme.dimens.spacing12),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
@ -206,7 +219,7 @@ private fun UnavailableProviderItem(state: ProviderListItemUM.Unavailable, modif
|
|||
.clip(TangemTheme.shapes.roundedCorners8)
|
||||
.alpha(UNAVAILABLE_ALPHA),
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(state.imageUrl)
|
||||
.data(imageUrl)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(false)
|
||||
.build(),
|
||||
|
|
@ -215,12 +228,12 @@ private fun UnavailableProviderItem(state: ProviderListItemUM.Unavailable, modif
|
|||
)
|
||||
Column(modifier = Modifier.weight(1F)) {
|
||||
Text(
|
||||
text = state.name,
|
||||
text = providerName,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
Text(
|
||||
text = state.subtitle.resolveReference(),
|
||||
text = subtitle.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue