Updated on 2026-08-14
This commit is contained in:
parent
0dc200beac
commit
7ee8825f97
3 changed files with 92 additions and 81 deletions
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.features.swap.v2.impl.amount
|
||||
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.models.SwapDirection
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -15,19 +14,19 @@ internal sealed class SwapAmountComponentParams {
|
|||
abstract val amountUM: SwapAmountUM
|
||||
abstract val analyticsCategoryName: String
|
||||
abstract val userWallet: UserWallet
|
||||
abstract val appCurrency: AppCurrency
|
||||
abstract val swapDirection: SwapDirection
|
||||
abstract val isBalanceHidingFlow: StateFlow<Boolean>
|
||||
abstract val primaryCryptoCurrencyStatusFlow: StateFlow<CryptoCurrencyStatus>
|
||||
abstract val secondaryCryptoCurrency: CryptoCurrency?
|
||||
|
||||
data class AmountParams(
|
||||
override val amountUM: SwapAmountUM,
|
||||
override val analyticsCategoryName: String,
|
||||
override val userWallet: UserWallet,
|
||||
override val isBalanceHidingFlow: StateFlow<Boolean>,
|
||||
override val appCurrency: AppCurrency,
|
||||
override val swapDirection: SwapDirection,
|
||||
val primaryCryptoCurrency: CryptoCurrency,
|
||||
val secondaryCryptoCurrency: CryptoCurrency?,
|
||||
override val primaryCryptoCurrencyStatusFlow: StateFlow<CryptoCurrencyStatus>,
|
||||
override val secondaryCryptoCurrency: CryptoCurrency?,
|
||||
val callback: SwapAmountComponent.ModelCallback,
|
||||
val currentRoute: Flow<SwapRoute.Amount>,
|
||||
) : SwapAmountComponentParams()
|
||||
|
|
@ -37,10 +36,9 @@ internal sealed class SwapAmountComponentParams {
|
|||
override val analyticsCategoryName: String,
|
||||
override val userWallet: UserWallet,
|
||||
override val isBalanceHidingFlow: StateFlow<Boolean>,
|
||||
override val appCurrency: AppCurrency,
|
||||
override val swapDirection: SwapDirection,
|
||||
val primaryCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||
override val primaryCryptoCurrencyStatusFlow: StateFlow<CryptoCurrencyStatus>,
|
||||
override val secondaryCryptoCurrency: CryptoCurrency?,
|
||||
val blockClickEnableFlow: StateFlow<Boolean>,
|
||||
) : SwapAmountComponentParams()
|
||||
}
|
||||
|
|
@ -14,10 +14,11 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
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.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.express.models.ExpressError
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.express.models.ExpressRateType
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.models.SwapCurrencies
|
||||
import com.tangem.domain.swap.models.SwapDirection
|
||||
import com.tangem.domain.swap.models.SwapQuoteModel
|
||||
import com.tangem.domain.swap.usecase.GetSwapPairsUseCase
|
||||
|
|
@ -41,12 +42,10 @@ import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
|||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM.Content.DifferencePercent
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
import com.tangem.utils.transformer.update as transformerUpdate
|
||||
|
|
@ -63,19 +62,23 @@ internal class SwapAmountModel @Inject constructor(
|
|||
private val getSwapQuoteUseCase: GetSwapQuoteUseCase,
|
||||
private val swapChooseTokenNetworkListener: SwapChooseTokenNetworkListener,
|
||||
private val getAllowanceUseCase: GetAllowanceUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val appRouter: AppRouter,
|
||||
) : Model(), SwapAmountClickIntents, SwapChooseProviderComponent.ModelCallback {
|
||||
|
||||
private val params: SwapAmountComponentParams = paramsContainer.require()
|
||||
private val swapDirection = params.swapDirection
|
||||
private val appCurrency = params.appCurrency
|
||||
private var appCurrency = AppCurrency.Default
|
||||
private var userWallet = params.userWallet
|
||||
|
||||
private var primaryCryptoCurrency: CryptoCurrency by Delegates.notNull()
|
||||
private var secondaryCryptoCurrency: CryptoCurrency? = null
|
||||
private var primaryCryptoCurrency: CryptoCurrency = params.primaryCryptoCurrencyStatusFlow.value.currency
|
||||
private var secondaryCryptoCurrency: CryptoCurrency? = params.secondaryCryptoCurrency
|
||||
|
||||
private var primaryCryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
private var secondaryCryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
private var primaryCryptoCurrencyStatus: CryptoCurrencyStatus = params.primaryCryptoCurrencyStatusFlow.value
|
||||
private var secondaryCryptoCurrencyStatus: CryptoCurrencyStatus = CryptoCurrencyStatus(
|
||||
currency = primaryCryptoCurrencyStatus.currency,
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
|
||||
private var primaryMaximumAmountBoundary: EnterAmountBoundary by Delegates.notNull()
|
||||
private var secondaryMaximumAmountBoundary: EnterAmountBoundary by Delegates.notNull()
|
||||
|
|
@ -86,24 +89,11 @@ internal class SwapAmountModel @Inject constructor(
|
|||
field = MutableStateFlow(params.amountUM)
|
||||
|
||||
init {
|
||||
when (params) {
|
||||
is SwapAmountComponentParams.AmountParams -> {
|
||||
primaryCryptoCurrency = params.primaryCryptoCurrency
|
||||
secondaryCryptoCurrency = params.secondaryCryptoCurrency
|
||||
}
|
||||
is SwapAmountComponentParams.AmountBlockParams -> {
|
||||
primaryCryptoCurrency = params.primaryCryptoCurrencyStatus.currency
|
||||
secondaryCryptoCurrency = params.secondaryCryptoCurrencyStatus?.currency
|
||||
primaryCryptoCurrencyStatus = params.primaryCryptoCurrencyStatus
|
||||
params.secondaryCryptoCurrencyStatus?.let {
|
||||
secondaryCryptoCurrencyStatus = it
|
||||
}
|
||||
}
|
||||
modelScope.launch {
|
||||
appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }
|
||||
}
|
||||
|
||||
initialState()
|
||||
configAmountNavigation()
|
||||
|
||||
subscribeOnCryptoCurrencyStatusFlow()
|
||||
observeChooseSelectToken()
|
||||
// todo observe balance hiding flow
|
||||
}
|
||||
|
|
@ -182,6 +172,27 @@ internal class SwapAmountModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun subscribeOnCryptoCurrencyStatusFlow() {
|
||||
params.primaryCryptoCurrencyStatusFlow.onEach { primaryCurrencyStatus ->
|
||||
primaryCryptoCurrencyStatus = primaryCurrencyStatus
|
||||
|
||||
val state = uiState.value
|
||||
if (state is SwapAmountUM.Content) {
|
||||
secondaryCryptoCurrency = state.primaryCryptoCurrencyStatus.currency
|
||||
secondaryCryptoCurrencyStatus = state.primaryCryptoCurrencyStatus
|
||||
initCurrencies(
|
||||
primaryStatus = state.primaryCryptoCurrencyStatus,
|
||||
secondaryStatus = state.secondaryCryptoCurrencyStatus,
|
||||
)
|
||||
} else {
|
||||
initPairs(
|
||||
primaryCryptoCurrency = primaryCryptoCurrency,
|
||||
secondaryCryptoCurrency = secondaryCryptoCurrency,
|
||||
)
|
||||
}
|
||||
}.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun observeChooseSelectToken() {
|
||||
swapChooseTokenNetworkListener.swapChooseTokenNetworkResultFlow
|
||||
.onEach { currency ->
|
||||
|
|
@ -202,46 +213,12 @@ internal class SwapAmountModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun initialState() {
|
||||
if (uiState.value is SwapAmountUM.Empty) {
|
||||
initPairs(
|
||||
primaryCryptoCurrency = primaryCryptoCurrency,
|
||||
secondaryCryptoCurrency = secondaryCryptoCurrency,
|
||||
)
|
||||
uiState.update {
|
||||
SwapAmountUM.Content(
|
||||
isPrimaryButtonEnabled = it.isPrimaryButtonEnabled,
|
||||
primaryAmount = it.primaryAmount,
|
||||
secondaryAmount = it.secondaryAmount,
|
||||
swapDirection = swapDirection,
|
||||
swapCurrencies = SwapCurrencies.EMPTY,
|
||||
appCurrency = appCurrency,
|
||||
swapQuotes = persistentListOf(),
|
||||
swapRateType = ExpressRateType.Float,
|
||||
selectedAmountType = SwapAmountType.From,
|
||||
selectedQuote = SwapQuoteUM.Empty,
|
||||
primaryCryptoCurrencyStatus = null,
|
||||
secondaryCryptoCurrencyStatus = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initPairs(primaryCryptoCurrency: CryptoCurrency, secondaryCryptoCurrency: CryptoCurrency?) {
|
||||
modelScope.launch {
|
||||
val cryptoCurrencyStatusList = getMultiCryptoCurrencyStatusUseCase
|
||||
.invokeMultiWalletSync(userWallet.walletId)
|
||||
.getOrElse { emptyList() }
|
||||
|
||||
val primaryStatus = cryptoCurrencyStatusList.firstOrNull {
|
||||
it.currency.id == primaryCryptoCurrency.id
|
||||
}
|
||||
if (primaryStatus == null) {
|
||||
Timber.e("Failed to get crypto currency status")
|
||||
// todo error
|
||||
return@launch
|
||||
}
|
||||
|
||||
val cryptoCurrencyStatusListExceptPrimary = cryptoCurrencyStatusList.filter {
|
||||
val statusFilter = it.value is CryptoCurrencyStatus.Loaded || it.value is CryptoCurrencyStatus.NoAccount
|
||||
val notCustomTokenFilter = !it.currency.isCustom
|
||||
|
|
@ -263,7 +240,9 @@ internal class SwapAmountModel @Inject constructor(
|
|||
)
|
||||
|
||||
if (secondaryStatus != null) {
|
||||
initCurrencies(primaryStatus, secondaryStatus)
|
||||
initCurrencies(primaryCryptoCurrencyStatus, secondaryStatus)
|
||||
this@SwapAmountModel.secondaryCryptoCurrency = secondaryCryptoCurrency
|
||||
secondaryCryptoCurrencyStatus = secondaryStatus
|
||||
uiState.update {
|
||||
SwapAmountReadyStateConverter(
|
||||
swapCurrencies = swapCurrencies,
|
||||
|
|
@ -288,9 +267,6 @@ internal class SwapAmountModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun initCurrencies(primaryStatus: CryptoCurrencyStatus, secondaryStatus: CryptoCurrencyStatus) {
|
||||
primaryCryptoCurrencyStatus = primaryStatus
|
||||
secondaryCryptoCurrencyStatus = secondaryStatus
|
||||
|
||||
primaryMinimumAmountBoundary = EnterAmountBoundary(
|
||||
amount = getMinimumTransactionAmountSyncUseCase
|
||||
.invoke(
|
||||
|
|
@ -317,8 +293,12 @@ internal class SwapAmountModel @Inject constructor(
|
|||
val state = uiState.value as? SwapAmountUM.Content ?: return
|
||||
|
||||
val (fromCryptoCurrency, toCryptoCurrency) = when (state.swapDirection) {
|
||||
SwapDirection.Direct -> primaryCryptoCurrencyStatus.currency to secondaryCryptoCurrencyStatus.currency
|
||||
SwapDirection.Reverse -> secondaryCryptoCurrencyStatus.currency to primaryCryptoCurrencyStatus.currency
|
||||
SwapDirection.Direct -> {
|
||||
state.primaryCryptoCurrencyStatus.currency to state.secondaryCryptoCurrencyStatus.currency
|
||||
}
|
||||
SwapDirection.Reverse -> {
|
||||
state.secondaryCryptoCurrencyStatus.currency to state.primaryCryptoCurrencyStatus.currency
|
||||
}
|
||||
}
|
||||
|
||||
val fromAmount = when (state.swapDirection) {
|
||||
|
|
@ -338,9 +318,11 @@ internal class SwapAmountModel @Inject constructor(
|
|||
uiState.transformerUpdate(SwapQuoteLoadingStateTransformer)
|
||||
|
||||
modelScope.launch {
|
||||
val quotes = swapGroups.firstOrNull {
|
||||
val quotes = swapGroups.filter {
|
||||
it.currencyStatus.currency.id == toCryptoCurrency.id
|
||||
}?.providers?.map { provider ->
|
||||
}.flatMap {
|
||||
it.providers
|
||||
}.map { provider ->
|
||||
async {
|
||||
getSwapQuoteUseCase(
|
||||
userWallet = userWallet,
|
||||
|
|
@ -353,7 +335,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
SwapQuoteUM.Error(
|
||||
provider = provider,
|
||||
expressError = error,
|
||||
)
|
||||
).takeIf { error is ExpressError.AmountError }
|
||||
},
|
||||
ifRight = { quote: SwapQuoteModel ->
|
||||
convertToSwapProviderUM(
|
||||
|
|
@ -365,7 +347,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
},
|
||||
)
|
||||
}
|
||||
}?.awaitAll().orEmpty()
|
||||
}.awaitAll().filterNotNull()
|
||||
|
||||
uiState.transformerUpdate(
|
||||
SwapAmountSetQuotesTransformer(
|
||||
|
|
|
|||
|
|
@ -6,8 +6,11 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.express.models.ExpressRateType
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.swap.models.SwapCurrencies
|
||||
import com.tangem.domain.swap.models.SwapDirection
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
|
||||
|
|
@ -16,6 +19,34 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
|
||||
internal data object SwapAmountContentPreview {
|
||||
|
||||
private val cryptoCurrencyStatus = CryptoCurrencyStatus(
|
||||
currency = CryptoCurrency.Coin(
|
||||
id = CryptoCurrency.ID.fromValue("coin⟨BITCOIN⟩bitcoin"),
|
||||
network = Network(
|
||||
id = Network.ID(
|
||||
value = "bitcoin",
|
||||
derivationPath = Network.DerivationPath.None,
|
||||
),
|
||||
backendId = "bitcoin",
|
||||
name = "Bitcoin",
|
||||
currencySymbol = "BTC",
|
||||
derivationPath = Network.DerivationPath.None,
|
||||
isTestnet = false,
|
||||
standardType = Network.StandardType.Unspecified("bitcoin"),
|
||||
hasFiatFeeRate = false,
|
||||
canHandleTokens = false,
|
||||
transactionExtrasType = Network.TransactionExtrasType.NONE,
|
||||
|
||||
),
|
||||
name = "Bitcoin",
|
||||
symbol = "BTC",
|
||||
decimals = 8,
|
||||
iconUrl = "",
|
||||
isCustom = false,
|
||||
),
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
|
||||
val emptyState = SwapAmountUM.Content(
|
||||
isPrimaryButtonEnabled = false,
|
||||
primaryAmount = SwapAmountFieldUM.Empty(
|
||||
|
|
@ -29,8 +60,8 @@ internal data object SwapAmountContentPreview {
|
|||
swapCurrencies = SwapCurrencies.EMPTY,
|
||||
swapQuotes = persistentListOf(),
|
||||
selectedQuote = SwapQuoteUM.Empty,
|
||||
primaryCryptoCurrencyStatus = null,
|
||||
secondaryCryptoCurrencyStatus = null,
|
||||
primaryCryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
secondaryCryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
swapRateType = ExpressRateType.Float,
|
||||
appCurrency = AppCurrency.Default,
|
||||
)
|
||||
|
|
@ -65,8 +96,8 @@ internal data object SwapAmountContentPreview {
|
|||
swapCurrencies = SwapCurrencies.EMPTY,
|
||||
swapQuotes = persistentListOf(),
|
||||
selectedQuote = SwapQuoteUM.Empty,
|
||||
primaryCryptoCurrencyStatus = null,
|
||||
secondaryCryptoCurrencyStatus = null,
|
||||
primaryCryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
secondaryCryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
swapRateType = ExpressRateType.Float,
|
||||
isPrimaryButtonEnabled = true,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue