From 2f767f566a8fd87c0028d568b068688917565cfd Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 28 Jul 2025 19:29:43 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../src/main/res/drawable/ic_stack_new_24.xml | 16 ++++++++++++ .../impl/amount/SwapAmountBlockComponent.kt | 4 +++ .../v2/impl/amount/model/SwapAmountModel.kt | 7 ++++++ .../SwapChooseProviderComponent.kt | 4 ++- .../SwapChooseProviderBottomSheetContent.kt | 1 + .../model/SwapChooseProviderModel.kt | 6 +++++ .../SwapProviderListItemConverter.kt | 10 +++++++- .../ui/SwapChooseProviderBottomSheet.kt | 25 +++++++++++++++---- .../SwapChooseProviderContentPreview.kt | 3 ++- .../swap/v2/impl/common/SwapProviderUtils.kt | 13 ++++++++++ 10 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_stack_new_24.xml create mode 100644 features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapProviderUtils.kt diff --git a/core/ui/src/main/res/drawable/ic_stack_new_24.xml b/core/ui/src/main/res/drawable/ic_stack_new_24.xml new file mode 100644 index 0000000000..a0a35fc823 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_stack_new_24.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt index dbbf511732..5917c12bf3 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt @@ -17,6 +17,7 @@ import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.features.swap.v2.impl.amount.SwapAmountComponentParams.AmountBlockParams import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM import com.tangem.features.swap.v2.impl.amount.model.SwapAmountModel @@ -77,6 +78,7 @@ internal class SwapAmountBlockComponent( providers = amountUM.swapQuotes, cryptoCurrency = cryptoCurrency, selectedProvider = selectedProvider, + userCountry = model.userCountry, ), ) }, @@ -95,6 +97,7 @@ internal class SwapAmountBlockComponent( providers = config.providers, cryptoCurrency = config.cryptoCurrency, selectedProvider = config.selectedProvider, + userCountry = config.userCountry, callback = model, onDismiss = { model.bottomSheetNavigation.dismiss() }, ), @@ -105,5 +108,6 @@ internal class SwapAmountBlockComponent( val providers: ImmutableList, val cryptoCurrency: CryptoCurrency, val selectedProvider: ExpressProvider, + val userCountry: UserCountry, ) } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index dfc08a58f3..23c478c056 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -17,6 +17,8 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.express.models.ExpressError import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.settings.usercountry.GetUserCountryUseCase +import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.domain.swap.models.SwapCurrencies import com.tangem.domain.swap.models.SwapDirection import com.tangem.domain.swap.models.SwapDirection.Companion.withSwapDirection @@ -54,6 +56,7 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber import java.math.BigDecimal +import java.util.Locale import javax.inject.Inject import kotlin.properties.Delegates import com.tangem.utils.transformer.update as transformerUpdate @@ -69,6 +72,7 @@ internal class SwapAmountModel @Inject constructor( private val swapChooseTokenNetworkListener: SwapChooseTokenNetworkListener, private val getAllowanceUseCase: GetAllowanceUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + private val getUserCountryUseCase: GetUserCountryUseCase, private val appRouter: AppRouter, private val swapAmountAlertFactory: SwapAmountAlertFactory, private val swapAlertFactory: SwapAlertFactory, @@ -89,6 +93,7 @@ internal class SwapAmountModel @Inject constructor( private var secondaryMaximumAmountBoundary: EnterAmountBoundary? = null private var secondaryMinimumAmountBoundary: EnterAmountBoundary? = null + var userCountry: UserCountry = UserCountry.Other(Locale.getDefault().country) val bottomSheetNavigation: SlotNavigation = SlotNavigation() val uiState: StateFlow @@ -100,6 +105,8 @@ internal class SwapAmountModel @Inject constructor( init { modelScope.launch { appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default } + userCountry = getUserCountryUseCase.invokeSync().getOrNull() + ?: UserCountry.Other(Locale.getDefault().country) } configAmountNavigation() subscribeOnCryptoCurrencyStatusFlow() diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt index 744a8a3c91..c04a88654c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/SwapChooseProviderComponent.kt @@ -10,6 +10,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.features.swap.v2.impl.chooseprovider.model.SwapChooseProviderModel import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderBottomSheet import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderContent @@ -41,7 +42,7 @@ internal class SwapChooseProviderComponent( SwapChooseProviderBottomSheet(config = bottomSheetConfig) { SwapChooseProviderContent( - providerList = state.value.providerList, + contentUM = state.value, onProviderClick = model::onProviderClick, ) } @@ -51,6 +52,7 @@ internal class SwapChooseProviderComponent( val cryptoCurrency: CryptoCurrency, val selectedProvider: ExpressProvider, val providers: ImmutableList, + val userCountry: UserCountry, val callback: ModelCallback, val onDismiss: () -> Unit, ) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt index ad5eb47e18..289afe2a26 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/entity/SwapChooseProviderBottomSheetContent.kt @@ -7,6 +7,7 @@ import kotlinx.collections.immutable.ImmutableList internal data class SwapChooseProviderBottomSheetContent( val providerList: ImmutableList, + val isApplyFCARestrictions: Boolean, val selectedProvider: ExpressProvider, ) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt index 5b9ce17796..c4bf3d574e 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/SwapChooseProviderModel.kt @@ -3,10 +3,12 @@ package com.tangem.features.swap.v2.impl.chooseprovider.model import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions import com.tangem.features.swap.v2.impl.chooseprovider.SwapChooseProviderComponent import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapChooseProviderBottomSheetContent import com.tangem.features.swap.v2.impl.chooseprovider.model.converter.SwapProviderListItemConverter import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM +import com.tangem.features.swap.v2.impl.common.isRestrictedByFCA import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.MutableStateFlow @@ -21,10 +23,13 @@ internal class SwapChooseProviderModel @Inject constructor( private val params: SwapChooseProviderComponent.Params = paramsContainer.require() + private val needApplyFCARestrictions = params.userCountry.needApplyFCARestrictions() + private val swapProviderListItemConverter by lazy(LazyThreadSafetyMode.NONE) { SwapProviderListItemConverter( cryptoCurrency = params.cryptoCurrency, selectedProvider = params.selectedProvider, + needApplyFCARestrictions = needApplyFCARestrictions, ) } @@ -38,6 +43,7 @@ internal class SwapChooseProviderModel @Inject constructor( private fun getInitialState(): SwapChooseProviderBottomSheetContent { return SwapChooseProviderBottomSheetContent( + isApplyFCARestrictions = needApplyFCARestrictions && params.selectedProvider.isRestrictedByFCA(), providerList = swapProviderListItemConverter.convertList(params.providers) .filterNotNull() .toPersistentList(), diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt index 75234f816b..e7f45e0726 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/model/converter/SwapProviderListItemConverter.kt @@ -15,11 +15,13 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderListItem import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM +import com.tangem.features.swap.v2.impl.common.isRestrictedByFCA import com.tangem.utils.converter.Converter internal class SwapProviderListItemConverter( private val cryptoCurrency: CryptoCurrency, private val selectedProvider: ExpressProvider, + private val needApplyFCARestrictions: Boolean, ) : Converter { override fun convert(value: SwapQuoteUM): SwapProviderListItem? { val provider = value.provider ?: return null @@ -66,9 +68,15 @@ internal class SwapProviderListItemConverter( }, ) } + is SwapQuoteUM.Content -> if (needApplyFCARestrictions && value.provider.isRestrictedByFCA()) { + ProviderChooseUM.ExtraUM.Action( + text = resourceReference(R.string.express_provider_fca_warning_list), + ) + } else { + ProviderChooseUM.ExtraUM.Empty + } SwapQuoteUM.Empty, SwapQuoteUM.Loading, - is SwapQuoteUM.Content, -> ProviderChooseUM.ExtraUM.Empty } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt index 46ab92931b..11d0e32879 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/SwapChooseProviderBottomSheet.kt @@ -1,6 +1,7 @@ package com.tangem.features.swap.v2.impl.chooseprovider.ui import android.content.res.Configuration +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material3.Icon @@ -19,6 +20,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle +import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.components.provider.ProviderChooseCrypto import com.tangem.core.ui.extensions.conditional import com.tangem.core.ui.extensions.resourceReference @@ -27,10 +29,9 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.swap.v2.impl.R import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapChooseProviderBottomSheetContent -import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderListItem import com.tangem.features.swap.v2.impl.chooseprovider.ui.preview.SwapChooseProviderContentPreview import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM -import kotlinx.collections.immutable.ImmutableList +import com.tangem.features.swap.v2.impl.notifications.entity.SwapNotificationUM @Composable internal fun SwapChooseProviderBottomSheet(config: TangemBottomSheetConfig, content: @Composable () -> Unit) { @@ -50,7 +51,7 @@ internal fun SwapChooseProviderBottomSheet(config: TangemBottomSheetConfig, cont @Composable internal fun SwapChooseProviderContent( - providerList: ImmutableList, + contentUM: SwapChooseProviderBottomSheetContent, onProviderClick: (SwapQuoteUM) -> Unit, modifier: Modifier = Modifier, ) { @@ -64,7 +65,17 @@ internal fun SwapChooseProviderContent( color = TangemTheme.colors.text.secondary, textAlign = TextAlign.Center, ) - providerList.fastForEachIndexed { index, provider -> + AnimatedVisibility( + modifier = Modifier.padding(top = 12.dp), + visible = contentUM.isApplyFCARestrictions, + ) { + Notification( + config = SwapNotificationUM.Error.FCAWarningList.config, + containerColor = TangemTheme.colors.button.disabled, + iconTint = TangemTheme.colors.icon.warning, + ) + } + contentUM.providerList.fastForEachIndexed { index, provider -> ProviderChooseCrypto( providerChooseUM = provider.providerUM, onClick = { onProviderClick(provider.quote) }, @@ -114,7 +125,11 @@ private fun SwapChooseProviderContent_Preview( ), ) { SwapChooseProviderContent( - providerList = params.providerList, + contentUM = SwapChooseProviderBottomSheetContent( + providerList = params.providerList, + isApplyFCARestrictions = true, + selectedProvider = SwapChooseProviderContentPreview.provider1, + ), onProviderClick = {}, ) } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt index b96c7acb4b..dd75bbf62c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/chooseprovider/ui/preview/SwapChooseProviderContentPreview.kt @@ -16,7 +16,7 @@ import java.math.BigDecimal internal object SwapChooseProviderContentPreview { - private val provider1 = ExpressProvider( + val provider1 = ExpressProvider( providerId = "changenow", rateTypes = listOf(ExpressRateType.Float), name = "ChangeNow", @@ -88,5 +88,6 @@ internal object SwapChooseProviderContentPreview { ), ), selectedProvider = provider1, + isApplyFCARestrictions = false, ) } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapProviderUtils.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapProviderUtils.kt new file mode 100644 index 0000000000..6c9e8ece21 --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapProviderUtils.kt @@ -0,0 +1,13 @@ +package com.tangem.features.swap.v2.impl.common + +import com.tangem.domain.express.models.ExpressProvider + +private val FCA_RESTRICTED_PROVIDER_IDS = setOf( + "changelly", + "changenow", + "okx-cross-chain", + "okx-on-chain", + "simpleswap", +) + +fun ExpressProvider.isRestrictedByFCA() = FCA_RESTRICTED_PROVIDER_IDS.contains(providerId) \ No newline at end of file