Updated on 2026-08-14
This commit is contained in:
parent
de5bb054fa
commit
2516b9e9b9
5 changed files with 53 additions and 26 deletions
|
|
@ -25,7 +25,7 @@ sealed class ProviderState {
|
|||
val subtitle: TextReference,
|
||||
val selectionType: SelectionType,
|
||||
val additionalBadge: AdditionalBadge,
|
||||
val percentLowerThenBest: Float = 0f,
|
||||
val percentLowerThenBest: PercentLowerThanBest = PercentLowerThanBest.Empty,
|
||||
override val onProviderClick: (String) -> Unit,
|
||||
) : ProviderState()
|
||||
|
||||
|
|
@ -50,18 +50,35 @@ sealed class ProviderState {
|
|||
}
|
||||
}
|
||||
|
||||
sealed class PercentLowerThanBest {
|
||||
data class Value(val value: Float) : PercentLowerThanBest()
|
||||
object Empty : PercentLowerThanBest()
|
||||
}
|
||||
|
||||
object ProviderPercentDiffComparator : Comparator<ProviderState> {
|
||||
override fun compare(o1: ProviderState, o2: ProviderState): Int {
|
||||
if (o1 is ProviderState.Content && o2 !is ProviderState.Content) {
|
||||
return 1
|
||||
}
|
||||
if (o1 !is ProviderState.Content && o2 is ProviderState.Content) {
|
||||
return -1
|
||||
}
|
||||
return if (o1 is ProviderState.Content && o2 is ProviderState.Content) {
|
||||
o1.percentLowerThenBest.compareTo(o2.percentLowerThenBest)
|
||||
if (o1 !is ProviderState.Content && o2 is ProviderState.Content) {
|
||||
return 1
|
||||
}
|
||||
if (o1 is ProviderState.Content && o2 is ProviderState.Content) {
|
||||
val o1Percent = o1.percentLowerThenBest
|
||||
val o2Percent = o2.percentLowerThenBest
|
||||
if (o1Percent is PercentLowerThanBest.Value && o2Percent !is PercentLowerThanBest.Value) {
|
||||
return -1
|
||||
}
|
||||
if (o1Percent !is PercentLowerThanBest.Value && o2Percent is PercentLowerThanBest.Value) {
|
||||
return 1
|
||||
}
|
||||
return if (o1Percent is PercentLowerThanBest.Value && o2Percent is PercentLowerThanBest.Value) {
|
||||
o1Percent.value.compareTo(o2Percent.value)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
|||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.states.ChooseProviderBottomSheetConfig
|
||||
import com.tangem.feature.swap.models.states.PercentLowerThanBest
|
||||
import com.tangem.feature.swap.models.states.ProviderState
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
|
@ -89,7 +90,7 @@ private fun ChooseProviderBottomSheet_Preview() {
|
|||
iconUrl = "",
|
||||
subtitle = stringReference("1 000 000"),
|
||||
additionalBadge = ProviderState.AdditionalBadge.BestTrade,
|
||||
percentLowerThenBest = -1.0f,
|
||||
percentLowerThenBest = PercentLowerThanBest.Value(-1.0f),
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
onProviderClick = {},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import com.tangem.core.ui.components.SpacerH24
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.states.PercentLowerThanBest
|
||||
import com.tangem.feature.swap.models.states.ProviderState
|
||||
|
||||
/**
|
||||
|
|
@ -151,8 +152,10 @@ private fun ProviderContentState(
|
|||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
if (state.percentLowerThenBest > 0f) {
|
||||
AnimatedContent(targetState = state.percentLowerThenBest, label = "") {
|
||||
if (state.percentLowerThenBest is PercentLowerThanBest.Value &&
|
||||
state.percentLowerThenBest.value > 0
|
||||
) {
|
||||
AnimatedContent(targetState = state.percentLowerThenBest.value, label = "") {
|
||||
Text(
|
||||
text = "-$it%",
|
||||
style = TangemTheme.typography.body2,
|
||||
|
|
@ -404,7 +407,7 @@ private fun ProviderItem_Content_Preview() {
|
|||
iconUrl = "",
|
||||
subtitle = stringReference("1 000 000"),
|
||||
additionalBadge = ProviderState.AdditionalBadge.PermissionRequired,
|
||||
percentLowerThenBest = -1.0f,
|
||||
percentLowerThenBest = PercentLowerThanBest.Value(-1.0f),
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
onProviderClick = {},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -789,7 +789,7 @@ internal class StateBuilder(
|
|||
fun showSelectProviderBottomSheet(
|
||||
uiState: SwapStateHolder,
|
||||
selectedProviderId: String,
|
||||
pricesLowerBest: Map<SwapProvider, Float>,
|
||||
pricesLowerBest: Map<String, Float>,
|
||||
providersStates: Map<SwapProvider, SwapState>,
|
||||
unavailableProviders: List<SwapProvider>,
|
||||
onDismiss: () -> Unit,
|
||||
|
|
@ -820,6 +820,7 @@ internal class StateBuilder(
|
|||
|
||||
fun updateProvidersBottomSheetContent(
|
||||
uiState: SwapStateHolder,
|
||||
pricesLowerBest: Map<String, Float>,
|
||||
tokenSwapInfoForProviders: Map<String, TokenSwapInfo>,
|
||||
): SwapStateHolder {
|
||||
val config = uiState.bottomSheetConfig?.content as? ChooseProviderBottomSheetConfig
|
||||
|
|
@ -835,6 +836,9 @@ internal class StateBuilder(
|
|||
.getFormattedCryptoAmount(tokenInfo.cryptoCurrencyStatus.currency)
|
||||
it.copy(
|
||||
subtitle = stringReference(rateString),
|
||||
percentLowerThenBest = pricesLowerBest[it.id]?.let { percent ->
|
||||
PercentLowerThanBest.Value(percent)
|
||||
} ?: PercentLowerThanBest.Empty,
|
||||
)
|
||||
} else {
|
||||
it
|
||||
|
|
@ -928,7 +932,7 @@ internal class StateBuilder(
|
|||
}
|
||||
|
||||
private fun Map.Entry<SwapProvider, SwapState>.convertToProviderBottomSheetState(
|
||||
pricesLowerBest: Map<SwapProvider, Float>,
|
||||
pricesLowerBest: Map<String, Float>,
|
||||
onProviderSelect: (String) -> Unit,
|
||||
): ProviderState? {
|
||||
val provider = this.key
|
||||
|
|
@ -1040,7 +1044,7 @@ internal class StateBuilder(
|
|||
subtitle = stringReference(rateString),
|
||||
additionalBadge = badge,
|
||||
selectionType = selectionType,
|
||||
percentLowerThenBest = ZERO_PERCENT,
|
||||
percentLowerThenBest = PercentLowerThanBest.Empty,
|
||||
onProviderClick = onProviderClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -1049,7 +1053,7 @@ internal class StateBuilder(
|
|||
isBestRate: Boolean,
|
||||
state: SwapState.QuotesLoadedState,
|
||||
selectionType: ProviderState.SelectionType,
|
||||
pricesLowerBest: Map<SwapProvider, Float>,
|
||||
pricesLowerBest: Map<String, Float>,
|
||||
onProviderClick: (String) -> Unit,
|
||||
): ProviderState {
|
||||
val toTokenInfo = state.toTokenInfo
|
||||
|
|
@ -1069,7 +1073,9 @@ internal class StateBuilder(
|
|||
subtitle = stringReference(rateString),
|
||||
additionalBadge = additionalBadge,
|
||||
selectionType = selectionType,
|
||||
percentLowerThenBest = pricesLowerBest[this] ?: ZERO_PERCENT,
|
||||
percentLowerThenBest = pricesLowerBest[this.providerId]?.let { percent ->
|
||||
PercentLowerThanBest.Value(percent)
|
||||
} ?: PercentLowerThanBest.Value(0f),
|
||||
onProviderClick = onProviderClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -1103,7 +1109,7 @@ internal class StateBuilder(
|
|||
selectionType = selectionType,
|
||||
subtitle = alertText,
|
||||
additionalBadge = ProviderState.AdditionalBadge.Empty,
|
||||
percentLowerThenBest = ZERO_PERCENT,
|
||||
percentLowerThenBest = PercentLowerThanBest.Empty,
|
||||
onProviderClick = onProviderClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -1148,6 +1154,5 @@ internal class StateBuilder(
|
|||
private const val HUNDRED_PERCENTS = 100
|
||||
private const val UNKNOWN_AMOUNT_SIGN = "—"
|
||||
private const val MAX_DECIMALS_TO_SHOW = 8
|
||||
private const val ZERO_PERCENT = 0f
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.*
|
||||
import arrow.core.getOrElse
|
||||
import arrow.core.mapNotNull
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.utils.InputNumberFormatter
|
||||
|
|
@ -289,11 +288,13 @@ internal class SwapViewModel @Inject constructor(
|
|||
if (providersState.isNotEmpty()) {
|
||||
val (provider, state) = updateLoadedQuotes(providersState)
|
||||
setupLoadedState(provider, state, fromToken)
|
||||
val successStates = providersState
|
||||
.getLastLoadedSuccessStates()
|
||||
val pricesLowerBest = getPricesLowerBest(successStates)
|
||||
uiState = stateBuilder.updateProvidersBottomSheetContent(
|
||||
uiState = uiState,
|
||||
tokenSwapInfoForProviders = providersState
|
||||
.getLastLoadedSuccessStates()
|
||||
.entries
|
||||
pricesLowerBest = pricesLowerBest,
|
||||
tokenSwapInfoForProviders = successStates.entries
|
||||
.associate { it.key.providerId to it.value.toTokenInfo },
|
||||
)
|
||||
} else {
|
||||
|
|
@ -869,21 +870,21 @@ internal class SwapViewModel @Inject constructor(
|
|||
}?.key
|
||||
}
|
||||
|
||||
private fun getPricesLowerBest(state: SuccessLoadedSwapData): Map<SwapProvider, Float> {
|
||||
private fun getPricesLowerBest(state: SuccessLoadedSwapData): Map<String, Float> {
|
||||
val bestRateEntry = state.maxByOrNull { it.value.toTokenInfo.tokenAmount.value } ?: return emptyMap()
|
||||
val bestRate = bestRateEntry.value.toTokenInfo.tokenAmount.value
|
||||
val hundredPercent = BigDecimal("100")
|
||||
return state.mapNotNull {
|
||||
return state.entries.mapNotNull {
|
||||
if (it.key != bestRateEntry.key) {
|
||||
val amount = it.value.toTokenInfo.tokenAmount.value
|
||||
val percentDiff = BigDecimal.ONE.minus(
|
||||
amount.divide(bestRate, RoundingMode.HALF_UP),
|
||||
).multiply(hundredPercent)
|
||||
percentDiff.setScale(2, RoundingMode.HALF_UP).toFloat().absoluteValue
|
||||
it.key.providerId to percentDiff.setScale(2, RoundingMode.HALF_UP).toFloat().absoluteValue
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
private fun createSelectedAppCurrencyFlow(): StateFlow<AppCurrency> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue