Updated on 2026-08-14
This commit is contained in:
parent
8c2e30f9bf
commit
438f502a4c
5 changed files with 83 additions and 26 deletions
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.core.ui.components.provider
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.domain.express.models.ProviderFilterType
|
||||
|
|
@ -20,31 +20,31 @@ fun ProviderTypeFilterPicker(
|
|||
onFilterSelect: (ProviderFilterType) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val segments = availableFilters.map { filter ->
|
||||
TangemSegmentUM(
|
||||
id = filter.name,
|
||||
title = when (filter) {
|
||||
ProviderFilterType.ALL -> resourceReference(R.string.common_all)
|
||||
ProviderFilterType.CEX -> TextReference.Str("CEX")
|
||||
ProviderFilterType.DEX -> TextReference.Str("DEX")
|
||||
},
|
||||
)
|
||||
}.toImmutableList()
|
||||
val selectedSegment = segments.firstOrNull { it.id == selectedFilter.name }
|
||||
TangemThemeRedesign {
|
||||
// key() forces recomposition when selectedFilter changes to re-seed initialSelectedItem,
|
||||
// because TangemSegmentedPicker owns its selection state internally via remember.
|
||||
key(selectedFilter) {
|
||||
TangemSegmentedPicker(
|
||||
items = segments,
|
||||
initialSelectedItem = selectedSegment,
|
||||
isFixed = true,
|
||||
modifier = modifier,
|
||||
onClick = { segment ->
|
||||
val filterType = availableFilters.firstOrNull { it.name == segment.id }
|
||||
if (filterType != null) onFilterSelect(filterType)
|
||||
val segments = remember(availableFilters) {
|
||||
availableFilters.map { filter ->
|
||||
TangemSegmentUM(
|
||||
id = filter.name,
|
||||
title = when (filter) {
|
||||
ProviderFilterType.ALL -> resourceReference(R.string.common_all)
|
||||
ProviderFilterType.CEX -> TextReference.Str("CEX")
|
||||
ProviderFilterType.DEX -> TextReference.Str("DEX")
|
||||
},
|
||||
)
|
||||
}
|
||||
}.toImmutableList()
|
||||
}
|
||||
val selectedSegment = remember(segments, selectedFilter) {
|
||||
segments.firstOrNull { it.id == selectedFilter.name }
|
||||
}
|
||||
TangemThemeRedesign {
|
||||
TangemSegmentedPicker(
|
||||
items = segments,
|
||||
initialSelectedItem = selectedSegment,
|
||||
isFixed = true,
|
||||
modifier = modifier,
|
||||
onClick = { segment ->
|
||||
val filterType = availableFilters.firstOrNull { it.name == segment.id }
|
||||
if (filterType != null) onFilterSelect(filterType)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -74,6 +74,8 @@ internal object SwapProviderStateBuilder {
|
|||
permissionState: PermissionDataState,
|
||||
pricesLowerBest: Map<String, Float>,
|
||||
selectionType: ProviderState.SelectionType,
|
||||
isBestRate: Boolean = false,
|
||||
isNeedBestRateBadge: Boolean = false,
|
||||
needApplyFCARestrictions: Boolean,
|
||||
onProviderClick: (String) -> Unit,
|
||||
): ProviderState.Content {
|
||||
|
|
@ -83,6 +85,8 @@ internal object SwapProviderStateBuilder {
|
|||
provider = provider,
|
||||
needApplyFCARestrictions = needApplyFCARestrictions,
|
||||
permissionState = permissionState,
|
||||
isBestRate = isBestRate,
|
||||
isNeedBestRateBadge = isNeedBestRateBadge,
|
||||
),
|
||||
selectionType = selectionType,
|
||||
percentLowerThenBest = pricesLowerBest[provider.providerId]
|
||||
|
|
|
|||
|
|
@ -1698,12 +1698,15 @@ internal class SwapModel @Inject constructor(
|
|||
analyticsEventHandler.send(SwapEvents.ProviderClicked())
|
||||
val states = dataState.lastLoadedSwapStates.getLastLoadedSuccessStates()
|
||||
val pricesLowerBest = getPricesLowerBest(providerId, states)
|
||||
val bestRatedProviderId = findBestQuoteProvider(states)?.providerId ?: providerId
|
||||
uiState = stateBuilder.showSelectProviderBottomSheet(
|
||||
uiState = uiState,
|
||||
selectedProviderId = providerId,
|
||||
pricesLowerBest = pricesLowerBest,
|
||||
providersStates = dataState.lastLoadedSwapStates,
|
||||
needApplyFCARestrictions = userCountry.needApplyFCARestrictions(),
|
||||
bestRatedProviderId = bestRatedProviderId,
|
||||
isNeedBestRateBadge = dataState.lastLoadedSwapStates.consideredProvidersStates().size > 1,
|
||||
) { uiState = stateBuilder.dismissBottomSheet(uiState) }
|
||||
},
|
||||
onProviderSelect = { providerId ->
|
||||
|
|
|
|||
|
|
@ -1068,6 +1068,8 @@ internal class StateBuilder(
|
|||
pricesLowerBest: Map<String, Float>,
|
||||
providersStates: Map<SwapProvider, SwapState>,
|
||||
needApplyFCARestrictions: Boolean,
|
||||
bestRatedProviderId: String,
|
||||
isNeedBestRateBadge: Boolean,
|
||||
onDismiss: () -> Unit,
|
||||
): SwapStateHolder {
|
||||
val availableProvidersStates = providersStates.entries
|
||||
|
|
@ -1076,6 +1078,8 @@ internal class StateBuilder(
|
|||
pricesLowerBest = pricesLowerBest,
|
||||
onProviderSelect = actions.onProviderSelect,
|
||||
needApplyFCARestrictions = needApplyFCARestrictions,
|
||||
bestRatedProviderId = bestRatedProviderId,
|
||||
isNeedBestRateBadge = isNeedBestRateBadge,
|
||||
)
|
||||
}
|
||||
.sortedWith(ProviderPercentDiffComparator)
|
||||
|
|
@ -1180,6 +1184,8 @@ internal class StateBuilder(
|
|||
pricesLowerBest: Map<String, Float>,
|
||||
onProviderSelect: (String) -> Unit,
|
||||
needApplyFCARestrictions: Boolean,
|
||||
bestRatedProviderId: String,
|
||||
isNeedBestRateBadge: Boolean,
|
||||
): ProviderState? {
|
||||
val provider = this.key
|
||||
return when (val state = this.value) {
|
||||
|
|
@ -1192,6 +1198,8 @@ internal class StateBuilder(
|
|||
pricesLowerBest = pricesLowerBest,
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
needApplyFCARestrictions = needApplyFCARestrictions,
|
||||
isBestRate = bestRatedProviderId == provider.providerId && !state.priceImpact.shouldShowWarning(),
|
||||
isNeedBestRateBadge = isNeedBestRateBadge,
|
||||
onProviderClick = onProviderSelect,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ internal class SwapProviderStateBuilderTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN best rate badge inputs WHEN buildContentSelectable THEN BestTrade badge is never set`() {
|
||||
fun `GIVEN best rate AND no FCA AND no permission WHEN buildContentSelectable THEN BestTrade badge`() {
|
||||
val provider = provider(id = "any", isRecommended = false)
|
||||
val info = tokenInfo(symbol = "USDT", decimals = 6, amount = BigDecimal("100"))
|
||||
|
||||
|
|
@ -231,6 +231,48 @@ internal class SwapProviderStateBuilderTest {
|
|||
pricesLowerBest = emptyMap(),
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
needApplyFCARestrictions = false,
|
||||
isBestRate = true,
|
||||
isNeedBestRateBadge = true,
|
||||
onProviderClick = onProviderClick,
|
||||
)
|
||||
|
||||
assertThat(result.additionalBadge).isEqualTo(ProviderState.AdditionalBadge.BestTrade)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN isNeedBestRateBadge false WHEN buildContentSelectable THEN no BestTrade badge`() {
|
||||
val provider = provider(id = "any", isRecommended = false)
|
||||
val info = tokenInfo(symbol = "USDT", decimals = 6, amount = BigDecimal("100"))
|
||||
|
||||
val result = SwapProviderStateBuilder.buildContentSelectable(
|
||||
provider = provider,
|
||||
toTokenInfo = info,
|
||||
permissionState = PermissionDataState.Empty,
|
||||
pricesLowerBest = emptyMap(),
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
needApplyFCARestrictions = false,
|
||||
isBestRate = true,
|
||||
isNeedBestRateBadge = false,
|
||||
onProviderClick = onProviderClick,
|
||||
)
|
||||
|
||||
assertThat(result.additionalBadge).isEqualTo(ProviderState.AdditionalBadge.Empty)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN isBestRate false AND badge enabled WHEN buildContentSelectable THEN no BestTrade badge`() {
|
||||
val provider = provider(id = "any", isRecommended = false)
|
||||
val info = tokenInfo(symbol = "USDT", decimals = 6, amount = BigDecimal("100"))
|
||||
|
||||
val result = SwapProviderStateBuilder.buildContentSelectable(
|
||||
provider = provider,
|
||||
toTokenInfo = info,
|
||||
permissionState = PermissionDataState.Empty,
|
||||
pricesLowerBest = emptyMap(),
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
needApplyFCARestrictions = false,
|
||||
isBestRate = false,
|
||||
isNeedBestRateBadge = true,
|
||||
onProviderClick = onProviderClick,
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue