From 96e28b2d1f8456fe3514282a256ac7257a749d2c Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 7 Nov 2025 10:40:40 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../status/di/AccountStatusUseCaseModule.kt | 20 ++++++++++ .../ToggleTokenListGroupingUseCaseV2.kt | 16 ++++++-- .../ToggleTokenListGroupingUseCaseV2Test.kt | 22 +++++------ .../model/OrganizeTokensModel.kt | 37 ++++++++++++++++--- .../OrganizeTokensStateHolder.kt | 12 ++++++ .../utils/CryptoCurrenciesIdsResolver.kt | 26 +++++++++++++ 6 files changed, 112 insertions(+), 21 deletions(-) diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusUseCaseModule.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusUseCaseModule.kt index 311f9baa82..caf0fb6122 100644 --- a/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusUseCaseModule.kt +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/di/AccountStatusUseCaseModule.kt @@ -136,4 +136,24 @@ internal object AccountStatusUseCaseModule { parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default), ) } + + @Provides + @Singleton + fun provideToggleTokenListSortingUseCaseV2( + dispatchers: CoroutineDispatcherProvider, + ): ToggleTokenListSortingUseCaseV2 { + return ToggleTokenListSortingUseCaseV2( + dispatchers = dispatchers, + ) + } + + @Provides + @Singleton + fun provideToggleTokenListGroupingUseCaseV2( + dispatchers: CoroutineDispatcherProvider, + ): ToggleTokenListGroupingUseCaseV2 { + return ToggleTokenListGroupingUseCaseV2( + dispatchers = dispatchers, + ) + } } \ No newline at end of file diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/usecase/ToggleTokenListGroupingUseCaseV2.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/usecase/ToggleTokenListGroupingUseCaseV2.kt index 6a7d6de2ea..3583eee3d2 100644 --- a/domain/account/status/src/main/java/com/tangem/domain/account/status/usecase/ToggleTokenListGroupingUseCaseV2.kt +++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/usecase/ToggleTokenListGroupingUseCaseV2.kt @@ -4,6 +4,7 @@ import arrow.core.Either import arrow.core.raise.ensure import com.tangem.domain.account.models.AccountStatusList import com.tangem.domain.core.utils.eitherOn +import com.tangem.domain.models.TokensGroupType import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.account.AccountStatus import com.tangem.domain.models.tokenlist.TokenList @@ -42,11 +43,18 @@ class ToggleTokenListGroupingUseCaseV2( TokenListSortingError.TokenListIsLoading } - accountStatusList.copy( - accountStatuses = accountStatusList.accountStatuses.map { account -> - if (account !is AccountStatus.CryptoPortfolio) return@map account + val updatedAccountList = accountStatusList.accountStatuses.map { account -> + if (account !is AccountStatus.CryptoPortfolio) return@map account - account.copy(tokenList = account.tokenList.reverseGroupType()) + account.copy(tokenList = account.tokenList.reverseGroupType()) + } + + accountStatusList.copy( + accountStatuses = updatedAccountList, + groupType = when (updatedAccountList.firstOrNull()?.getCryptoTokenList()) { + is TokenList.GroupedByNetwork -> TokensGroupType.NETWORK + is TokenList.Ungrouped -> TokensGroupType.NONE + else -> accountStatusList.groupType }, ) } diff --git a/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/usecase/ToggleTokenListGroupingUseCaseV2Test.kt b/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/usecase/ToggleTokenListGroupingUseCaseV2Test.kt index fe325fcf79..016e88d152 100644 --- a/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/usecase/ToggleTokenListGroupingUseCaseV2Test.kt +++ b/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/usecase/ToggleTokenListGroupingUseCaseV2Test.kt @@ -64,7 +64,7 @@ class ToggleTokenListGroupingUseCaseV2Test { currencies = listOf(mockk(relaxed = true)), ) - val accountStatusList = createAccountStatusList(tokenList) + val accountStatusList = createAccountStatusList(tokenList, TokensGroupType.NONE) // Act val actual = useCase(accountStatusList) @@ -86,7 +86,7 @@ class ToggleTokenListGroupingUseCaseV2Test { sortedBy = TokensSortType.BALANCE, ) - val accountStatusList = createAccountStatusList(tokenList) + val accountStatusList = createAccountStatusList(tokenList, TokensGroupType.NONE) val updatedTokenList = createGroupedByNetwork( statuses = listOf( @@ -101,7 +101,7 @@ class ToggleTokenListGroupingUseCaseV2Test { val actual = useCase(accountStatusList) // Assert - val expected = createAccountStatusList(updatedTokenList).right() + val expected = createAccountStatusList(updatedTokenList, TokensGroupType.NETWORK).right() Truth.assertThat(actual).isEqualTo(expected) } @@ -116,7 +116,7 @@ class ToggleTokenListGroupingUseCaseV2Test { ), ) - val accountStatusList = createAccountStatusList(tokenList) + val accountStatusList = createAccountStatusList(tokenList, TokensGroupType.NONE) val updatedTokenList = createGroupedByNetwork( statuses = listOf( @@ -130,7 +130,7 @@ class ToggleTokenListGroupingUseCaseV2Test { val actual = useCase(accountStatusList) // Assert - val expected = createAccountStatusList(updatedTokenList).right() + val expected = createAccountStatusList(updatedTokenList, TokensGroupType.NETWORK).right() Truth.assertThat(actual).isEqualTo(expected) } @@ -146,7 +146,7 @@ class ToggleTokenListGroupingUseCaseV2Test { sortedBy = TokensSortType.BALANCE, ) - val accountStatusList = createAccountStatusList(tokenList) + val accountStatusList = createAccountStatusList(tokenList, TokensGroupType.NETWORK) val updatedTokenList = createUngrouped( statuses = listOf( @@ -161,7 +161,7 @@ class ToggleTokenListGroupingUseCaseV2Test { val actual = useCase(accountStatusList) // Assert - val expected = createAccountStatusList(updatedTokenList).right() + val expected = createAccountStatusList(updatedTokenList, TokensGroupType.NONE).right() Truth.assertThat(actual).isEqualTo(expected) } @@ -176,7 +176,7 @@ class ToggleTokenListGroupingUseCaseV2Test { ), ) - val accountStatusList = createAccountStatusList(tokenList) + val accountStatusList = createAccountStatusList(tokenList, TokensGroupType.NETWORK) val updatedTokenList = createUngrouped( statuses = listOf( @@ -190,11 +190,11 @@ class ToggleTokenListGroupingUseCaseV2Test { val actual = useCase(accountStatusList) // Assert - val expected = createAccountStatusList(updatedTokenList).right() + val expected = createAccountStatusList(updatedTokenList, TokensGroupType.NONE).right() Truth.assertThat(actual).isEqualTo(expected) } - private fun createAccountStatusList(tokenList: TokenList): AccountStatusList { + private fun createAccountStatusList(tokenList: TokenList, groupType: TokensGroupType): AccountStatusList { val accountStatus = AccountStatus.CryptoPortfolio( account = Account.CryptoPortfolio.createMainAccount(userWalletId), tokenList = tokenList, @@ -207,7 +207,7 @@ class ToggleTokenListGroupingUseCaseV2Test { totalAccounts = 1, totalFiatBalance = tokenList.totalFiatBalance, sortType = tokenList.sortedBy, - groupType = TokensGroupType.NONE, + groupType = groupType, ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/organizetokens/model/OrganizeTokensModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/organizetokens/model/OrganizeTokensModel.kt index 0ca835c3df..97057fd80e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/organizetokens/model/OrganizeTokensModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/organizetokens/model/OrganizeTokensModel.kt @@ -11,6 +11,9 @@ import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.models.AccountStatusList import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier +import com.tangem.domain.account.status.usecase.ApplyTokenListSortingUseCaseV2 +import com.tangem.domain.account.status.usecase.ToggleTokenListGroupingUseCaseV2 +import com.tangem.domain.account.status.usecase.ToggleTokenListSortingUseCaseV2 import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency @@ -55,6 +58,9 @@ internal class OrganizeTokensModel @Inject constructor( private val accountsFeatureToggles: AccountsFeatureToggles, private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase, private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier, + private val toggleTokenListGroupingUseCaseV2: ToggleTokenListGroupingUseCaseV2, + private val toggleTokenListSortingUseCaseV2: ToggleTokenListSortingUseCaseV2, + private val applyTokenListSortingUseCaseV2: ApplyTokenListSortingUseCaseV2, ) : Model(), OrganizeTokensIntents { private val selectedAppCurrencyFlow = createSelectedAppCurrencyFlow() @@ -82,6 +88,8 @@ internal class OrganizeTokensModel @Inject constructor( private var cachedTokenList: TokenList? = null private var cachedAccountStatusList: AccountStatusList? = null + private var isAccountsModeEnabled: Boolean = false + val uiState: StateFlow = stateHolder.stateFlow val onBack = MutableSharedFlow() @@ -112,7 +120,13 @@ internal class OrganizeTokensModel @Inject constructor( analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.ByBalance) modelScope.launch { - // todo account token list sorting + toggleTokenListSortingUseCaseV2(list).fold( + ifLeft = stateHolder::updateStateWithError, + ifRight = { + stateHolder.updateStateAfterTokenListSortingV2(it, isAccountsModeEnabled) + cachedAccountStatusList = it + }, + ) } } else { val list = cachedTokenList ?: return @@ -134,10 +148,18 @@ internal class OrganizeTokensModel @Inject constructor( override fun onGroupClick() { if (accountsFeatureToggles.isFeatureEnabled) { - cachedAccountStatusList ?: return + val list = cachedAccountStatusList ?: return + analyticsEventsHandler.send(PortfolioOrganizeTokensAnalyticsEvent.Group) + modelScope.launch { - // todo account token list grouping + toggleTokenListGroupingUseCaseV2(list).fold( + ifLeft = stateHolder::updateStateWithError, + ifRight = { + stateHolder.updateStateAfterTokenListSortingV2(it, isAccountsModeEnabled) + cachedAccountStatusList = it + }, + ) } } else { val list = cachedTokenList ?: return @@ -172,8 +194,11 @@ internal class OrganizeTokensModel @Inject constructor( isSortedByBalance = isSortedByBalance, ) - // val sorted = resolver.resolveV2(tokensListUM, cachedAccountStatusList) - TODO("Implement account token list sorting") + applyTokenListSortingUseCaseV2( + sortedTokensIdsByAccount = resolver.resolveV2(tokensListUM, cachedAccountStatusList), + isGroupedByNetwork = isGroupedByNetwork, + isSortedByBalance = isSortedByBalance, + ) } else { val listState = uiState.value.itemsState @@ -215,7 +240,7 @@ internal class OrganizeTokensModel @Inject constructor( SingleAccountStatusListProducer.Params(userWalletId), ) ?: return@launch - val isAccountsModeEnabled = isAccountsModeEnabledUseCase.invokeSync() + isAccountsModeEnabled = isAccountsModeEnabledUseCase.invokeSync() stateHolder.updateStateWithAccountList( accountStatusList = accountList, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt index 9c08e93374..f1eb7915cf 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt @@ -79,6 +79,18 @@ internal class OrganizeTokensStateHolder( } } + fun updateStateAfterTokenListSortingV2(accountStatusList: AccountStatusList, isAccountsModeEnabled: Boolean) { + updateState { + TokenListToStateConverterV2( + accountStatusList = accountStatusList, + isAccountsMode = isAccountsModeEnabled, + appCurrency = appCurrencyProvider(), + ).transform(this).copy( + scrollListToTop = triggeredEvent(Unit, ::consumeScrollListToTopEvent), + ) + } + } + fun updateStateToDisplayProgress() { updateState { inProgressStateConverter.convert(value = this) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt index 74bec371ac..d62385f7be 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt @@ -1,9 +1,13 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils +import com.tangem.domain.account.models.AccountStatusList +import com.tangem.domain.account.status.model.AccountCryptoCurrencies +import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.tokenlist.TokenList import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListUM internal class CryptoCurrenciesIdsResolver { @@ -29,4 +33,26 @@ internal class CryptoCurrenciesIdsResolver { currencyStatus?.currency?.id } } + + fun resolveV2(tokensListUM: OrganizeTokensListUM, accountStatusList: AccountStatusList?): AccountCryptoCurrencies { + val draggableTokens = when (tokensListUM) { + OrganizeTokensListUM.EmptyList -> return emptyMap() + is OrganizeTokensListUM.AccountList, + is OrganizeTokensListUM.TokensList, + -> tokensListUM.items.filterIsInstance() + } + + return accountStatusList?.accountStatuses + ?.filter { it.getCryptoTokenList() != TokenList.Empty } + ?.associate { accountStatus -> + val currencies = accountStatus.flattenCurrencies() + accountStatus.account as Account.CryptoPortfolio to draggableTokens + .asSequence() + .filter { it.accountId == accountStatus.account.accountId.value } + .mapNotNull { sortedToken -> + currencies.firstOrNull { it.currency.id.value == sortedToken.id }?.currency + } + .toList() + } ?: emptyMap() + } } \ No newline at end of file