diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt index b482191a87..beb46d4941 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponentParams.kt @@ -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 + abstract val primaryCryptoCurrencyStatusFlow: StateFlow + abstract val secondaryCryptoCurrency: CryptoCurrency? data class AmountParams( override val amountUM: SwapAmountUM, override val analyticsCategoryName: String, override val userWallet: UserWallet, override val isBalanceHidingFlow: StateFlow, - override val appCurrency: AppCurrency, override val swapDirection: SwapDirection, - val primaryCryptoCurrency: CryptoCurrency, - val secondaryCryptoCurrency: CryptoCurrency?, + override val primaryCryptoCurrencyStatusFlow: StateFlow, + override val secondaryCryptoCurrency: CryptoCurrency?, val callback: SwapAmountComponent.ModelCallback, val currentRoute: Flow, ) : SwapAmountComponentParams() @@ -37,10 +36,9 @@ internal sealed class SwapAmountComponentParams { override val analyticsCategoryName: String, override val userWallet: UserWallet, override val isBalanceHidingFlow: StateFlow, - override val appCurrency: AppCurrency, override val swapDirection: SwapDirection, - val primaryCryptoCurrencyStatus: CryptoCurrencyStatus, - val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus?, + override val primaryCryptoCurrencyStatusFlow: StateFlow, + override val secondaryCryptoCurrency: CryptoCurrency?, val blockClickEnableFlow: StateFlow, ) : SwapAmountComponentParams() } \ 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 8313317b76..b8461d6736 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 @@ -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( diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt index 6db113e583..596cecef01 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountContentPreview.kt @@ -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, )