From 96ce2fc718e3bbeab28d59f11ff31b8ce1b6699a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 5 Sep 2025 14:58:00 +0200 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/OnrampDomainModule.kt | 34 +- core/res/src/main/res/values/strings.xml | 5 +- .../src/main/res/drawable/ic_best_rate_16.xml | 9 + .../src/main/res/drawable/ic_fastest_16.xml | 10 + .../tangem/domain/onramp/model/OnrampOffer.kt | 5 +- .../onramp/GetOnrampAllOffersUseCase.kt | 5 +- .../domain/onramp/GetOnrampOffersUseCase.kt | 46 ++- .../domain/onramp/utils/OnrampOfferUtils.kt | 8 + .../onramp/alloffers/AllOffersComponent.kt | 17 + .../alloffers/DefaultAllOffersComponent.kt | 31 ++ .../di/AllOffersComponentModelModule.kt | 20 + .../alloffers/di/AllOffersComponentModule.kt | 18 + .../onramp/alloffers/model/AllOffersModel.kt | 19 + .../mainv2/DefaultOnrampV2MainComponent.kt | 10 + .../DefaultOnrampV2MainFeatureToggle.kt | 2 +- .../mainv2/entity/OnrampOfferBlockUM.kt | 23 +- .../entity/OnrampV2MainBottomSheetConfig.kt | 3 + .../factory/OnrampOffersStateFactory.kt | 106 ++++++ .../model/OnrampV2MainComponentModel.kt | 38 +- .../onramp/mainv2/ui/OnrampOffersContent.kt | 357 +++++++++++++++++- .../onramp/mainv2/ui/OnrampV2AmountContent.kt | 13 +- .../onramp/root/DefaultOnrampComponent.kt | 62 ++- 22 files changed, 784 insertions(+), 57 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_best_rate_16.xml create mode 100644 core/ui/src/main/res/drawable/ic_fastest_16.xml create mode 100644 domain/onramp/src/main/java/com/tangem/domain/onramp/utils/OnrampOfferUtils.kt create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/AllOffersComponent.kt create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/DefaultAllOffersComponent.kt create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/di/AllOffersComponentModelModule.kt create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/di/AllOffersComponentModule.kt create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/model/AllOffersModel.kt create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/factory/OnrampOffersStateFactory.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt index f5c4fda538..18f3540843 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/OnrampDomainModule.kt @@ -1,11 +1,7 @@ package com.tangem.tap.di.domain -import com.tangem.domain.onramp.repositories.LegacyTopUpRepository import com.tangem.domain.onramp.* -import com.tangem.domain.onramp.repositories.HotCryptoRepository -import com.tangem.domain.onramp.repositories.OnrampErrorResolver -import com.tangem.domain.onramp.repositories.OnrampRepository -import com.tangem.domain.onramp.repositories.OnrampTransactionRepository +import com.tangem.domain.onramp.repositories.* import com.tangem.domain.settings.repositories.SettingsRepository import dagger.Module import dagger.Provides @@ -237,4 +233,32 @@ internal object OnrampDomainModule { fun provideGetLegacyTopUpUrlUseCase(legacyTopUpRepository: LegacyTopUpRepository): GetLegacyTopUpUrlUseCase { return GetLegacyTopUpUrlUseCase(legacyTopUpRepository) } + + @Provides + @Singleton + fun provideGetOnrampAllOffersUseCase( + onrampRepository: OnrampRepository, + onrampErrorResolver: OnrampErrorResolver, + settingsRepository: SettingsRepository, + ): GetOnrampAllOffersUseCase { + return GetOnrampAllOffersUseCase( + onrampRepository = onrampRepository, + errorResolver = onrampErrorResolver, + settingsRepository = settingsRepository, + ) + } + + @Provides + @Singleton + fun provideGetOnrampOffersUseCase( + onrampRepository: OnrampRepository, + onrampErrorResolver: OnrampErrorResolver, + onrampTransactionRepository: OnrampTransactionRepository, + ): GetOnrampOffersUseCase { + return GetOnrampOffersUseCase( + onrampRepository = onrampRepository, + errorResolver = onrampErrorResolver, + onrampTransactionRepository = onrampTransactionRepository, + ) + } } \ No newline at end of file diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 7d747732ea..b475fe441f 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -876,7 +876,10 @@ Payment method Available up to %s Available from %s - %d providers + + %d provider + %d providers + Providers Recently used Recommended diff --git a/core/ui/src/main/res/drawable/ic_best_rate_16.xml b/core/ui/src/main/res/drawable/ic_best_rate_16.xml new file mode 100644 index 0000000000..7e36b66936 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_best_rate_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_fastest_16.xml b/core/ui/src/main/res/drawable/ic_fastest_16.xml new file mode 100644 index 0000000000..072444ff1d --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_fastest_16.xml @@ -0,0 +1,10 @@ + + + diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampOffer.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampOffer.kt index e116e55735..56700b01f4 100644 --- a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampOffer.kt +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampOffer.kt @@ -1,13 +1,16 @@ package com.tangem.domain.onramp.model +import java.math.BigDecimal + data class OnrampOffersBlock( val category: OnrampOfferCategory, val offers: List, - val isVisible: Boolean = offers.isNotEmpty(), + val hasMoreOffers: Boolean, ) data class OnrampOffer( val quote: OnrampQuote, + val rateDif: BigDecimal?, val advantages: OnrampOfferAdvantages = OnrampOfferAdvantages.Default, ) diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampAllOffersUseCase.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampAllOffersUseCase.kt index 6cc1247867..f2f787e182 100644 --- a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampAllOffersUseCase.kt +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampAllOffersUseCase.kt @@ -12,6 +12,7 @@ import com.tangem.domain.onramp.model.OnrampQuote import com.tangem.domain.onramp.model.error.OnrampError import com.tangem.domain.onramp.repositories.OnrampErrorResolver import com.tangem.domain.onramp.repositories.OnrampRepository +import com.tangem.domain.onramp.utils.calculateRateDif import com.tangem.domain.settings.repositories.SettingsRepository import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.map @@ -38,6 +39,7 @@ class GetOnrampAllOffersUseCase( val isGooglePayAvailable = settingsRepository.isGooglePayAvailability() val overallBestRateQuote = validQuotes.maxByOrNull { it.toAmount.value } + val bestRate = overallBestRateQuote?.toAmount?.value val offersByPaymentMethod = validQuotes.groupBy { it.paymentMethod } @@ -48,7 +50,8 @@ class GetOnrampAllOffersUseCase( } else { OnrampOfferAdvantages.Default } - OnrampOffer(quote = quote, advantages = advantages) + val rateDif = calculateRateDif(quote.toAmount.value, bestRate) + OnrampOffer(quote = quote, rateDif = rateDif, advantages = advantages) } val groupBestRateOfferData = methodQuotes.maxByOrNull { it.toAmount.value } diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampOffersUseCase.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampOffersUseCase.kt index 29041336a9..1632abd3cc 100644 --- a/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampOffersUseCase.kt +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/GetOnrampOffersUseCase.kt @@ -11,6 +11,7 @@ import com.tangem.domain.onramp.model.error.OnrampError import com.tangem.domain.onramp.repositories.OnrampErrorResolver import com.tangem.domain.onramp.repositories.OnrampRepository import com.tangem.domain.onramp.repositories.OnrampTransactionRepository +import com.tangem.domain.onramp.utils.calculateRateDif import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map @@ -43,8 +44,12 @@ class GetOnrampOffersUseCase( val validQuotes = quotes.filterIsInstance() if (validQuotes.isEmpty()) return emptyList() + val bestRateQuote = validQuotes.maxByOrNull { it.toAmount.value } + val bestRate = bestRateQuote?.toAmount?.value + val offers = validQuotes.map { quote -> - OnrampOffer(quote = quote) + val rateDif = calculateRateDif(quote.toAmount.value, bestRate) + OnrampOffer(quote = quote, rateDif = rateDif) } val recentOffer = findRecentOffer(offers, transactions) @@ -107,6 +112,15 @@ class GetOnrampOffersUseCase( fastestOffer: OnrampOffer?, allOffers: List, ): List { + val recommendedOffers = buildRecommendedOffers( + recentOffer = recentOffer, + bestRateOffer = bestRateOffer, + fastestOffer = fastestOffer, + ) + + val shownOffersCount = (if (recentOffer != null) 1 else 0) + recommendedOffers.size + val hasMoreOffers = allOffers.size > shownOffersCount + return buildList { if (recentOffer != null) { add( @@ -119,23 +133,20 @@ class GetOnrampOffersUseCase( bestRateOffer, fastestOffer, ), + rateDif = if (bestRateOffer != null) recentOffer.rateDif else null, ), ), + hasMoreOffers = false, ), ) } - val recommendedOffers = buildRecommendedOffers( - recentOffer = recentOffer, - bestRateOffer = bestRateOffer, - fastestOffer = fastestOffer, - ) - if (recommendedOffers.isNotEmpty() && hasOnlyOneMethodAndProvider(allOffers).not()) { add( OnrampOffersBlock( category = OnrampOfferCategory.Recommended, offers = recommendedOffers, + hasMoreOffers = hasMoreOffers, ), ) } @@ -167,17 +178,32 @@ class GetOnrampOffersUseCase( return buildList { if (isSameOffer(bestRateOffer, fastestOffer)) { bestRateOffer?.let { offer -> - add(offer.copy(advantages = OnrampOfferAdvantages.BestRate)) + add( + offer.copy( + advantages = OnrampOfferAdvantages.BestRate, + rateDif = null, + ), + ) } } else { if (bestRateOffer != null && !isSameOffer(bestRateOffer, recentOffer)) { - add(bestRateOffer.copy(advantages = OnrampOfferAdvantages.BestRate)) + add( + bestRateOffer.copy( + advantages = OnrampOfferAdvantages.BestRate, + rateDif = null, + ), + ) } if (fastestOffer != null && !isSameOffer(fastestOffer, recentOffer) && !isSameOffer(fastestOffer, bestRateOffer) ) { - add(fastestOffer.copy(advantages = OnrampOfferAdvantages.Fastest)) + add( + fastestOffer.copy( + advantages = OnrampOfferAdvantages.Fastest, + rateDif = if (bestRateOffer != null) fastestOffer.rateDif else null, + ), + ) } } } diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/utils/OnrampOfferUtils.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/utils/OnrampOfferUtils.kt new file mode 100644 index 0000000000..0b3625d494 --- /dev/null +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/utils/OnrampOfferUtils.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.onramp.utils + +import java.math.BigDecimal + +internal fun calculateRateDif(currentTokenRate: BigDecimal, bestRate: BigDecimal?): BigDecimal? { + if (bestRate == null) return null + return BigDecimal.ONE - currentTokenRate / bestRate +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/AllOffersComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/AllOffersComponent.kt new file mode 100644 index 0000000000..7c2de6da52 --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/AllOffersComponent.kt @@ -0,0 +1,17 @@ +package com.tangem.features.onramp.alloffers + +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableBottomSheetComponent +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWallet + +internal interface AllOffersComponent : ComposableBottomSheetComponent { + + data class Params( + val userWallet: UserWallet, + val cryptoCurrency: CryptoCurrency, + val onDismiss: () -> Unit, + ) + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/DefaultAllOffersComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/DefaultAllOffersComponent.kt new file mode 100644 index 0000000000..02adaaa504 --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/DefaultAllOffersComponent.kt @@ -0,0 +1,31 @@ +package com.tangem.features.onramp.alloffers + +import androidx.compose.runtime.Composable +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.features.onramp.alloffers.model.AllOffersModel +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +internal class DefaultAllOffersComponent @AssistedInject constructor( + @Assisted context: AppComponentContext, + @Assisted params: AllOffersComponent.Params, +) : AllOffersComponent, AppComponentContext by context { + + private val model: AllOffersModel = getOrCreateModel(params) + + override fun dismiss() { + model.dismiss() + } + + @Composable + override fun BottomSheet() { + // TODO to be continued in [REDACTED_TASK_KEY] + } + + @AssistedFactory + interface Factory : AllOffersComponent.Factory { + override fun create(context: AppComponentContext, params: AllOffersComponent.Params): DefaultAllOffersComponent + } +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/di/AllOffersComponentModelModule.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/di/AllOffersComponentModelModule.kt new file mode 100644 index 0000000000..e709ab73d6 --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/di/AllOffersComponentModelModule.kt @@ -0,0 +1,20 @@ +package com.tangem.features.onramp.alloffers.di + +import com.tangem.core.decompose.di.ModelComponent +import com.tangem.core.decompose.model.Model +import com.tangem.features.onramp.alloffers.model.AllOffersModel +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +@InstallIn(ModelComponent::class) +internal interface AllOffersComponentModelModule { + + @Binds + @IntoMap + @ClassKey(AllOffersModel::class) + fun bindAllOffersModel(model: AllOffersModel): Model +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/di/AllOffersComponentModule.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/di/AllOffersComponentModule.kt new file mode 100644 index 0000000000..3979f343c2 --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/di/AllOffersComponentModule.kt @@ -0,0 +1,18 @@ +package com.tangem.features.onramp.alloffers.di + +import com.tangem.features.onramp.alloffers.AllOffersComponent +import com.tangem.features.onramp.alloffers.DefaultAllOffersComponent +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal interface AllOffersComponentModule { + + @Binds + @Singleton + fun bindAllOffersComponentFactory(factory: DefaultAllOffersComponent.Factory): AllOffersComponent.Factory +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/model/AllOffersModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/model/AllOffersModel.kt new file mode 100644 index 0000000000..3280c17cb9 --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/alloffers/model/AllOffersModel.kt @@ -0,0 +1,19 @@ +package com.tangem.features.onramp.alloffers.model + +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.features.onramp.alloffers.AllOffersComponent +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import javax.inject.Inject + +internal class AllOffersModel @Inject constructor( + override val dispatchers: CoroutineDispatcherProvider, + paramsContainer: ParamsContainer, +) : Model() { + + private val params: AllOffersComponent.Params = paramsContainer.require() + + fun dismiss() { + params.onDismiss() + } +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/DefaultOnrampV2MainComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/DefaultOnrampV2MainComponent.kt index a651416d7f..2487bf37b1 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/DefaultOnrampV2MainComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/DefaultOnrampV2MainComponent.kt @@ -12,6 +12,7 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableBottomSheetComponent +import com.tangem.features.onramp.alloffers.AllOffersComponent import com.tangem.features.onramp.confirmresidency.ConfirmResidencyComponent import com.tangem.features.onramp.mainv2.entity.OnrampV2MainBottomSheetConfig import com.tangem.features.onramp.mainv2.model.OnrampV2MainComponentModel @@ -26,6 +27,7 @@ internal class DefaultOnrampV2MainComponent @AssistedInject constructor( @Assisted private val params: OnrampV2MainComponent.Params, private val confirmResidencyComponentFactory: ConfirmResidencyComponent.Factory, private val selectCurrencyComponentFactory: SelectCurrencyComponent.Factory, + private val allOffersComponentFactory: AllOffersComponent.Factory, ) : OnrampV2MainComponent, AppComponentContext by appComponentContext { private val model: OnrampV2MainComponentModel = getOrCreateModel(params) @@ -67,6 +69,14 @@ internal class DefaultOnrampV2MainComponent @AssistedInject constructor( onDismiss = model.bottomSheetNavigation::dismiss, ), ) + OnrampV2MainBottomSheetConfig.AllOffers -> allOffersComponentFactory.create( + context = childByContext(componentContext), + params = AllOffersComponent.Params( + userWallet = model.userWallet, + cryptoCurrency = params.cryptoCurrency, + onDismiss = model.bottomSheetNavigation::dismiss, + ), + ) } @AssistedFactory diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/DefaultOnrampV2MainFeatureToggle.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/DefaultOnrampV2MainFeatureToggle.kt index 256bead28d..815fa5060b 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/DefaultOnrampV2MainFeatureToggle.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/DefaultOnrampV2MainFeatureToggle.kt @@ -6,5 +6,5 @@ class DefaultOnrampV2MainFeatureToggle( private val featureTogglesManager: FeatureTogglesManager, ) : OnrampV2MainFeatureToggle { override val isOnrampNewMainEnabled: Boolean - get() = featureTogglesManager.isFeatureEnabled("NEW_ONRAMP_RECEIVE_ENABLED") + get() = featureTogglesManager.isFeatureEnabled("NEW_ONRAMP_MAIN_ENABLED") } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/OnrampOfferBlockUM.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/OnrampOfferBlockUM.kt index 583cc962bb..5e41b49346 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/OnrampOfferBlockUM.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/OnrampOfferBlockUM.kt @@ -1,6 +1,7 @@ package com.tangem.features.onramp.mainv2.entity import androidx.compose.runtime.Immutable +import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.onramp.model.OnrampPaymentMethod import kotlinx.collections.immutable.ImmutableList @@ -20,22 +21,32 @@ internal sealed interface OnrampOffersBlockUM { data class Content( override val isBlockVisible: Boolean, - val offers: ImmutableList, + val recentOffer: OnrampOfferUM?, + val recommended: ImmutableList, + val onrampAllOffersButtonConfig: OnrampAllOffersButtonConfig?, ) : OnrampOffersBlockUM } internal data class OnrampOfferUM( - val category: OnrampOfferCategory, - val advantages: OnrampOfferAdvantages, + val category: OnrampOfferCategoryUM, + val advantages: OnrampOfferAdvantagesUM, val paymentMethod: OnrampPaymentMethod, val providerId: String, val providerName: String, + val rate: String, + val diff: TextReference?, + val onBuyClicked: () -> Unit, ) -internal enum class OnrampOfferCategory { +internal enum class OnrampOfferCategoryUM { RecentlyUsed, Recommended } -internal enum class OnrampOfferAdvantages { +internal enum class OnrampOfferAdvantagesUM { Default, BestRate, Fastest -} \ No newline at end of file +} + +internal data class OnrampAllOffersButtonConfig( + val title: TextReference, + val onClick: () -> Unit, +) \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/OnrampV2MainBottomSheetConfig.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/OnrampV2MainBottomSheetConfig.kt index dcb21bbf8a..d9e6c47a2d 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/OnrampV2MainBottomSheetConfig.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/OnrampV2MainBottomSheetConfig.kt @@ -10,4 +10,7 @@ sealed interface OnrampV2MainBottomSheetConfig { @Serializable data object CurrenciesList : OnrampV2MainBottomSheetConfig + + @Serializable + data object AllOffers : OnrampV2MainBottomSheetConfig } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/factory/OnrampOffersStateFactory.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/factory/OnrampOffersStateFactory.kt new file mode 100644 index 0000000000..377c5987d4 --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/entity/factory/OnrampOffersStateFactory.kt @@ -0,0 +1,106 @@ +package com.tangem.features.onramp.mainv2.entity.factory + +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format +import com.tangem.core.ui.format.bigdecimal.percent +import com.tangem.domain.onramp.model.* +import com.tangem.features.onramp.impl.R +import com.tangem.features.onramp.main.entity.OnrampIntents +import com.tangem.features.onramp.mainv2.entity.* +import com.tangem.utils.Provider +import com.tangem.utils.StringsSigns.MINUS +import kotlinx.collections.immutable.toPersistentList + +internal class OnrampOffersStateFactory( + private val currentStateProvider: Provider, + private val onrampIntents: OnrampIntents, +) { + + fun getOnShowOffersState(offers: List): OnrampV2MainComponentUM { + val currentState = currentStateProvider.invoke() + return when (currentState) { + is OnrampV2MainComponentUM.Content -> { + currentState.copy( + offersBlockState = mapOnrampOffersBlockToUM(offers), + ) + } + is OnrampV2MainComponentUM.InitialLoading -> { + currentState + } + } + } + + private fun mapOnrampOffersBlockToUM(offersBlocks: List): OnrampOffersBlockUM.Content { + val allOffersUM = mutableListOf() + offersBlocks.map { block -> + block.offers.forEach { offer -> + when (val currentQuote = offer.quote) { + is OnrampQuote.AmountError, + is OnrampQuote.Error, + -> Unit + is OnrampQuote.Data -> { + allOffersUM.add( + OnrampOfferUM( + category = mapOfferCategoryDTOtoUM(block.category), + advantages = mapOfferAdvantagesDTOtoUM(offer.advantages), + paymentMethod = currentQuote.paymentMethod, + providerId = currentQuote.provider.id, + providerName = currentQuote.provider.info.name, + rate = currentQuote.toAmount.value.format { + crypto( + symbol = currentQuote.toAmount.symbol, + decimals = currentQuote.toAmount.decimals, + ) + }, + diff = offer.rateDif?.let { diff -> + stringReference("$MINUS${diff.format { percent() }}") + }, + onBuyClicked = { + onrampIntents.onBuyClick( + quote = OnrampProviderWithQuote.Data( + provider = offer.quote.provider, + paymentMethod = currentQuote.paymentMethod, + toAmount = currentQuote.toAmount, + fromAmount = currentQuote.fromAmount, + ), + ) + }, + ), + ) + } + } + } + } + + return OnrampOffersBlockUM.Content( + isBlockVisible = false, + recentOffer = allOffersUM.firstOrNull { it.category == OnrampOfferCategoryUM.RecentlyUsed }, + recommended = allOffersUM.filter { it.category == OnrampOfferCategoryUM.Recommended }.toPersistentList(), + onrampAllOffersButtonConfig = if (offersBlocks.any { it.hasMoreOffers }) { + OnrampAllOffersButtonConfig( + title = TextReference.Res(R.string.onramp_all_offers_button_title), + onClick = { onrampIntents.openProviders() }, + ) + } else { + null + }, + ) + } + + private fun mapOfferCategoryDTOtoUM(category: OnrampOfferCategory): OnrampOfferCategoryUM { + return when (category) { + OnrampOfferCategory.Recent -> OnrampOfferCategoryUM.RecentlyUsed + OnrampOfferCategory.Recommended -> OnrampOfferCategoryUM.Recommended + } + } + + private fun mapOfferAdvantagesDTOtoUM(advantages: OnrampOfferAdvantages): OnrampOfferAdvantagesUM { + return when (advantages) { + OnrampOfferAdvantages.Default -> OnrampOfferAdvantagesUM.Default + OnrampOfferAdvantages.BestRate -> OnrampOfferAdvantagesUM.BestRate + OnrampOfferAdvantages.Fastest -> OnrampOfferAdvantagesUM.Fastest + } + } +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/model/OnrampV2MainComponentModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/model/OnrampV2MainComponentModel.kt index 8d6615a675..d83a94d1f0 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/model/OnrampV2MainComponentModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/model/OnrampV2MainComponentModel.kt @@ -20,6 +20,7 @@ import com.tangem.features.onramp.main.entity.OnrampLastUpdate import com.tangem.features.onramp.mainv2.OnrampV2MainComponent import com.tangem.features.onramp.mainv2.entity.* import com.tangem.features.onramp.mainv2.entity.factory.OnrampAmountButtonUMStateFactory +import com.tangem.features.onramp.mainv2.entity.factory.OnrampOffersStateFactory import com.tangem.features.onramp.mainv2.entity.factory.OnrampV2AmountStateFactory import com.tangem.features.onramp.mainv2.entity.factory.OnrampV2StateFactory import com.tangem.features.onramp.utils.sendOnrampErrorEvent @@ -32,7 +33,7 @@ import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject -@Suppress("LongParameterList") +@Suppress("LongParameterList", "LargeClass") internal class OnrampV2MainComponentModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, private val analyticsEventHandler: AnalyticsEventHandler, @@ -44,6 +45,7 @@ internal class OnrampV2MainComponentModel @Inject constructor( private val getOnrampQuotesUseCase: GetOnrampQuotesUseCase, private val fetchPairsUseCase: OnrampFetchPairsUseCase, private val amountInputManager: InputManager, + private val getOnrampOffersUseCase: GetOnrampOffersUseCase, paramsContainer: ParamsContainer, getWalletsUseCase: GetWalletsUseCase, ) : Model(), OnrampIntents { @@ -58,6 +60,13 @@ internal class OnrampV2MainComponentModel @Inject constructor( OnrampAmountButtonUMStateFactory() } + private val onrampOffersStateFactory: OnrampOffersStateFactory by lazy(LazyThreadSafetyMode.NONE) { + OnrampOffersStateFactory( + currentStateProvider = Provider { _state.value }, + onrampIntents = this, + ) + } + private val stateFactory = OnrampV2StateFactory( currentStateProvider = Provider { _state.value }, cryptoCurrency = params.cryptoCurrency, @@ -94,6 +103,7 @@ internal class OnrampV2MainComponentModel @Inject constructor( subscribeToAmountChanges() subscribeToCountryAndCurrencyUpdates() subscribeToQuotesUpdate() + subscribeOnOffers() } override fun onDestroy() { @@ -117,11 +127,19 @@ internal class OnrampV2MainComponentModel @Inject constructor( } override fun onBuyClick(quote: OnrampProviderWithQuote.Data) { - // TODO in [REDACTED_TASK_KEY] to be continued + val currentContentState = state.value as? OnrampV2MainComponentUM.Content ?: return + analyticsEventHandler.send( + OnrampAnalyticsEvent.OnBuyClick( + providerName = quote.provider.info.name, + currency = currentContentState.amountBlockState.currencyUM.code, + tokenSymbol = params.cryptoCurrency.symbol, + ), + ) + params.openRedirectPage(quote) } override fun openProviders() { - // TODO in [REDACTED_TASK_KEY] to be continued + bottomSheetNavigation.activate(OnrampV2MainBottomSheetConfig.AllOffers) } override fun onRefresh() { @@ -172,6 +190,20 @@ internal class OnrampV2MainComponentModel @Inject constructor( router.pop() } + private fun subscribeOnOffers() = modelScope.launch { + getOnrampOffersUseCase.invoke( + userWalletId = userWallet.walletId, + cryptoCurrencyId = params.cryptoCurrency.id, + ).collectLatest { maybeOffers -> + maybeOffers.fold( + ifLeft = ::handleOnrampError, + ifRight = { offers -> + _state.update { onrampOffersStateFactory.getOnShowOffersState(offers) } + }, + ) + } + } + private fun subscribeToAmountChanges() = modelScope.launch { amountInputManager.query .filter(String::isNotEmpty) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/ui/OnrampOffersContent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/ui/OnrampOffersContent.kt index 6b35933d9e..1fc602096c 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/ui/OnrampOffersContent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/ui/OnrampOffersContent.kt @@ -1,13 +1,42 @@ package com.tangem.features.onramp.mainv2.ui +import android.content.res.Configuration import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.tween import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastForEach +import coil.compose.SubcomposeAsyncImage +import coil.request.ImageRequest +import com.tangem.core.ui.components.* +import com.tangem.core.ui.components.buttons.common.TangemButtonSize +import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.domain.onramp.model.OnrampPaymentMethod +import com.tangem.domain.onramp.model.PaymentMethodType +import com.tangem.features.onramp.impl.R +import com.tangem.features.onramp.mainv2.entity.OnrampOfferAdvantagesUM +import com.tangem.features.onramp.mainv2.entity.OnrampOfferCategoryUM +import com.tangem.features.onramp.mainv2.entity.OnrampOfferUM import com.tangem.features.onramp.mainv2.entity.OnrampOffersBlockUM +import kotlinx.collections.immutable.persistentListOf @Composable internal fun OnrampOffersContent(state: OnrampOffersBlockUM) { @@ -23,10 +52,330 @@ internal fun OnrampOffersContent(state: OnrampOffersBlockUM) { ), label = "Offers block animation", ) { - Text( - text = "Some offers", - style = TangemTheme.typography.head, + if (state is OnrampOffersBlockUM.Content) { + Column(modifier = Modifier.fillMaxWidth()) { + state.recentOffer?.let { recentOffer -> + Column { + Text( + modifier = Modifier.padding(start = 12.dp), + text = stringResourceSafe(R.string.onramp_recently_used_title), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + + SpacerH(8.dp) + + Offer(recentOffer) + + SpacerH(16.dp) + } + } + + Text( + modifier = Modifier.padding(start = 12.dp), + text = stringResourceSafe(R.string.onramp_recommended_title), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + + SpacerH(8.dp) + + state.recommended.fastForEach { + Offer(it) + SpacerH(8.dp) + } + + state.onrampAllOffersButtonConfig?.let { + SpacerH(12.dp) + SecondaryButton( + modifier = Modifier.fillMaxWidth(), + text = it.title.resolveReference(), + onClick = it.onClick, + ) + } + } + } + } +} + +@Composable +private fun Offer(onrampOfferUM: OnrampOfferUM, modifier: Modifier = Modifier) { + Column( + modifier = modifier + .fillMaxWidth() + .background( + color = TangemTheme.colors.background.action, + shape = RoundedCornerShape(14.dp), + ) + .padding(12.dp), + ) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + OfferHeader(advantage = onrampOfferUM.advantages) + RateBlock(rate = onrampOfferUM.rate, diff = onrampOfferUM.diff) + } + SpacerWMax() + SecondaryButton( + size = TangemButtonSize.RoundedAction, + text = stringResourceSafe(R.string.common_buy), + onClick = onrampOfferUM.onBuyClicked, + ) + } + SpacerH(10.dp) + HorizontalDivider(color = TangemTheme.colors.stroke.primary) + SpacerH(12.dp) + PaymentBlockInOffer( + paymentMethod = onrampOfferUM.paymentMethod, + providerName = onrampOfferUM.providerName, ) - // TODO in [REDACTED_TASK_KEY] to be continued + } +} + +@Composable +private fun OfferHeader(advantage: OnrampOfferAdvantagesUM) { + when (advantage) { + OnrampOfferAdvantagesUM.Default -> { + Text( + text = stringResourceSafe(R.string.onramp_title_you_get), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + OnrampOfferAdvantagesUM.BestRate -> { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_best_rate_16), + tint = TangemTheme.colors.icon.accent, + contentDescription = null, + ) + Text( + text = stringResourceSafe(R.string.express_provider_best_rate), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.icon.accent, + ) + } + } + OnrampOfferAdvantagesUM.Fastest -> { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_fastest_16), + tint = TangemTheme.colors.icon.attention, + contentDescription = null, + ) + Text( + text = stringResourceSafe(R.string.onramp_offer_type_fastet), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.icon.attention, + ) + } + } + } +} + +@Composable +private fun RateBlock(rate: String, diff: TextReference?) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + text = rate, + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + diff?.let { + Text( + modifier = Modifier + .background( + color = TangemTheme.colors.text.warning.copy(alpha = 0.1f), + shape = RoundedCornerShape(4.dp), + ) + .padding(horizontal = 4.dp), + text = it.resolveReference(), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.text.warning, + ) + } + } +} + +@Composable +private fun PaymentBlockInOffer(paymentMethod: OnrampPaymentMethod, providerName: String) { + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + modifier = Modifier.size(16.dp), + imageVector = ImageVector.vectorResource(R.drawable.ic_clock_24), + tint = TangemTheme.colors.icon.informative, + contentDescription = null, + ) + + SpacerW(2.dp) + + TimingBlock(speed = paymentMethod.type.getProcessingSpeed()) + + SpacerW(6.dp) + + DrawDot(color = TangemTheme.colors.text.tertiary) + + SpacerW(6.dp) + + Text( + text = providerName, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + + SpacerWMax() + + Text( + text = stringResourceSafe(R.string.onramp_pay_with), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + + SpacerW(4.dp) + + SubcomposeAsyncImage( + modifier = Modifier.sizeIn(maxWidth = 38.dp, maxHeight = 16.dp), + model = ImageRequest.Builder(context = LocalContext.current) + .data(paymentMethod.imageUrl) + .crossfade(enable = true) + .allowHardware(false) + .build(), + loading = { + TextShimmer( + style = TangemTheme.typography.body1, + modifier = Modifier.width(40.dp), + ) + }, + contentDescription = null, + ) + } +} + +@Composable +private fun TimingBlock(speed: PaymentMethodType.PaymentSpeed) { + val timingText = when (speed) { + PaymentMethodType.PaymentSpeed.Instant -> { + stringResourceSafe(id = R.string.onramp_instant_status) + } + PaymentMethodType.PaymentSpeed.FewMin -> { + stringResourceSafe( + id = R.string.onramp_timing_minutes, + FEW_MINS_VALUE, + ) + } + PaymentMethodType.PaymentSpeed.FewDays -> { + pluralStringResourceSafe( + id = R.plurals.onramp_timing_days, + count = FEW_DAYS_VALUE, + FEW_DAYS_VALUE, + ) + } + PaymentMethodType.PaymentSpeed.PlentyDays -> { + pluralStringResourceSafe( + id = R.plurals.onramp_timing_days, + count = PLENTY_DAYS_VALUE, + PLENTY_DAYS_VALUE, + ) + } + PaymentMethodType.PaymentSpeed.Unknown -> "" + } + + Text( + text = timingText, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) +} + +@Composable +fun DrawDot(color: Color) { + Spacer( + modifier = Modifier + .size(4.dp) + .drawWithCache { + val radius = size.minDimension / 2f + onDrawBehind { + drawCircle( + color = color, + radius = radius, + ) + } + }, + ) +} + +// will be hardcoded till server will be ready to provide this values +private const val FEW_MINS_VALUE = "3-5" +private const val FEW_DAYS_VALUE = 3 +private const val PLENTY_DAYS_VALUE = 5 + +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun OnrampOffersContentPreview() { + val state = OnrampOffersBlockUM.Content( + isBlockVisible = true, + recentOffer = OnrampOfferUM( + category = OnrampOfferCategoryUM.RecentlyUsed, + advantages = OnrampOfferAdvantagesUM.Default, + paymentMethod = OnrampPaymentMethod( + id = "card", + name = "Card", + imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png", + type = PaymentMethodType.CARD, + ), + providerId = "providerId3", + providerName = "Simplex", + rate = "0,00045334 BTC", + diff = stringReference("–27%"), + onBuyClicked = {}, + ), + recommended = persistentListOf( + OnrampOfferUM( + category = OnrampOfferCategoryUM.Recommended, + advantages = OnrampOfferAdvantagesUM.BestRate, + paymentMethod = OnrampPaymentMethod( + id = "card", + name = "Card", + imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png", + type = PaymentMethodType.CARD, + ), + providerId = "providerId1", + providerName = "Simplex", + rate = "0,0245334 BTC", + diff = null, + onBuyClicked = {}, + ), + OnrampOfferUM( + category = OnrampOfferCategoryUM.Recommended, + advantages = OnrampOfferAdvantagesUM.Fastest, + paymentMethod = OnrampPaymentMethod( + id = "card", + name = "Card", + imageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/PaymentMethods/visa-mc.png", + type = PaymentMethodType.CARD, + ), + providerId = "providerId2", + providerName = "Simplex", + rate = "0,00145334 BTC", + diff = stringReference("–0.07%"), + onBuyClicked = {}, + ), + ), + onrampAllOffersButtonConfig = null, + ) + TangemThemePreview { + OnrampOffersContent(state) } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/ui/OnrampV2AmountContent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/ui/OnrampV2AmountContent.kt index 284e2e04cc..b0ea37b98b 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/ui/OnrampV2AmountContent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/mainv2/ui/OnrampV2AmountContent.kt @@ -1,5 +1,6 @@ package com.tangem.features.onramp.mainv2.ui +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.tween import androidx.compose.foundation.background @@ -51,6 +52,7 @@ internal fun OnrampV2AmountContent(state: OnrampV2MainComponentUM.Content, modif Column( modifier = modifier + .fillMaxWidth() .background( color = TangemTheme.colors.background.action, shape = RoundedCornerShape(size = TangemTheme.dimens.radius16), @@ -61,16 +63,14 @@ internal fun OnrampV2AmountContent(state: OnrampV2MainComponentUM.Content, modif ) { OnrampHeaderTitle() - SpacerH(12.dp) - OnrampAmountField( amountField = state.amountBlockState.amountFieldModel, currencyCode = state.amountBlockState.currencyUM.code, ) - SpacerH(8.dp) - - OnrampAmountSecondary(state = state.amountBlockState.secondaryFieldModel) + AnimatedVisibility(!state.offersBlockState.isBlockVisible) { + OnrampAmountSecondary(state = state.amountBlockState.secondaryFieldModel) + } SpacerH(20.dp) @@ -115,7 +115,8 @@ private fun OnrampAmountField(amountField: AmountFieldModel, currencyCode: Strin modifier = Modifier .focusRequester(requester) .padding( - top = TangemTheme.dimens.spacing24, + top = TangemTheme.dimens.spacing8, + bottom = TangemTheme.dimens.spacing4, start = TangemTheme.dimens.spacing12, end = TangemTheme.dimens.spacing12, ) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt index e9ee2b3295..b9ff5cb77a 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt @@ -17,6 +17,8 @@ import com.tangem.core.decompose.context.childByContext import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.features.onramp.component.OnrampComponent import com.tangem.features.onramp.main.OnrampMainComponent +import com.tangem.features.onramp.mainv2.OnrampV2MainComponent +import com.tangem.features.onramp.mainv2.OnrampV2MainFeatureToggle import com.tangem.features.onramp.redirect.OnrampRedirectComponent import com.tangem.features.onramp.root.entity.OnrampChild import com.tangem.features.onramp.settings.OnrampSettingsComponent @@ -24,13 +26,15 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -@Suppress("UnusedPrivateMember") +@Suppress("LongParameterList") internal class DefaultOnrampComponent @AssistedInject constructor( @Assisted context: AppComponentContext, @Assisted private val params: OnrampComponent.Params, private val settingsComponentFactory: OnrampSettingsComponent.Factory, private val onrampMainComponentFactory: OnrampMainComponent.Factory, + private val onrampMainV2ComponentFactory: OnrampV2MainComponent.Factory, private val onrampRedirectComponentFactory: OnrampRedirectComponent.Factory, + private val onrampV2MainFeatureToggle: OnrampV2MainFeatureToggle, ) : OnrampComponent, AppComponentContext by context { private val navigation = StackNavigation() @@ -66,24 +70,44 @@ internal class DefaultOnrampComponent @AssistedInject constructor( onBack = navigation::pop, ), ) - OnrampChild.Main -> onrampMainComponentFactory.create( - context = childByContext(componentContext), - params = OnrampMainComponent.Params( - userWalletId = params.userWalletId, - cryptoCurrency = params.cryptoCurrency, - openSettings = { navigation.push(OnrampChild.Settings) }, - source = params.source, - openRedirectPage = { - navigation.push( - OnrampChild.RedirectPage( - quote = it, - cryptoCurrency = params.cryptoCurrency, - ), - ) - }, - launchSepa = params.launchSepa, - ), - ) + OnrampChild.Main -> if (onrampV2MainFeatureToggle.isOnrampNewMainEnabled) { + onrampMainV2ComponentFactory.create( + context = childByContext(componentContext), + params = OnrampV2MainComponent.Params( + userWalletId = params.userWalletId, + cryptoCurrency = params.cryptoCurrency, + openSettings = { navigation.push(OnrampChild.Settings) }, + source = params.source, + openRedirectPage = { + navigation.push( + OnrampChild.RedirectPage( + quote = it, + cryptoCurrency = params.cryptoCurrency, + ), + ) + }, + ), + ) + } else { + onrampMainComponentFactory.create( + context = childByContext(componentContext), + params = OnrampMainComponent.Params( + userWalletId = params.userWalletId, + cryptoCurrency = params.cryptoCurrency, + openSettings = { navigation.push(OnrampChild.Settings) }, + source = params.source, + openRedirectPage = { + navigation.push( + OnrampChild.RedirectPage( + quote = it, + cryptoCurrency = params.cryptoCurrency, + ), + ) + }, + launchSepa = params.launchSepa, + ), + ) + } is OnrampChild.RedirectPage -> onrampRedirectComponentFactory.create( context = childByContext(componentContext), params = OnrampRedirectComponent.Params(