From 6b4ddb18452728c8fad6aa6e9b0007f0d6f67a8a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 6 Dec 2024 14:41:42 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../onramp/model/OnrampProviderWithQuote.kt | 4 ++ .../GetOnrampProviderWithQuoteUseCase.kt | 4 ++ .../amount/OnrampAmountStateFactory.kt | 48 ++++++++++++---- .../main/model/OnrampMainComponentModel.kt | 5 +- .../providers/SelectProviderComponent.kt | 4 +- .../providers/entity/ProviderListItemUM.kt | 8 +++ .../providers/entity/SelectProviderResult.kt | 25 +++++++++ .../providers/model/SelectProviderModel.kt | 55 +++++++++++++++---- .../previewData/SelectProviderPreviewData.kt | 1 + .../providers/ui/SelectProviderBottomSheet.kt | 31 ++++++++--- 10 files changed, 151 insertions(+), 34 deletions(-) create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/SelectProviderResult.kt diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampProviderWithQuote.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampProviderWithQuote.kt index 15738335be..bb8efebbff 100644 --- a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampProviderWithQuote.kt +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampProviderWithQuote.kt @@ -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 diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampProviderWithQuoteUseCase.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampProviderWithQuoteUseCase.kt index 55867bd7de..0773755610 100644 --- a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampProviderWithQuoteUseCase.kt +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampProviderWithQuoteUseCase.kt @@ -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, ) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountStateFactory.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountStateFactory.kt index 5b86c98d6e..b196910fff 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountStateFactory.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/entity/factory/amount/OnrampAmountStateFactory.kt @@ -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( diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt index 95d148c94e..54eb03154f 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt @@ -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() { diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/SelectProviderComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/SelectProviderComponent.kt index fb44d2510a..deb486a9ee 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/SelectProviderComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/SelectProviderComponent.kt @@ -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, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListItemUM.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListItemUM.kt index 79ec50e05c..0107136929 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListItemUM.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListItemUM.kt @@ -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, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/SelectProviderResult.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/SelectProviderResult.kt new file mode 100644 index 0000000000..434138e8ab --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/SelectProviderResult.kt @@ -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() +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt index 30f11109d9..436a0454c1 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt @@ -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.toProvidersListItems(): ImmutableList { 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: * diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/previewData/SelectProviderPreviewData.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/previewData/SelectProviderPreviewData.kt index daf5e0ad78..492b52b826 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/previewData/SelectProviderPreviewData.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/previewData/SelectProviderPreviewData.kt @@ -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( diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt index 53aaec57c9..45ca653245 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/ui/SelectProviderBottomSheet.kt @@ -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, )