From 2e7c487c462e3ac6899bce2cba668ead5dfe85e4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 20 Mar 2025 13:11:49 +0400 Subject: [PATCH 1/3] Updated on 2026-08-14 --- .../tap/di/domain/ManageTokensDomainModule.kt | 3 + .../tap/di/domain/MarketsDomainModule.kt | 5 ++ .../tap/di/domain/TokensDomainModule.kt | 4 ++ .../managetokens/SaveManagedTokensUseCase.kt | 12 ++++ domain/markets/build.gradle.kts | 1 + .../domain/markets/SaveMarketTokensUseCase.kt | 40 +++++++++-- .../tokens/AddCryptoCurrenciesUseCase.kt | 24 +++++++ .../CachedCurrenciesStatusesOperations.kt | 70 ++++++++++++++----- 8 files changed, 136 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt index c3520a41cf..8d32208885 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/ManageTokensDomainModule.kt @@ -7,6 +7,7 @@ import com.tangem.domain.managetokens.repository.ManageTokensRepository import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository +import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.walletmanager.WalletManagersFacade import dagger.Module import dagger.Provides @@ -67,6 +68,7 @@ internal object ManageTokensDomainModule { networksRepository: NetworksRepository, derivationsRepository: DerivationsRepository, stakingRepository: StakingRepository, + quotesRepository: QuotesRepository, ): SaveManagedTokensUseCase { return SaveManagedTokensUseCase( customTokensRepository = customTokensRepository, @@ -75,6 +77,7 @@ internal object ManageTokensDomainModule { networksRepository = networksRepository, derivationsRepository = derivationsRepository, stakingRepository = stakingRepository, + quotesRepository = quotesRepository, ) } diff --git a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt index 4498836964..72b1e2451c 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt @@ -4,6 +4,7 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.markets.* import com.tangem.domain.markets.repositories.MarketsTokenRepository +import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository @@ -57,12 +58,16 @@ object MarketsDomainModule { marketsTokenRepository: MarketsTokenRepository, currenciesRepository: CurrenciesRepository, networksRepository: NetworksRepository, + stakingRepository: StakingRepository, + quotesRepository: QuotesRepository, ): SaveMarketTokensUseCase { return SaveMarketTokensUseCase( derivationsRepository = derivationsRepository, marketsTokenRepository = marketsTokenRepository, currenciesRepository = currenciesRepository, networksRepository = networksRepository, + stakingRepository = stakingRepository, + quotesRepository = quotesRepository, ) } diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index b719015a7a..73ecbf344e 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -27,10 +27,14 @@ internal object TokensDomainModule { fun provideAddCryptoCurrenciesUseCase( currenciesRepository: CurrenciesRepository, networksRepository: NetworksRepository, + stakingRepository: StakingRepository, + quotesRepository: QuotesRepository, ): AddCryptoCurrenciesUseCase { return AddCryptoCurrenciesUseCase( currenciesRepository = currenciesRepository, networksRepository = networksRepository, + stakingRepository = stakingRepository, + quotesRepository = quotesRepository, ) } diff --git a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt index 4143444b7b..1cec10d0c1 100644 --- a/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt +++ b/domain/manage-tokens/src/main/kotlin/com/tangem/domain/managetokens/SaveManagedTokensUseCase.kt @@ -10,9 +10,11 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository +import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWalletId +@Suppress("LongParameterList") class SaveManagedTokensUseCase( private val customTokensRepository: CustomTokensRepository, private val walletManagersFacade: WalletManagersFacade, @@ -20,6 +22,7 @@ class SaveManagedTokensUseCase( private val networksRepository: NetworksRepository, private val derivationsRepository: DerivationsRepository, private val stakingRepository: StakingRepository, + private val quotesRepository: QuotesRepository, ) { suspend operator fun invoke( @@ -54,6 +57,8 @@ class SaveManagedTokensUseCase( ) refreshUpdatedYieldBalances(userWalletId, existingCurrencies) + + refreshUpdatedQuotes(addingCurrencies) } private suspend fun removeCurrenciesFromWalletManager( @@ -105,6 +110,13 @@ class SaveManagedTokensUseCase( ) } + private suspend fun refreshUpdatedQuotes(addedCurrencies: List) { + quotesRepository.fetchQuotes( + currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId }, + refresh = true, + ) + } + /** * Determines if the [existingCurrencies] list contains a coin that corresponds * to the given [network]. diff --git a/domain/markets/build.gradle.kts b/domain/markets/build.gradle.kts index b3d59216c4..f5a22e3d18 100644 --- a/domain/markets/build.gradle.kts +++ b/domain/markets/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { api(projects.domain.models) api(projects.domain.legacy) api(projects.domain.wallets) + api(projects.domain.staking) implementation(projects.domain.tokens.models) implementation(projects.domain.tokens) diff --git a/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt b/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt index f5b43a9bee..352fb55d85 100644 --- a/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt +++ b/domain/markets/src/main/java/com/tangem/domain/markets/SaveMarketTokensUseCase.kt @@ -3,10 +3,12 @@ package com.tangem.domain.markets import arrow.core.Either import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.markets.repositories.MarketsTokenRepository +import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository +import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.wallets.models.UserWalletId /** @@ -23,6 +25,8 @@ class SaveMarketTokensUseCase( private val marketsTokenRepository: MarketsTokenRepository, private val currenciesRepository: CurrenciesRepository, private val networksRepository: NetworksRepository, + private val stakingRepository: StakingRepository, + private val quotesRepository: QuotesRepository, ) { suspend operator fun invoke( @@ -60,11 +64,37 @@ class SaveMarketTokensUseCase( currenciesRepository.addCurrencies(userWalletId = userWalletId, currencies = addedCurrencies) - networksRepository.getNetworkStatusesSync( - userWalletId = userWalletId, - networks = addedCurrencies.map(CryptoCurrency::network).toSet(), - refresh = true, - ) + refreshUpdatedNetworks(userWalletId, addedCurrencies) + + refreshUpdatedYieldBalances(userWalletId, addedCurrencies) + + refreshUpdatedQuotes(addedCurrencies) } } + + private suspend fun refreshUpdatedNetworks(userWalletId: UserWalletId, addedCurrencies: List) { + networksRepository.getNetworkStatusesSync( + userWalletId = userWalletId, + networks = addedCurrencies.map(CryptoCurrency::network).toSet(), + refresh = true, + ) + } + + private suspend fun refreshUpdatedYieldBalances( + userWalletId: UserWalletId, + existingCurrencies: List, + ) { + stakingRepository.fetchMultiYieldBalance( + userWalletId = userWalletId, + cryptoCurrencies = existingCurrencies, + refresh = true, + ) + } + + private suspend fun refreshUpdatedQuotes(addedCurrencies: List) { + quotesRepository.fetchQuotes( + currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId }, + refresh = true, + ) + } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt index 01223c61a3..370a29ee8e 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/AddCryptoCurrenciesUseCase.kt @@ -5,10 +5,12 @@ import arrow.core.raise.Raise import arrow.core.raise.catch import arrow.core.raise.either import arrow.core.toNonEmptyListOrNull +import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository +import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.wallets.models.UserWalletId /** @@ -21,6 +23,8 @@ import com.tangem.domain.wallets.models.UserWalletId class AddCryptoCurrenciesUseCase( private val currenciesRepository: CurrenciesRepository, private val networksRepository: NetworksRepository, + private val stakingRepository: StakingRepository, + private val quotesRepository: QuotesRepository, ) { /** @@ -105,6 +109,8 @@ class AddCryptoCurrenciesUseCase( val tokenToAdd = createTokenCurrency(userWalletId, contractAddress, networkId) addCurrencies(userWalletId, listOf(tokenToAdd)) refreshUpdatedNetworks(userWalletId, listOf(tokenToAdd), existingCurrencies) + refreshUpdatedYieldBalances(userWalletId, existingCurrencies) + refreshUpdatedQuotes(existingCurrencies) tokenToAdd } @@ -139,6 +145,24 @@ class AddCryptoCurrenciesUseCase( } } + private suspend fun refreshUpdatedYieldBalances( + userWalletId: UserWalletId, + existingCurrencies: List, + ) { + stakingRepository.fetchMultiYieldBalance( + userWalletId = userWalletId, + cryptoCurrencies = existingCurrencies, + refresh = true, + ) + } + + private suspend fun refreshUpdatedQuotes(addedCurrencies: List) { + quotesRepository.fetchQuotes( + currenciesIds = addedCurrencies.mapNotNullTo(hashSetOf()) { it.id.rawCurrencyId }, + refresh = true, + ) + } + private suspend fun Raise.createTokenCurrency( userWalletId: UserWalletId, contractAddress: String, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt index 739ad63738..a14f37e601 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt @@ -7,6 +7,7 @@ import com.tangem.domain.core.lce.LceFlow import com.tangem.domain.core.lce.lce import com.tangem.domain.core.lce.lceFlow import com.tangem.domain.core.utils.EitherFlow +import com.tangem.domain.core.utils.lceContent import com.tangem.domain.core.utils.lceError import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.model.stakekit.YieldBalanceList @@ -44,30 +45,49 @@ class CachedCurrenciesStatusesOperations( userWalletId: UserWalletId, currenciesFlow: EitherFlow>, ): LceFlow> = lceFlow { - currenciesFlow.flatMapLatest { maybeCurrencies -> - val isUpdating = MutableStateFlow(value = true) + val prevStatuses = MutableStateFlow(value = emptyList()) - val nonEmptyCurrencies = maybeCurrencies + val isUpdating = MutableStateFlow(value = true) + val isFetchingStarted = MutableStateFlow(value = false) + + val nonEmptyCurrencies = currenciesFlow.mapNotNull { it.getOrNull() }.firstOrNull()?.toNonEmptyListOrNull() + + if (nonEmptyCurrencies != null) { + launch { + isFetchingStarted.value = true + + val (networks, currenciesIds) = getIds(nonEmptyCurrencies) + fetchComponents(userWalletId, networks, currenciesIds, nonEmptyCurrencies) + } + .invokeOnCompletion { isUpdating.value = false } + } + + currenciesFlow.flatMapLatest { maybeCurrencies -> + val currencies = maybeCurrencies .getOrElse { return@flatMapLatest flowOf(it.lceError()) } .toNonEmptyListOrNull() - if (nonEmptyCurrencies.isNullOrEmpty()) { + if (currencies.isNullOrEmpty()) { + prevStatuses.value = emptyList() return@flatMapLatest flowOf(TokenListError.EmptyTokens.lceError()) } // This is only 'true' when the flow here is empty, such as during initial loading if (isLoading.get()) { val loadingCurrencies = createCurrenciesStatuses( - currencies = nonEmptyCurrencies, + currencies = currencies, maybeNetworkStatuses = null, maybeQuotes = null, maybeYieldBalances = null, isUpdating = true, ) + + loadingCurrencies.getOrNull()?.let { prevStatuses.value = it } + send(loadingCurrencies) } - val (networks, currenciesIds) = getIds(nonEmptyCurrencies) + val (networks, currenciesIds) = getIds(currencies) fun createCurrenciesStatuses( maybeQuotes: Either>?, @@ -75,26 +95,46 @@ class CachedCurrenciesStatusesOperations( maybeYieldBalances: Either?, isUpdating: Boolean, ) = createCurrenciesStatuses( - currencies = nonEmptyCurrencies, + currencies = currencies, maybeQuotes = maybeQuotes, maybeNetworkStatuses = maybeNetworkStatuses, maybeYieldBalances = maybeYieldBalances, isUpdating = isUpdating, ) - launch { fetchComponents(userWalletId, networks, currenciesIds, nonEmptyCurrencies) } - .invokeOnCompletion { isUpdating.value = false } + // removing token + val prevStatusesValue = prevStatuses.value + if (prevStatusesValue.size - currencies.size == 1) { + val removed = prevStatusesValue.map { it.currency } - currencies + + return@flatMapLatest flowOf( + prevStatusesValue.filter { it.currency !in removed }.lceContent(), + ) + } + + if (!isFetchingStarted.value) { + launch { + isFetchingStarted.value = true + + fetchComponents(userWalletId, networks, currenciesIds, currencies) + } + .invokeOnCompletion { isUpdating.value = false } + } combine( flow = getQuotes(currenciesIds), flow2 = getNetworksStatuses(userWalletId, networks), - flow3 = getYieldBalances(userWalletId, nonEmptyCurrencies), + flow3 = getYieldBalances(userWalletId, currencies), flow4 = isUpdating, transform = ::createCurrenciesStatuses, ) .distinctUntilChanged() } - .onEach(::send) + .onEach { statusesLce -> + statusesLce.getOrNull()?.let { prevStatuses.value = it } + + send(statusesLce) + } .launchIn(scope = this) } @@ -112,13 +152,7 @@ class CachedCurrenciesStatusesOperations( val rawCurrenciesIds = currenciesIds.mapNotNullTo(mutableSetOf()) { it.rawCurrencyId } quotesRepository.fetchQuotes(rawCurrenciesIds) }, - async { - if (currencies.size == 1) { - stakingRepository.fetchSingleYieldBalance(userWalletId, currencies.first()) - } else { - stakingRepository.fetchMultiYieldBalance(userWalletId, currencies) - } - }, + async { stakingRepository.fetchMultiYieldBalance(userWalletId, currencies) }, ) } .map { } From af29fec09f7a2e6e84497d497ecabf446a34f5af Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 25 Mar 2025 17:48:40 +0300 Subject: [PATCH 2/3] Updated on 2026-08-14 --- .../feature/wallet/child/wallet/model/WalletModel.kt | 10 ++++++++++ .../presentation/wallet/state/model/WalletEvent.kt | 2 ++ .../state/transformers/ScrollToWalletTransformer.kt | 7 ++++++- .../wallet/presentation/wallet/ui/WalletEventEffect.kt | 4 ++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt index 3152303d57..8f748fd22b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/WalletModel.kt @@ -442,6 +442,16 @@ internal class WalletModel @Inject constructor( // Should not show scroll animation if WalletScreen isn't in the background. if (screenLifecycleProvider.isBackgroundState.value) { onConsume() + stateHolder.update( + ScrollToWalletTransformer( + prevIndex = prevIndex, + newIndex = newIndex, + currentStateProvider = Provider(action = stateHolder::value), + withScrollAnimation = false, + stateUpdater = { newState -> stateHolder.update { newState } }, + onConsume = {}, + ), + ) } else { stateHolder.update( ScrollToWalletTransformer( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletEvent.kt index ee8ed9bee9..662fdca3de 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletEvent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletEvent.kt @@ -9,6 +9,8 @@ internal sealed class WalletEvent { /** Change wallet with animation. Using [prevIndex] and [newIndex] to calculate offset */ data class ChangeWallet(val prevIndex: Int, val newIndex: Int) : WalletEvent() + data class ChangeWalletWithoutScroll(val newIndex: Int) : WalletEvent() + data class ShowError(val text: TextReference) : WalletEvent() data class ShowAlert(val state: WalletAlertState) : WalletEvent() diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ScrollToWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ScrollToWalletTransformer.kt index 1bbd651f80..4504e5f4d1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ScrollToWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ScrollToWalletTransformer.kt @@ -10,6 +10,7 @@ internal class ScrollToWalletTransformer( private val prevIndex: Int, private val newIndex: Int, private val currentStateProvider: Provider, + private val withScrollAnimation: Boolean = true, private val stateUpdater: (WalletScreenState) -> Unit, private val onConsume: () -> Unit = {}, ) : WalletScreenStateTransformer { @@ -17,7 +18,11 @@ internal class ScrollToWalletTransformer( override fun transform(prevState: WalletScreenState): WalletScreenState { return prevState.copy( event = triggeredEvent( - data = WalletEvent.ChangeWallet(prevIndex = prevIndex, newIndex = newIndex), + data = if (withScrollAnimation) { + WalletEvent.ChangeWallet(prevIndex = prevIndex, newIndex = newIndex) + } else { + WalletEvent.ChangeWalletWithoutScroll(newIndex = newIndex) + }, onConsume = { stateUpdater( currentStateProvider().copy(event = consumedEvent()), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt index a5e177dec1..6553a234d3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt @@ -36,6 +36,10 @@ internal fun WalletEventEffect( onAutoScrollSet() walletsListState.animateScrollByIndex(prevIndex = value.prevIndex, newIndex = value.newIndex) } + is WalletEvent.ChangeWalletWithoutScroll -> { + onAutoScrollSet() + walletsListState.scrollToItem(value.newIndex) + } is WalletEvent.ShowError -> { snackbarHostState.showSnackbar(message = value.text.resolveReference(resources)) } From 53504be151723bf73e207fb07134cdfd2ff96058 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 25 Mar 2025 15:16:11 +0000 Subject: [PATCH 3/3] Updated on 2026-08-14 --- gradle/tangem_dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml index 58cb7cc642..fc082393ac 100644 --- a/gradle/tangem_dependencies.toml +++ b/gradle/tangem_dependencies.toml @@ -1,7 +1,7 @@ [versions] -tangemBlockchainSdk = "release-app_5.22-987" +tangemBlockchainSdk = "develop-989" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "release-app_5.22-440" +tangemCardSdk = "develop-444" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem-developments8" #tangemVico = "0.0.1" # Keep it! - used for local builds ^