Updated on 2026-08-14

This commit is contained in:
Tangem 2024-09-16 17:05:19 +03:00
parent df9318faef
commit c1b426ef9f
9 changed files with 75 additions and 41 deletions

View file

@ -220,13 +220,16 @@ sealed class AppRoute(val path: String) : Route {
data class Swap(
val currency: CryptoCurrency,
val userWalletId: UserWalletId,
) : AppRoute(path = "/swap/${currency.id.value}/${userWalletId.stringValue}"), RouteBundleParams {
val isInitialReverseOrder: Boolean = false,
) : AppRoute(path = "/swap/${currency.id.value}/${userWalletId.stringValue}/$isInitialReverseOrder"),
RouteBundleParams {
override fun getBundle(): Bundle = bundle(serializer())
companion object {
const val CURRENCY_BUNDLE_KEY = "currency"
const val USER_WALLET_ID_KEY = "userWalletId"
const val IS_INITIAL_REVERSE_ORDER = "isInitialReverseOrder"
}
}

View file

@ -151,6 +151,7 @@ internal class TokenActionsHandler @AssistedInject constructor(
AppRoute.Swap(
currency = cryptoCurrencyData.status.currency,
userWalletId = cryptoCurrencyData.userWallet.walletId,
isInitialReverseOrder = true,
),
)
}

View file

@ -18,6 +18,14 @@ data class TokensDataStateExpress(
}
}
fun TokensDataStateExpress.getGroupWithReverse(isReverseFromTo: Boolean): CurrenciesGroup {
return if (isReverseFromTo) {
this.fromGroup
} else {
this.toGroup
}
}
data class CurrenciesGroup(
val available: List<CryptoCurrencySwapInfo>,
val unavailable: List<CryptoCurrencySwapInfo>,

View file

@ -2,31 +2,34 @@ package com.tangem.feature.swap.domain
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.swap.domain.models.ui.TokensDataStateExpress
import com.tangem.feature.swap.domain.models.ui.getGroupWithReverse
import java.math.BigDecimal
internal class DefaultInitialToCurrencyResolver(
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val swapTransactionRepository: SwapTransactionRepository,
) : InitialToCurrencyResolver {
override suspend fun tryGetFromCache(
userWallet: UserWallet,
initialCryptoCurrency: CryptoCurrency,
state: TokensDataStateExpress,
isReverseFromTo: Boolean,
): CryptoCurrencyStatus? {
val selectedId = getSelectedWalletSyncUseCase().getOrNull() ?: return null
val id = swapTransactionRepository.getLastSwappedCryptoCurrencyId(selectedId.walletId) ?: return null
val id = swapTransactionRepository.getLastSwappedCryptoCurrencyId(userWallet.walletId) ?: return null
return if (id != initialCryptoCurrency.id.value) {
state.toGroup.available.find { it.currencyStatus.currency.id.value == id }?.currencyStatus
val group = state.getGroupWithReverse(isReverseFromTo)
group.available.find { it.currencyStatus.currency.id.value == id }?.currencyStatus
} else {
null
}
}
override fun tryGetWithMaxAmount(state: TokensDataStateExpress): CryptoCurrencyStatus? {
return state.toGroup.available.maxByOrNull {
override fun tryGetWithMaxAmount(state: TokensDataStateExpress, isReverseFromTo: Boolean): CryptoCurrencyStatus? {
val group = state.getGroupWithReverse(isReverseFromTo)
return group.available.maxByOrNull {
it.currencyStatus.value.fiatAmount ?: BigDecimal.ZERO
}?.currencyStatus
}

View file

@ -2,14 +2,17 @@ package com.tangem.feature.swap.domain
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.swap.domain.models.ui.TokensDataStateExpress
interface InitialToCurrencyResolver {
suspend fun tryGetFromCache(
userWallet: UserWallet,
initialCryptoCurrency: CryptoCurrency,
state: TokensDataStateExpress,
isReverseFromTo: Boolean,
): CryptoCurrencyStatus?
fun tryGetWithMaxAmount(state: TokensDataStateExpress): CryptoCurrencyStatus?
fun tryGetWithMaxAmount(state: TokensDataStateExpress, isReverseFromTo: Boolean): CryptoCurrencyStatus?
}

View file

@ -79,9 +79,10 @@ interface SwapInteractor {
*/
fun getTokenBalance(token: CryptoCurrencyStatus): SwapAmount
suspend fun selectInitialCurrencyToSwap(
suspend fun getInitialCurrencyToSwap(
initialCryptoCurrency: CryptoCurrency,
state: TokensDataStateExpress,
isReverseFromTo: Boolean,
): CryptoCurrencyStatus?
fun getNativeToken(networkId: String): CryptoCurrency

View file

@ -965,13 +965,15 @@ internal class SwapInteractorImpl @AssistedInject constructor(
return SwapAmount(token.value.amount ?: BigDecimal.ZERO, token.currency.decimals)
}
override suspend fun selectInitialCurrencyToSwap(
override suspend fun getInitialCurrencyToSwap(
initialCryptoCurrency: CryptoCurrency,
state: TokensDataStateExpress,
isReverseFromTo: Boolean,
): CryptoCurrencyStatus? {
return initialToCurrencyResolver.tryGetFromCache(initialCryptoCurrency, state)
?: initialToCurrencyResolver.tryGetWithMaxAmount(state)
?: state.toGroup.available.firstOrNull()?.currencyStatus
val group = state.getGroupWithReverse(isReverseFromTo)
return initialToCurrencyResolver.tryGetFromCache(userWallet, initialCryptoCurrency, state, isReverseFromTo)
?: initialToCurrencyResolver.tryGetWithMaxAmount(state, isReverseFromTo)
?: group.available.firstOrNull()?.currencyStatus
}
override fun getNativeToken(networkId: String): CryptoCurrency {

View file

@ -5,8 +5,6 @@ import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.transaction.usecase.*
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.feature.swap.domain.*
import com.tangem.lib.crypto.TransactionManager
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -60,11 +58,9 @@ internal class SwapDomainModule {
@Provides
@Singleton
fun provideInitialToCurrencyResolver(
getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
swapTransactionRepository: SwapTransactionRepository,
): InitialToCurrencyResolver {
return DefaultInitialToCurrencyResolver(
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
swapTransactionRepository = swapTransactionRepository,
)
}

View file

@ -84,6 +84,9 @@ internal class SwapViewModel @Inject constructor(
?.unbundle(UserWalletId.serializer())
?: error("no expected parameter UserWalletId found")
private val isInitiallyReversed: Boolean = savedStateHandle.get<Boolean>(AppRoute.Swap.IS_INITIAL_REVERSE_ORDER)
?: false
private val swapInteractor = swapInteractorFactory.create(userWalletId)
private lateinit var initialCryptoCurrencyStatus: CryptoCurrencyStatus
@ -137,7 +140,7 @@ internal class SwapViewModel @Inject constructor(
uiState = stateBuilder.addAlert(uiState = uiState, onClick = swapRouter::back)
} else {
initialCryptoCurrencyStatus = cryptoCurrencyStatus
initTokens()
initTokens(isInitiallyReversed)
}
}
}
@ -180,18 +183,21 @@ internal class SwapViewModel @Inject constructor(
analyticsEventHandler.send(SwapEvents.ChooseTokenScreenOpened(availableTokens = isAnyAvailableTokens))
}
private fun initTokens() {
private fun initTokens(isReverseFromTo: Boolean) {
viewModelScope.launch(dispatchers.main) {
runCatching(dispatchers.io) {
swapInteractor.getTokensDataState(initialCryptoCurrency)
}.onSuccess { state ->
updateTokensState(state)
val selectedCurrency = swapInteractor.getInitialCurrencyToSwap(
initialCryptoCurrency = initialCryptoCurrency,
state = state,
isReverseFromTo = isReverseFromTo,
)
applyInitialTokenChoice(
state,
swapInteractor.selectInitialCurrencyToSwap(
initialCryptoCurrency,
state,
),
state = state,
selectedCurrency = selectedCurrency,
isReverseFromTo = isReverseFromTo,
)
(dataState.fromCryptoCurrency?.currency as? CryptoCurrency.Coin)?.let {
@ -215,6 +221,7 @@ internal class SwapViewModel @Inject constructor(
applyInitialTokenChoice(
state = TokensDataStateExpress.EMPTY,
selectedCurrency = null,
isReverseFromTo = isReverseFromTo,
)
uiState = stateBuilder.createInitialErrorState(
@ -227,33 +234,43 @@ internal class SwapViewModel @Inject constructor(
initialCryptoCurrency.network.backendId,
),
)
initTokens()
initTokens(isReverseFromTo)
}
}
}
}
private fun applyInitialTokenChoice(state: TokensDataStateExpress, selectedCurrency: CryptoCurrencyStatus?) {
val fromCurrencyStatus = initialCryptoCurrencyStatus
dataState = dataState.copy(
fromCryptoCurrency = fromCurrencyStatus,
toCryptoCurrency = selectedCurrency,
tokensDataState = state,
)
private fun applyInitialTokenChoice(
state: TokensDataStateExpress,
selectedCurrency: CryptoCurrencyStatus?,
isReverseFromTo: Boolean,
) {
// exceptional case
if (selectedCurrency == null) {
analyticsEventHandler.send(SwapEvents.NoticeNoAvailableTokensToSwap)
uiState = stateBuilder.createNoAvailableTokensToSwapState(
uiStateHolder = uiState,
fromToken = fromCurrencyStatus,
)
} else {
startLoadingQuotes(
fromToken = fromCurrencyStatus,
toToken = selectedCurrency,
amount = lastAmount.value,
toProvidersList = findSwapProviders(fromCurrencyStatus, selectedCurrency),
fromToken = initialCryptoCurrencyStatus,
)
return
}
isOrderReversed = isReverseFromTo
val (fromCurrencyStatus, toCurrencyStatus) = if (isOrderReversed) {
selectedCurrency to initialCryptoCurrencyStatus
} else {
initialCryptoCurrencyStatus to selectedCurrency
}
dataState = dataState.copy(
fromCryptoCurrency = fromCurrencyStatus,
toCryptoCurrency = toCurrencyStatus,
tokensDataState = state,
)
startLoadingQuotes(
fromToken = fromCurrencyStatus,
toToken = toCurrencyStatus,
amount = lastAmount.value,
toProvidersList = findSwapProviders(fromCurrencyStatus, toCurrencyStatus),
)
}
private fun updateTokensState(tokenDataState: TokensDataStateExpress) {