From 18d4a01642fd09cbe513757aa8fa0cc703cc0d52 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 6 Feb 2026 11:13:31 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/WalletsDomainModule.kt | 8 ++++ .../DefaultUserWalletsListRepository.kt | 43 +++++++++++++++++ .../UserWalletsPublicInformationRepository.kt | 4 ++ ...tUserWalletsPublicInformationRepository.kt | 15 ++++++ .../wallets/UserWalletTransformAction.kt | 35 ++++++++++++++ .../wallets/UserWalletsListRepository.kt | 8 ++++ .../ApplyUserWalletListSortingUseCase.kt | 46 +++++++++++++++++++ 7 files changed, 159 insertions(+) create mode 100644 domain/common/src/main/java/com/tangem/domain/common/wallets/UserWalletTransformAction.kt create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ApplyUserWalletListSortingUseCase.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt index 6e58009dd5..b3c453e6c5 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt @@ -398,4 +398,12 @@ internal object WalletsDomainModule { fun provideSyncWalletWithRemoteUseCase(walletsRepository: WalletsRepository): SyncWalletWithRemoteUseCase { return SyncWalletWithRemoteUseCase(walletsRepository = walletsRepository) } + + @Provides + @Singleton + fun provideApplyUserWalletListSortingUseCase( + userWalletsListRepository: UserWalletsListRepository, + ): ApplyUserWalletListSortingUseCase { + return ApplyUserWalletListSortingUseCase(userWalletsListRepository = userWalletsListRepository) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt index 414ea8a0b3..f307b8653d 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt @@ -13,6 +13,7 @@ import com.tangem.core.analytics.utils.TrackingContextProxy import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.getSyncOrDefault +import com.tangem.domain.common.wallets.UserWalletTransformAction import com.tangem.domain.common.wallets.UserWalletsListRepository import com.tangem.domain.common.wallets.UserWalletsListRepository.LockMethod import com.tangem.domain.common.wallets.error.* @@ -31,6 +32,8 @@ import com.tangem.sdk.api.TangemSdkManager import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey import com.tangem.tap.domain.userWalletList.utils.encryptionKey import com.tangem.tap.domain.userWalletList.utils.lock +import com.tangem.tap.domain.userWalletList.utils.publicInformation +import com.tangem.tap.domain.userWalletList.utils.sensitiveInformation import com.tangem.tap.domain.userWalletList.utils.toUserWallets import com.tangem.tap.domain.userWalletList.utils.updateWith import com.tangem.utils.Provider @@ -38,10 +41,12 @@ import com.tangem.utils.ProviderSuspend import com.tangem.utils.coroutines.runSuspendCatching import com.tangem.utils.extensions.addOrReplace import com.tangem.utils.extensions.indexOfFirstOrNull +import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext @Suppress("LongParameterList", "LargeClass") internal class DefaultUserWalletsListRepository( @@ -384,6 +389,44 @@ internal class DefaultUserWalletsListRepository( return userWallets.any { it.walletId !in unsecuredWalletIds } } + override suspend fun transform(action: UserWalletTransformAction) { + val wallets = userWalletsSync() + val walletsMap = wallets.associateBy { it.walletId } + val transformedWallets = action.transform(wallets) + val transformedWalletsMap = transformedWallets.associateBy { it.walletId } + + require(walletsMap.keys == transformedWalletsMap.keys) { + "The transformation action must not change the set of wallet IDs." + + "Original IDs: ${walletsMap.keys}, Transformed IDs: ${transformedWalletsMap.keys}" + } + + updateWallets { transformedWallets } + + withContext(NonCancellable) { + if (savePersistentInformation()) { + publicInformationRepository.transform { + transformedWallets.map { wallet -> wallet.publicInformation } + } + + val changedSensitiveInfoWallets = wallets.filter { wallet -> + val transformedWallet = transformedWalletsMap[wallet.walletId] + transformedWallet != null && transformedWallet.sensitiveInformation != wallet.sensitiveInformation + } + + changedSensitiveInfoWallets.forEach { wallet -> + if (wallet.isLocked.not()) { + sensitiveInformationRepository.save(wallet, wallet.encryptionKey) + } + + checkForUpgradeAndDeleteHotWalletIfNeeded( + newUserWallet = wallet, + oldUserWallet = walletsMap[wallet.walletId] ?: error("This should never happen"), + ) + } + } + } + } + private suspend fun checkForUpgradeAndDeleteHotWalletIfNeeded( newUserWallet: UserWallet, oldUserWallet: UserWallet, diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsPublicInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsPublicInformationRepository.kt index 6daa482e36..2497e8224a 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsPublicInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsPublicInformationRepository.kt @@ -12,4 +12,8 @@ internal interface UserWalletsPublicInformationRepository { suspend fun delete(walletIds: List): CompletionResult suspend fun clear(): CompletionResult + + suspend fun transform( + block: (List?) -> List?, + ): CompletionResult } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt index c3e8f86072..f2e3f0fcea 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt @@ -77,6 +77,21 @@ internal class DefaultUserWalletsPublicInformationRepository( } } + override suspend fun transform( + block: (List?) -> List?, + ): CompletionResult { + return withContext(Dispatchers.IO) { + getAll().flatMap { currentInfo -> + val transformed = block(currentInfo) + if (transformed != null) { + save(transformed) + } else { + CompletionResult.Success(Unit) + } + } + } + } + @JvmName("saveWithPublicInformation") private suspend fun save(publicInformation: List): CompletionResult = catching { withContext(Dispatchers.IO) { diff --git a/domain/common/src/main/java/com/tangem/domain/common/wallets/UserWalletTransformAction.kt b/domain/common/src/main/java/com/tangem/domain/common/wallets/UserWalletTransformAction.kt new file mode 100644 index 0000000000..ddf77dce94 --- /dev/null +++ b/domain/common/src/main/java/com/tangem/domain/common/wallets/UserWalletTransformAction.kt @@ -0,0 +1,35 @@ +package com.tangem.domain.common.wallets + +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.UserWalletId + +/** + * Sealed class representing actions that can transform the user wallets list. + */ +sealed class UserWalletTransformAction { + + /** + * The transformation block to apply to the current wallets list. + */ + abstract fun transform(list: List): List + + /** + * Reorders user wallets + * + * @param action The transformation block that takes the current list of user wallets + * and returns a list of user wallet IDs in the desired order. + */ + data class Reorder( + private val action: (List) -> List, + ) : UserWalletTransformAction() { + + override fun transform(list: List): List { + val walletById = list.associateBy { it.walletId } + val newOrderIds = action(list) + require(walletById.keys == newOrderIds.toSet()) { + "Reorder action must return the same set of wallet IDs as the input list" + } + return newOrderIds.mapNotNull { walletById[it] } + } + } +} \ No newline at end of file diff --git a/domain/common/src/main/java/com/tangem/domain/common/wallets/UserWalletsListRepository.kt b/domain/common/src/main/java/com/tangem/domain/common/wallets/UserWalletsListRepository.kt index 52df2eb43d..c9f9aecb00 100644 --- a/domain/common/src/main/java/com/tangem/domain/common/wallets/UserWalletsListRepository.kt +++ b/domain/common/src/main/java/com/tangem/domain/common/wallets/UserWalletsListRepository.kt @@ -123,6 +123,14 @@ interface UserWalletsListRepository { */ suspend fun clearPersistentData() + /** + * Transforms user wallets list according to the provided action. + * + * @param action The transformation action to apply. + * @throws IllegalArgumentException if the transformation action is invalid (e.g. reordering with missing or extra wallet IDs). + */ + suspend fun transform(action: UserWalletTransformAction) + /** * Checks if there are any secured wallets (wallets that are not locked with [LockMethod.NoLock]). */ diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ApplyUserWalletListSortingUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ApplyUserWalletListSortingUseCase.kt new file mode 100644 index 0000000000..4e3ac597d4 --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ApplyUserWalletListSortingUseCase.kt @@ -0,0 +1,46 @@ +package com.tangem.domain.wallets.usecase + +import arrow.core.Either +import arrow.core.raise.catch +import arrow.core.raise.either +import arrow.core.raise.ensure +import com.tangem.domain.common.wallets.UserWalletTransformAction +import com.tangem.domain.common.wallets.UserWalletsListRepository +import com.tangem.domain.models.wallet.UserWalletId + +/** + * Use case to apply a specific sorting order to a list of user wallets. + * + * @property userWalletsListRepository Repository for managing user wallets list. + */ +class ApplyUserWalletListSortingUseCase( + private val userWalletsListRepository: UserWalletsListRepository, +) { + + /** + * Applies the sorting order of the provided list of user wallet IDs. + * + * @param userWalletIds List of [UserWalletId] representing the desired order. + * @return Either an [Error] or Unit on successful completion. + */ + suspend operator fun invoke(userWalletIds: List): Either = either { + ensure(userWalletIds.size > 1) { Error.UnableToSortSingleWallet } + catch( + block = { + userWalletsListRepository.transform(UserWalletTransformAction.Reorder { userWalletIds }) + }, + catch = { raise(Error.ReorderError) }, + ) + } + + /** + * Sealed interface representing possible errors that can occur during the application + * of user wallet list sorting. + */ + sealed interface Error { + + data object UnableToSortSingleWallet : Error + + data object ReorderError : Error + } +} \ No newline at end of file