Updated on 2026-08-14

This commit is contained in:
Tangem 2024-02-28 20:54:42 +03:00
commit 23175bb2fb
6 changed files with 87 additions and 42 deletions

View file

@ -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 {

View file

@ -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)) },
)
}

View file

@ -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,
)

View file

@ -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() }
},
)
}

View file

@ -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(

View file

@ -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()
}
}
}