Updated on 2026-08-14
This commit is contained in:
parent
34d43ad7c9
commit
2f767f566a
10 changed files with 81 additions and 8 deletions
16
core/ui/src/main/res/drawable/ic_stack_new_24.xml
Normal file
16
core/ui/src/main/res/drawable/ic_stack_new_24.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M18.656,15.224C18.898,14.92 19.328,14.854 19.648,15.059L19.71,15.104L19.875,15.245C20.247,15.585 20.532,16.024 20.532,16.6C20.532,17.432 19.947,17.969 19.336,18.362C18.868,18.663 18.238,18.97 17.483,19.319L16.688,19.683L15.213,20.355C13.847,20.978 12.961,21.399 12,21.399C11.28,21.399 10.601,21.162 9.73,20.781L8.787,20.355L7.312,19.683C6.2,19.177 5.288,18.763 4.664,18.362C4.053,17.969 3.468,17.432 3.468,16.6C3.468,15.941 3.84,15.462 4.29,15.104L4.352,15.059C4.672,14.854 5.102,14.92 5.344,15.224C5.602,15.548 5.548,16.02 5.224,16.278L5.135,16.353C5.059,16.423 5.017,16.477 4.996,16.513C4.972,16.552 4.968,16.575 4.968,16.6C4.968,16.617 4.951,16.763 5.475,17.1C5.986,17.428 6.776,17.79 7.934,18.318L9.409,18.99L10.364,19.418C11.171,19.769 11.581,19.899 12,19.899C12.559,19.899 13.101,19.669 14.591,18.99L16.066,18.318L16.867,17.952C17.601,17.612 18.142,17.346 18.525,17.1C19.049,16.763 19.032,16.617 19.032,16.6C19.032,16.575 19.028,16.552 19.004,16.513C18.983,16.477 18.941,16.423 18.865,16.353L18.776,16.278L18.718,16.227C18.446,15.962 18.414,15.528 18.656,15.224Z"
|
||||
android:fillColor="#1E1E1E"/>
|
||||
<path
|
||||
android:pathData="M3.791,10.784C4.031,10.446 4.499,10.367 4.836,10.607C5.174,10.847 5.253,11.315 5.013,11.653C4.972,11.711 4.968,11.741 4.968,11.766C4.968,11.783 4.951,11.929 5.475,12.266C5.986,12.594 6.776,12.956 7.934,13.484L9.409,14.156L10.364,14.584C11.171,14.936 11.581,15.065 12,15.065C12.559,15.065 13.101,14.835 14.591,14.156L16.066,13.484L16.867,13.118C17.601,12.778 18.142,12.512 18.525,12.266C19.049,11.929 19.032,11.783 19.032,11.766C19.032,11.753 19.031,11.74 19.025,11.722L18.987,11.653L18.946,11.588C18.758,11.258 18.847,10.832 19.164,10.607C19.48,10.382 19.911,10.437 20.162,10.723L20.209,10.784L20.282,10.893C20.44,11.152 20.532,11.444 20.532,11.766C20.532,12.598 19.947,13.135 19.336,13.528C18.868,13.829 18.238,14.137 17.483,14.486L16.688,14.849L15.213,15.521C13.847,16.143 12.961,16.565 12,16.565C11.28,16.565 10.601,16.328 9.73,15.947L8.787,15.521L7.312,14.849C6.2,14.342 5.288,13.929 4.664,13.528C4.053,13.135 3.468,12.598 3.468,11.766C3.468,11.398 3.588,11.069 3.791,10.784Z"
|
||||
android:fillColor="#1E1E1E"/>
|
||||
<path
|
||||
android:pathData="M12,2.604C12.964,2.604 13.852,3.032 15.217,3.664L16.691,4.345L17.487,4.714C18.242,5.068 18.872,5.381 19.34,5.686C19.954,6.086 20.532,6.628 20.532,7.461C20.532,8.294 19.954,8.837 19.34,9.237C18.872,9.542 18.242,9.854 17.487,10.208L16.691,10.577L15.217,11.258C13.852,11.889 12.964,12.318 12,12.318C11.278,12.318 10.597,12.077 9.726,11.69L8.783,11.258L7.309,10.577C6.197,10.063 5.284,9.644 4.66,9.237C4.046,8.837 3.468,8.294 3.468,7.461C3.468,6.628 4.046,6.086 4.66,5.686C5.284,5.279 6.197,4.859 7.309,4.345L8.783,3.664L9.726,3.232C10.597,2.845 11.278,2.604 12,2.604ZM12,4.104C11.583,4.104 11.175,4.234 10.368,4.59L9.412,5.025L7.938,5.706C6.78,6.242 5.99,6.609 5.479,6.942C4.958,7.282 4.968,7.433 4.968,7.461C4.968,7.49 4.96,7.642 5.479,7.98C5.99,8.313 6.78,8.68 7.938,9.215L9.412,9.897L10.368,10.332C11.175,10.688 11.583,10.818 12,10.818C12.556,10.818 13.097,10.586 14.588,9.897L16.062,9.215L16.863,8.843C17.598,8.498 18.138,8.23 18.521,7.98C19.04,7.642 19.032,7.49 19.032,7.461C19.032,7.433 19.042,7.282 18.521,6.942C18.138,6.692 17.598,6.423 16.863,6.079L16.062,5.706L14.588,5.025C13.097,4.336 12.556,4.104 12,4.104Z"
|
||||
android:fillColor="#1E1E1E"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
|
|
@ -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<SwapQuoteUM>,
|
||||
val cryptoCurrency: CryptoCurrency,
|
||||
val selectedProvider: ExpressProvider,
|
||||
val userCountry: UserCountry,
|
||||
)
|
||||
}
|
||||
|
|
@ -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<SwapChooseProviderConfig> = SlotNavigation()
|
||||
|
||||
val uiState: StateFlow<SwapAmountUM>
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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<SwapQuoteUM>,
|
||||
val userCountry: UserCountry,
|
||||
val callback: ModelCallback,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
|
||||
internal data class SwapChooseProviderBottomSheetContent(
|
||||
val providerList: ImmutableList<SwapProviderListItem>,
|
||||
val isApplyFCARestrictions: Boolean,
|
||||
val selectedProvider: ExpressProvider,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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<SwapQuoteUM, SwapProviderListItem?> {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<SwapProviderListItem>,
|
||||
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 = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue