From ff1ac5d0e7b708f494385e44b686ab8541ebbb80 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 21 Feb 2024 13:42:29 +0100 Subject: [PATCH] Updated on 2026-08-14 --- .../viewmodels/AddCustomTokenViewModel.kt | 2 +- .../viewmodels/TokensListMigration.kt | 6 +- .../tokens/GetCryptoCurrenciesUseCase.kt | 13 ++- .../viewmodels/AddCustomTokenViewModel.kt | 2 +- .../viewmodels/ManageTokensViewModel.kt | 91 ++++++++++++------- .../presentation/viewmodel/SendViewModel.kt | 2 +- 6 files changed, 79 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt index 0825668edb..b15a5fdee5 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt @@ -87,7 +87,7 @@ internal class AddCustomTokenViewModel @Inject constructor( currentCryptoCurrencies = getSelectedWalletSyncUseCase().fold( ifLeft = { emptyList() }, ifRight = { selectedWallet -> - getCurrenciesUseCase(selectedWallet.walletId).fold( + getCurrenciesUseCase.getSync(selectedWallet.walletId).fold( ifLeft = { emptyList() }, ifRight = { it }, ) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt index f4f5e5e72d..0768165d5b 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt @@ -49,7 +49,11 @@ internal class TokensListMigration( is Either.Right -> { currentUserWallet = selectedWalletEither.value - when (val currenciesEither = getCurrenciesUseCase(userWalletId = selectedWalletEither.value.walletId)) { + when ( + val currenciesEither = getCurrenciesUseCase.getSync( + userWalletId = selectedWalletEither.value.walletId, + ) + ) { is Either.Left -> { Timber.e(currenciesEither.value.toString()) TokensListCryptoCurrencies(coins = emptyList(), tokens = emptyList()) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrenciesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrenciesUseCase.kt index 1ead96bd8a..f0c73552f7 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrenciesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrenciesUseCase.kt @@ -1,16 +1,27 @@ package com.tangem.domain.tokens import arrow.core.Either +import arrow.core.left import arrow.core.raise.catch import arrow.core.raise.either +import arrow.core.right import com.tangem.domain.tokens.error.GetCurrenciesError import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.models.UserWalletId +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.map class GetCryptoCurrenciesUseCase(private val currenciesRepository: CurrenciesRepository) { - suspend operator fun invoke( + operator fun invoke(userWalletId: UserWalletId): Flow>> { + return currenciesRepository.getMultiCurrencyWalletCurrenciesUpdates(userWalletId) + .map, Either>> { it.right() } + .catch { emit(GetCurrenciesError.DataError(it).left()) } + } + + suspend fun getSync( userWalletId: UserWalletId, refresh: Boolean = false, ): Either> { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt index 1efdf2911b..71cb37a5e9 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt @@ -424,7 +424,7 @@ internal class AddCustomTokenViewModel @Inject constructor( selectedWallet: UserWallet, cryptoCurrency: CryptoCurrency, ): Boolean { - val currenciesList = getCurrenciesUseCase(selectedWallet.walletId).getOrElse { emptyList() } + val currenciesList = getCurrenciesUseCase.getSync(selectedWallet.walletId).getOrElse { emptyList() } return when (cryptoCurrency) { is CryptoCurrency.Coin -> { currenciesList.any { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt index ad42c3159e..09ced5cf54 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt @@ -33,11 +33,15 @@ import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.Debouncer import com.tangem.utils.coroutines.Debouncer.Companion.DEFAULT_WAIT_TIME_MS +import com.tangem.utils.coroutines.JobHolder +import com.tangem.utils.coroutines.saveIn import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.* import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch -import kotlinx.coroutines.plus -import kotlinx.coroutines.withContext +import timber.log.Timber +import java.util.Collections +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject import kotlin.collections.set import kotlin.properties.Delegates @@ -62,8 +66,6 @@ internal class ManageTokensViewModel @Inject constructor( private val analyticsEventHandler: AnalyticsEventHandler, ) : ViewModel(), ManageTokensClickIntents, ManageTokensUiEvents { - private val debouncer = Debouncer() - private val stateFactory = ManageTokensStateFactory( currentStateProvider = Provider { uiState }, clickIntents = this, @@ -73,11 +75,17 @@ internal class ManageTokensViewModel @Inject constructor( var uiState: ManageTokensState by mutableStateOf(stateFactory.getInitialState(flowOf(PagingData.from(emptyList())))) private set - private var allAddedCurrencies: MutableList = mutableListOf() + private val currenciesListJobHolder: JobHolder = JobHolder() - private var wallets: List by Delegates.notNull() + private val debouncer = Debouncer() - private var addedCurrenciesByWallet: MutableMap> = mutableMapOf() + private var allAddedCurrencies: MutableList = Collections.synchronizedList( + mutableListOf(), + ) + + private var wallets: CopyOnWriteArrayList by Delegates.notNull() + + private var addedCurrenciesByWallet: MutableMap> = ConcurrentHashMap() private var selectedWallet: UserWallet? = null @@ -114,34 +122,53 @@ internal class ManageTokensViewModel @Inject constructor( getWalletsUseCase() .distinctUntilChanged() .collectLatest { userWallets -> - wallets = userWallets.filter { it.isMultiCurrency && !it.isLocked } - wallets.map { wallet -> - val currencies = getCurrenciesUseCase(wallet.walletId).fold( - ifLeft = { emptyList() }, - ifRight = { it }, - ) - allAddedCurrencies += currencies - addedCurrenciesByWallet[wallet] = currencies.toMutableList() - } - withContext(dispatchers.main) { - uiState = uiState.copy(tokens = getInitialTokensList()) - } - selectedWallet = getSelectedWalletSyncUseCase().fold( - ifLeft = { null }, - ifRight = { if (!it.isMultiCurrency || it.isLocked) null else it }, - ) - if (selectedWallet == null && wallets.isNotEmpty()) { - selectWalletUseCase(wallets.first().walletId) - selectedWallet = wallets.first() - } - updateDerivationNotificationState() - withContext(dispatchers.main) { - uiState = stateFactory.updateChooseWalletState(wallets, userWallets, selectedWallet) - } + launch { + subscribeToCurrencies(userWallets) + }.saveIn(currenciesListJobHolder) } } } + private suspend fun subscribeToCurrencies(userWallets: List) { + wallets = CopyOnWriteArrayList(userWallets.filter { it.isMultiCurrency && !it.isLocked }) + + combine(wallets.map { getCurrenciesUseCase.invoke(it.walletId).distinctUntilChanged() }) { + allAddedCurrencies.clear() + addedCurrenciesByWallet.clear() + + val walletsWithCurrencies = wallets.zip( + it.map { currencyList -> + currencyList.getOrElse { + Timber.e("Couldn't retrieve currency list") + emptyList() + } + }, + ) + + allAddedCurrencies = walletsWithCurrencies.flatMap { it.second }.toMutableList() + + walletsWithCurrencies.forEach { (wallet, currencies) -> + addedCurrenciesByWallet[wallet] = currencies.toMutableList() + } + + withContext(dispatchers.main) { + uiState = uiState.copy(tokens = getInitialTokensList()) + } + selectedWallet = getSelectedWalletSyncUseCase().fold( + ifLeft = { null }, + ifRight = { if (!it.isMultiCurrency || it.isLocked) null else it }, + ) + if (selectedWallet == null && wallets.isNotEmpty()) { + selectWalletUseCase(wallets.first().walletId) + selectedWallet = wallets.first() + } + updateDerivationNotificationState() + withContext(dispatchers.main) { + uiState = stateFactory.updateChooseWalletState(wallets, userWallets, selectedWallet) + } + }.collect() + } + private fun getInitialTokensList(searchText: String = ""): Flow> { return getGlobalTokenListUseCase(searchText = searchText).map { it.map { token -> tokenConverter.convert(token) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt index 8a0f1440e1..524d95dada 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt @@ -324,7 +324,7 @@ internal class SendViewModel @Inject constructor( .filterNot { it.walletId == userWalletId || it.isLocked } .map { wallet -> async(dispatchers.io) { - getCryptoCurrenciesUseCase(wallet.walletId) + getCryptoCurrenciesUseCase.getSync(wallet.walletId) .fold( ifRight = { currencyItem -> val walletCurrency = currencyItem.firstOrNull {