Updated on 2026-08-14
This commit is contained in:
commit
23175bb2fb
6 changed files with 87 additions and 42 deletions
|
|
@ -74,7 +74,7 @@ internal class MercuryoService(private val environment: MercuryoEnvironment) : E
|
|||
.appendQueryParameter("address", walletAddress)
|
||||
.appendQueryParameter("signature", signature(walletAddress))
|
||||
.appendQueryParameter("fix_currency", "true")
|
||||
.appendQueryParameter("return_url", ExchangeUrlBuilder.SUCCESS_URL)
|
||||
.appendQueryParameter("redirect_url", ExchangeUrlBuilder.SUCCESS_URL)
|
||||
if (isDarkTheme) builder.appendQueryParameter("theme", "1inch")
|
||||
|
||||
blockchain.mercuryoNetwork()?.let {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ import com.tangem.domain.tokens.model.CryptoCurrency
|
|||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.collections.set
|
||||
|
||||
class ApplyTokenListSortingUseCase(
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
|
|
@ -25,11 +27,17 @@ class ApplyTokenListSortingUseCase(
|
|||
isGroupedByNetwork: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
): Either<TokenListSortingError, Unit> {
|
||||
return withContext(dispatchers.default) {
|
||||
either {
|
||||
return either {
|
||||
val storedCurrencies = getCurrencies(userWalletId)
|
||||
val isSortingTypeChanged = checkIsCurrenciesSortedByBalance(userWalletId) != isSortedByBalance
|
||||
val isGroupingTypeChanged = checkIsCurrenciesGroupedByNetwork(userWalletId) != isGroupedByNetwork
|
||||
|
||||
val sortedCurrencies = sortTokens(sortedTokensIds, storedCurrencies)
|
||||
|
||||
if (storedCurrencies != sortedCurrencies || isSortingTypeChanged || isGroupingTypeChanged) {
|
||||
applySorting(
|
||||
userWalletId = userWalletId,
|
||||
tokens = sortTokens(sortedTokensIds, getCurrencies(userWalletId)),
|
||||
currencies = sortedCurrencies,
|
||||
isGrouped = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
|
|
@ -37,17 +45,29 @@ class ApplyTokenListSortingUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<TokenListSortingError>.checkIsCurrenciesSortedByBalance(userWalletId: UserWalletId) =
|
||||
catch(
|
||||
block = { currenciesRepository.isTokensSortedByBalance(userWalletId).firstOrNull() ?: false },
|
||||
catch = { raise(TokenListSortingError.DataError(it)) },
|
||||
)
|
||||
|
||||
private suspend fun Raise<TokenListSortingError>.checkIsCurrenciesGroupedByNetwork(userWalletId: UserWalletId) =
|
||||
catch(
|
||||
block = { currenciesRepository.isTokensGrouped(userWalletId).firstOrNull() ?: false },
|
||||
catch = { raise(TokenListSortingError.DataError(it)) },
|
||||
)
|
||||
|
||||
private suspend fun Raise<TokenListSortingError>.sortTokens(
|
||||
sortedTokensIds: List<CryptoCurrency.ID>,
|
||||
unsortedTokens: List<CryptoCurrency>,
|
||||
sortedCurrenciesIds: List<CryptoCurrency.ID>,
|
||||
unsortedCurrencies: List<CryptoCurrency>,
|
||||
): List<CryptoCurrency> = withContext(dispatchers.default) {
|
||||
val nonEmptySortedTokensIds = ensureNotNull(sortedTokensIds.toNonEmptySetOrNull()) {
|
||||
val nonEmptySortedTokensIds = ensureNotNull(sortedCurrenciesIds.toNonEmptySetOrNull()) {
|
||||
TokenListSortingError.TokenListIsEmpty
|
||||
}
|
||||
|
||||
val sortedTokens = sortedMapOf<Int, CryptoCurrency>()
|
||||
|
||||
unsortedTokens.distinct().forEach { currency ->
|
||||
unsortedCurrencies.distinct().forEach { currency ->
|
||||
val index = nonEmptySortedTokensIds.indexOfFirst { currencyId ->
|
||||
currencyId == currency.id
|
||||
}
|
||||
|
|
@ -79,12 +99,12 @@ class ApplyTokenListSortingUseCase(
|
|||
|
||||
private suspend fun Raise<TokenListSortingError>.applySorting(
|
||||
userWalletId: UserWalletId,
|
||||
tokens: List<CryptoCurrency>,
|
||||
currencies: List<CryptoCurrency>,
|
||||
isGrouped: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
) = withContext(dispatchers.io) {
|
||||
catch(
|
||||
block = { currenciesRepository.saveTokens(userWalletId, tokens, isGrouped, isSortedByBalance) },
|
||||
block = { currenciesRepository.saveTokens(userWalletId, currencies, isGrouped, isSortedByBalance) },
|
||||
catch = { raise(TokenListSortingError.DataError(it)) },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ internal class ApplyTokenListSortingUseCaseTest {
|
|||
// When
|
||||
val result = useCase(
|
||||
userWalletId = userWalletId,
|
||||
sortedTokensIds = MockTokens.tokens.map { it.id },
|
||||
sortedTokensIds = MockTokens.tokens.map { it.id }.sortedByDescending { it.value },
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -147,9 +147,7 @@ internal class OrganizeTokensViewModel @Inject constructor(
|
|||
ifLeft = stateHolder::updateStateWithError,
|
||||
ifRight = {
|
||||
stateHolder.updateStateToHideProgress()
|
||||
withContext(
|
||||
dispatchers.main,
|
||||
) { router.popBackStack() }
|
||||
withContext(dispatchers.main) { router.popBackStack() }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,14 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListErrorTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
internal typealias MaybeTokenListFlow = Flow<Either<TokenListError, TokenList>>
|
||||
|
|
@ -32,24 +35,25 @@ internal abstract class BasicTokenListSubscriber(
|
|||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
protected abstract fun tokenListFlow(): MaybeTokenListFlow
|
||||
private val sendAnalyticsJobHolder = JobHolder()
|
||||
private val onTokenListReceivedJobHolder = JobHolder()
|
||||
|
||||
protected open suspend fun onTokenListReceived(tokenList: TokenList) { /* no-op */
|
||||
}
|
||||
protected abstract fun tokenListFlow(): MaybeTokenListFlow
|
||||
|
||||
override fun create(coroutineScope: CoroutineScope): Flow<*> {
|
||||
return combine(
|
||||
flow = tokenListFlow()
|
||||
.onEach { maybeTokenList ->
|
||||
val displayedState = stateHolder.getWalletStateIfSelected(userWallet.walletId)
|
||||
|
||||
tokenListAnalyticsSender.send(
|
||||
displayedUiState = displayedState,
|
||||
userWallet = userWallet,
|
||||
tokenList = maybeTokenList.getOrElse { return@onEach },
|
||||
)
|
||||
coroutineScope.launch {
|
||||
sendTokenListAnalytics(maybeTokenList)
|
||||
}.saveIn(sendAnalyticsJobHolder)
|
||||
}
|
||||
.distinctUntilChanged(),
|
||||
.distinctUntilChanged()
|
||||
.onEach { maybeTokenList ->
|
||||
coroutineScope.launch {
|
||||
onTokenListReceived(maybeTokenList)
|
||||
}.saveIn(onTokenListReceivedJobHolder)
|
||||
},
|
||||
flow2 = getSelectedAppCurrencyUseCase().distinctUntilChanged(),
|
||||
transform = { maybeTokenList, maybeAppCurrency ->
|
||||
val tokenList = maybeTokenList.getOrElse { e ->
|
||||
|
|
@ -64,11 +68,24 @@ internal abstract class BasicTokenListSubscriber(
|
|||
|
||||
updateContent(tokenList, appCurrency)
|
||||
walletWithFundsChecker.check(tokenList)
|
||||
onTokenListReceived(tokenList)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
protected open suspend fun onTokenListReceived(maybeTokenList: Either<TokenListError, TokenList>) {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
private suspend fun sendTokenListAnalytics(maybeTokenList: Either<TokenListError, TokenList>) {
|
||||
val displayedState = stateHolder.getWalletStateIfSelected(userWallet.walletId)
|
||||
|
||||
tokenListAnalyticsSender.send(
|
||||
displayedUiState = displayedState,
|
||||
userWallet = userWallet,
|
||||
tokenList = maybeTokenList.getOrElse { return },
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateContent(tokenList: TokenList, appCurrency: AppCurrency) {
|
||||
stateHolder.update(
|
||||
SetTokenListTransformer(
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
|
|
@ -31,28 +35,34 @@ internal class MultiWalletTokenListSubscriber(
|
|||
|
||||
override fun tokenListFlow(): MaybeTokenListFlow = getTokenListUseCase(userWallet.walletId)
|
||||
|
||||
override suspend fun onTokenListReceived(tokenList: TokenList) {
|
||||
updateSortingIfNeeded(tokenList)
|
||||
override suspend fun onTokenListReceived(maybeTokenList: Either<TokenListError, TokenList>) {
|
||||
updateSortingIfNeeded(maybeTokenList)
|
||||
}
|
||||
|
||||
private suspend fun updateSortingIfNeeded(tokenList: TokenList) {
|
||||
if (tokenList.totalFiatBalance is TokenList.FiatBalance.Loading ||
|
||||
tokenList.sortedBy == TokenList.SortType.NONE
|
||||
) {
|
||||
return
|
||||
}
|
||||
private suspend fun updateSortingIfNeeded(maybeTokenList: Either<TokenListError, TokenList>) {
|
||||
val tokenList = maybeTokenList.getOrElse { return }
|
||||
if (!checkNeedSorting(tokenList)) return
|
||||
|
||||
applyTokenListSortingUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
sortedTokensIds = when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { group ->
|
||||
group.currencies.map { it.currency.id }
|
||||
}
|
||||
is TokenList.Ungrouped -> tokenList.currencies.map { it.currency.id }
|
||||
is TokenList.Empty -> return
|
||||
},
|
||||
sortedTokensIds = getCurrenciesIds(tokenList),
|
||||
isGroupedByNetwork = tokenList is TokenList.GroupedByNetwork,
|
||||
isSortedByBalance = true,
|
||||
isSortedByBalance = tokenList.sortedBy == TokenList.SortType.BALANCE,
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkNeedSorting(tokenList: TokenList): Boolean {
|
||||
return tokenList.totalFiatBalance !is TokenList.FiatBalance.Loading &&
|
||||
tokenList.sortedBy == TokenList.SortType.BALANCE
|
||||
}
|
||||
|
||||
private fun getCurrenciesIds(tokenList: TokenList): List<CryptoCurrency.ID> {
|
||||
return when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { group ->
|
||||
group.currencies.map { it.currency.id }
|
||||
}
|
||||
is TokenList.Ungrouped -> tokenList.currencies.map { it.currency.id }
|
||||
is TokenList.Empty -> emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue