diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt index 85dbdb56f6..25a92979ef 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/DefaultOnrampRepository.kt @@ -468,6 +468,7 @@ internal class DefaultOnrampRepository( OnrampQuote.Error( paymentMethod = paymentMethod, provider = provider, + fromAmount = fromOnrampAmount, error = onrampError, ) } diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt index c511e04b8a..c36638db32 100644 --- a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampQuote.kt @@ -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() } \ No newline at end of file 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 0e6727be5b..f3c9d5341b 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 @@ -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 } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListUM.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListUM.kt index 28350458da..b4f1435144 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListUM.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/entity/ProviderListUM.kt @@ -4,13 +4,14 @@ import com.tangem.domain.onramp.model.OnrampPaymentMethod import kotlinx.collections.immutable.ImmutableList internal data class SelectPaymentAndProviderUM( - val paymentMethods: ImmutableList, - val selectedPaymentMethod: SelectProviderUM, + val paymentMethods: ImmutableList, + 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, ) \ 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 1c94f39113..e85759d361 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 @@ -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().toImmutableList(), + ), + ), isPaymentMethodClickEnabled = false, onPaymentMethodClick = ::openPaymentMethods, - selectedPaymentMethod = SelectProviderUM( + selectedPaymentMethod = PaymentProviderUM( paymentMethod = params.selectedPaymentMethod, providers = emptyList().toImmutableList(), ), + selectedProviderId = params.selectedProviderId, ) } @@ -131,16 +149,18 @@ internal class SelectProviderModel @Inject constructor( ) } - private fun ImmutableList.toPaymentUMList(): ImmutableList = map { method -> + private fun ImmutableList.toPaymentUMList(): ImmutableList = 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) { 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 4110250555..f4a23e8734 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 @@ -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 = {}, ) 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 9918d1f05e..071e09b4b6 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 @@ -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), ) {