diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt index 82059ca4fd..e315ffa1ee 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt @@ -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 { - 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.checkIsCurrenciesSortedByBalance(userWalletId: UserWalletId) = + catch( + block = { currenciesRepository.isTokensSortedByBalance(userWalletId).firstOrNull() ?: false }, + catch = { raise(TokenListSortingError.DataError(it)) }, + ) + + private suspend fun Raise.checkIsCurrenciesGroupedByNetwork(userWalletId: UserWalletId) = + catch( + block = { currenciesRepository.isTokensGrouped(userWalletId).firstOrNull() ?: false }, + catch = { raise(TokenListSortingError.DataError(it)) }, + ) + private suspend fun Raise.sortTokens( - sortedTokensIds: List, - unsortedTokens: List, + sortedCurrenciesIds: List, + unsortedCurrencies: List, ): List = withContext(dispatchers.default) { - val nonEmptySortedTokensIds = ensureNotNull(sortedTokensIds.toNonEmptySetOrNull()) { + val nonEmptySortedTokensIds = ensureNotNull(sortedCurrenciesIds.toNonEmptySetOrNull()) { TokenListSortingError.TokenListIsEmpty } val sortedTokens = sortedMapOf() - 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.applySorting( userWalletId: UserWalletId, - tokens: List, + currencies: List, 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)) }, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt index a9e0e7b0c7..5342e0ece5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt @@ -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() } }, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt index d9cc56d227..0dcd95aa6a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt @@ -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> @@ -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) { + /* no-op */ + } + + private suspend fun sendTokenListAnalytics(maybeTokenList: Either) { + 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( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt index 8eb0af9ab2..1c53ebee84 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt @@ -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) { + 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) { + 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 { + 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() + } + } } \ No newline at end of file