Updated on 2026-08-14
This commit is contained in:
parent
ebb562b2a0
commit
ae759af1c7
14 changed files with 422 additions and 140 deletions
|
|
@ -142,22 +142,6 @@ internal object NFTDomainModule {
|
||||||
return GetWalletNFTEnabledUseCase(walletsRepository)
|
return GetWalletNFTEnabledUseCase(walletsRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideClearNFTCacheUseCase(
|
|
||||||
nftCleaner: NFTCleaner,
|
|
||||||
currenciesRepository: CurrenciesRepository,
|
|
||||||
accountsFeatureToggles: AccountsFeatureToggles,
|
|
||||||
singleAccountListSupplier: SingleAccountListSupplier,
|
|
||||||
): ObserveAndClearNFTCacheIfNeedUseCase {
|
|
||||||
return ObserveAndClearNFTCacheIfNeedUseCase(
|
|
||||||
nftCleaner = nftCleaner,
|
|
||||||
currenciesRepository = currenciesRepository,
|
|
||||||
accountsFeatureToggles = accountsFeatureToggles,
|
|
||||||
singleAccountListSupplier = singleAccountListSupplier,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideGetNftCurrencyUseCase(nftRepository: NFTRepository): GetNFTCurrencyUseCase {
|
fun provideGetNftCurrencyUseCase(nftRepository: NFTRepository): GetNFTCurrencyUseCase {
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,11 @@ import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.networks.utils.NetworksCleaner
|
import com.tangem.domain.networks.utils.NetworksCleaner
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import kotlinx.coroutines.coroutineScope
|
import com.tangem.utils.coroutines.runSuspendCatching
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.awaitAll
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of [NetworksCleaner].
|
* Default implementation of [NetworksCleaner].
|
||||||
|
|
@ -27,33 +29,51 @@ internal class DefaultNetworksCleaner(
|
||||||
) : NetworksCleaner {
|
) : NetworksCleaner {
|
||||||
|
|
||||||
override suspend fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
override suspend fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||||
|
if (currencies.isEmpty()) {
|
||||||
|
Timber.d("No currencies to clear for wallet: $userWalletId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
withContext(dispatchers.default) {
|
withContext(dispatchers.default) {
|
||||||
val (networks, tokens) = currencies.partitionByType()
|
val (networks, tokens) = currencies.partitionByType()
|
||||||
|
|
||||||
coroutineScope {
|
awaitAll(
|
||||||
launch { cleanStore(userWalletId = userWalletId, networks = networks) }
|
async { clearStatusesStore(userWalletId = userWalletId, networks = networks) },
|
||||||
launch { cleanWalletManager(userWalletId = userWalletId, networks = networks, tokens = tokens) }
|
async { clearBlockchainSDK(userWalletId = userWalletId, networks = networks, tokens = tokens) },
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun cleanStore(userWalletId: UserWalletId, networks: Set<Network>) {
|
private suspend fun clearStatusesStore(userWalletId: UserWalletId, networks: Set<Network>) {
|
||||||
if (networks.isNotEmpty()) {
|
if (networks.isNotEmpty()) {
|
||||||
networksStatusesStore.clear(userWalletId = userWalletId, networks = networks)
|
runSuspendCatching {
|
||||||
|
networksStatusesStore.clear(userWalletId = userWalletId, networks = networks)
|
||||||
|
}
|
||||||
|
.onFailure { Timber.e(it, "Failed to clear network statuses for wallet: $userWalletId") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun cleanWalletManager(
|
private suspend fun clearBlockchainSDK(
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
networks: Set<Network>,
|
networks: Set<Network>,
|
||||||
tokens: Set<CryptoCurrency.Token>,
|
tokens: Set<CryptoCurrency.Token>,
|
||||||
) {
|
) {
|
||||||
if (networks.isNotEmpty()) {
|
if (networks.isNotEmpty()) {
|
||||||
walletManagersFacade.remove(userWalletId = userWalletId, networks = networks)
|
runSuspendCatching {
|
||||||
|
walletManagersFacade.remove(userWalletId = userWalletId, networks = networks)
|
||||||
|
}
|
||||||
|
.onFailure {
|
||||||
|
Timber.e(it, "Failed to remove networks from Blockchain SDK for wallet: $userWalletId")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokens.isNotEmpty()) {
|
if (tokens.isNotEmpty()) {
|
||||||
walletManagersFacade.removeTokens(userWalletId = userWalletId, tokens = tokens)
|
runSuspendCatching {
|
||||||
|
walletManagersFacade.removeTokens(userWalletId = userWalletId, tokens = tokens)
|
||||||
|
}
|
||||||
|
.onFailure {
|
||||||
|
Timber.e(it, "Failed to remove tokens from Blockchain SDK for wallet: $userWalletId")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||||
import io.mockk.clearMocks
|
import io.mockk.clearMocks
|
||||||
|
import io.mockk.coEvery
|
||||||
import io.mockk.coVerifyOrder
|
import io.mockk.coVerifyOrder
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
|
@ -45,7 +46,7 @@ class DefaultNetworksCleanerTest {
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
networksStatusesStore.clear(userWalletId, setOf(network))
|
networksStatusesStore.clear(userWalletId = userWalletId, networks = setOf(network))
|
||||||
walletManagersFacade.remove(userWalletId = userWalletId, networks = setOf(network))
|
walletManagersFacade.remove(userWalletId = userWalletId, networks = setOf(network))
|
||||||
walletManagersFacade.removeTokens(userWalletId = userWalletId, tokens = setOf(token))
|
walletManagersFacade.removeTokens(userWalletId = userWalletId, tokens = setOf(token))
|
||||||
}
|
}
|
||||||
|
|
@ -66,12 +67,15 @@ class DefaultNetworksCleanerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `should clear only networks when there are no tokens`() = runTest {
|
fun `should clear only networks when there are no tokens`() = runTest {
|
||||||
|
// Arrange
|
||||||
val currencies = listOf(coin)
|
val currencies = listOf(coin)
|
||||||
|
|
||||||
|
// Act
|
||||||
cleaner(userWalletId = userWalletId, currencies = currencies)
|
cleaner(userWalletId = userWalletId, currencies = currencies)
|
||||||
|
|
||||||
|
// Assert
|
||||||
coVerifyOrder {
|
coVerifyOrder {
|
||||||
networksStatusesStore.clear(userWalletId, setOf(network))
|
networksStatusesStore.clear(userWalletId = userWalletId, networks = setOf(network))
|
||||||
walletManagersFacade.remove(userWalletId = userWalletId, networks = setOf(network))
|
walletManagersFacade.remove(userWalletId = userWalletId, networks = setOf(network))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,4 +102,27 @@ class DefaultNetworksCleanerTest {
|
||||||
walletManagersFacade.remove(userWalletId = any(), networks = any())
|
walletManagersFacade.remove(userWalletId = any(), networks = any())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should handle exception during cleaning`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val currencies = listOf(coin, token)
|
||||||
|
|
||||||
|
val exception = Exception("Test exception")
|
||||||
|
coEvery { networksStatusesStore.clear(userWalletId, setOf(network)) } throws exception
|
||||||
|
coEvery { walletManagersFacade.remove(userWalletId = userWalletId, networks = setOf(network)) } throws exception
|
||||||
|
coEvery {
|
||||||
|
walletManagersFacade.removeTokens(userWalletId = userWalletId, tokens = setOf(token))
|
||||||
|
} throws exception
|
||||||
|
|
||||||
|
// Act
|
||||||
|
cleaner(userWalletId = userWalletId, currencies = currencies)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
coVerifyOrder {
|
||||||
|
networksStatusesStore.clear(userWalletId = userWalletId, networks = setOf(network))
|
||||||
|
walletManagersFacade.remove(userWalletId = userWalletId, networks = setOf(network))
|
||||||
|
walletManagersFacade.removeTokens(userWalletId = userWalletId, tokens = setOf(token))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||||
import com.tangem.datasource.local.token.P2PEthPoolVaultsStore
|
import com.tangem.datasource.local.token.P2PEthPoolVaultsStore
|
||||||
import com.tangem.datasource.local.token.StakingActionsStore
|
import com.tangem.datasource.local.token.StakingActionsStore
|
||||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||||
|
import com.tangem.domain.staking.StakingIdFactory
|
||||||
import com.tangem.domain.staking.repositories.*
|
import com.tangem.domain.staking.repositories.*
|
||||||
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
import com.tangem.domain.staking.toggles.StakingFeatureToggles
|
||||||
import com.tangem.domain.staking.utils.StakingCleaner
|
import com.tangem.domain.staking.utils.StakingCleaner
|
||||||
|
|
@ -136,10 +137,12 @@ internal object StakingDataModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideStakingCleaner(
|
fun provideStakingCleaner(
|
||||||
|
stakingIdFactory: StakingIdFactory,
|
||||||
stakingBalancesStore: StakingBalancesStore,
|
stakingBalancesStore: StakingBalancesStore,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
): StakingCleaner {
|
): StakingCleaner {
|
||||||
return DefaultStakingCleaner(
|
return DefaultStakingCleaner(
|
||||||
|
stakingIdFactory = stakingIdFactory,
|
||||||
stakingBalancesStore = stakingBalancesStore,
|
stakingBalancesStore = stakingBalancesStore,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,17 @@
|
||||||
package com.tangem.data.staking.utils
|
package com.tangem.data.staking.utils
|
||||||
|
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.data.staking.store.StakingBalancesStore
|
import com.tangem.data.staking.store.StakingBalancesStore
|
||||||
import com.tangem.domain.models.staking.StakingID
|
import com.tangem.domain.models.staking.StakingID
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.staking.StakingIdFactory
|
||||||
import com.tangem.domain.staking.utils.StakingCleaner
|
import com.tangem.domain.staking.utils.StakingCleaner
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import com.tangem.utils.coroutines.runSuspendCatching
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of [StakingCleaner].
|
* Default implementation of [StakingCleaner].
|
||||||
|
|
@ -15,15 +22,46 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
[REDACTED_AUTHOR]
|
[REDACTED_AUTHOR]
|
||||||
*/
|
*/
|
||||||
internal class DefaultStakingCleaner(
|
internal class DefaultStakingCleaner(
|
||||||
|
private val stakingIdFactory: StakingIdFactory,
|
||||||
private val stakingBalancesStore: StakingBalancesStore,
|
private val stakingBalancesStore: StakingBalancesStore,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
) : StakingCleaner {
|
) : StakingCleaner {
|
||||||
|
|
||||||
override suspend fun invoke(userWalletId: UserWalletId, stakingIds: Set<StakingID>) {
|
override suspend fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||||
if (stakingIds.isEmpty()) return
|
if (currencies.isEmpty()) {
|
||||||
|
Timber.d("No currencies to clear for wallet: $userWalletId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
with(dispatchers.default) {
|
val stakingIds = currencies.mapNotNullTo(hashSetOf()) {
|
||||||
stakingBalancesStore.clear(userWalletId, stakingIds)
|
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stakingIds.isEmpty()) {
|
||||||
|
Timber.d("All currencies have no stakingIds to clear for wallet: $userWalletId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
invoke(userWalletId = userWalletId, stakingIds = stakingIds)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun invoke(userWalletId: UserWalletId, stakingIds: Set<StakingID>) {
|
||||||
|
if (stakingIds.isEmpty()) {
|
||||||
|
Timber.d("No stakingIds to clear for wallet: $userWalletId")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(dispatchers.default) {
|
||||||
|
awaitAll(
|
||||||
|
async { clearStatusesStore(userWalletId = userWalletId, stakingIds = stakingIds) },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun clearStatusesStore(userWalletId: UserWalletId, stakingIds: Set<StakingID>) {
|
||||||
|
runSuspendCatching {
|
||||||
|
stakingBalancesStore.clear(userWalletId, stakingIds)
|
||||||
|
}
|
||||||
|
.onFailure { Timber.e(it, "Failed to clear yield balance statuses for wallet: $userWalletId") }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,55 +1,146 @@
|
||||||
package com.tangem.data.staking.utils
|
package com.tangem.data.staking.utils
|
||||||
|
|
||||||
|
import arrow.core.right
|
||||||
|
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||||
import com.tangem.data.staking.store.StakingBalancesStore
|
import com.tangem.data.staking.store.StakingBalancesStore
|
||||||
import com.tangem.domain.models.staking.StakingID
|
import com.tangem.domain.models.staking.StakingID
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.staking.StakingIdFactory
|
||||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||||
import io.mockk.clearMocks
|
import io.mockk.clearMocks
|
||||||
|
import io.mockk.coEvery
|
||||||
import io.mockk.coVerifyOrder
|
import io.mockk.coVerifyOrder
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Nested
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class DefaultStakingCleanerTest {
|
class DefaultStakingCleanerTest {
|
||||||
|
|
||||||
|
private val stakingIdFactory = mockk<StakingIdFactory>(relaxed = true)
|
||||||
private val stakingBalancesStore = mockk<StakingBalancesStore>(relaxed = true)
|
private val stakingBalancesStore = mockk<StakingBalancesStore>(relaxed = true)
|
||||||
private val cleaner = DefaultStakingCleaner(
|
private val cleaner = DefaultStakingCleaner(
|
||||||
|
stakingIdFactory = stakingIdFactory,
|
||||||
stakingBalancesStore = stakingBalancesStore,
|
stakingBalancesStore = stakingBalancesStore,
|
||||||
dispatchers = TestingCoroutineDispatcherProvider(),
|
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private val userWalletId = UserWalletId("011")
|
private val userWalletId = UserWalletId("011")
|
||||||
private val stakingIds = setOf(
|
|
||||||
StakingID(integrationId = StakingIntegrationID.StakeKit.Coin.Cardano.value, address = "0x1"),
|
|
||||||
)
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
clearMocks(stakingBalancesStore)
|
clearMocks(stakingIdFactory, stakingBalancesStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
fun `should clear yields balances when called`() = runTest {
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
// Act
|
inner class ClearByStakingIds {
|
||||||
cleaner(userWalletId = userWalletId, stakingIds = stakingIds)
|
|
||||||
|
|
||||||
// Assert
|
private val stakingIds = setOf(
|
||||||
coVerifyOrder {
|
StakingID(integrationId = StakingIntegrationID.StakeKit.Coin.Cardano.value, address = "0x1"),
|
||||||
stakingBalancesStore.clear(userWalletId = userWalletId, stakingIds = stakingIds)
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should clear yields balances when called`() = runTest {
|
||||||
|
// Act
|
||||||
|
cleaner(userWalletId = userWalletId, stakingIds = stakingIds)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
coVerifyOrder {
|
||||||
|
stakingBalancesStore.clear(userWalletId = userWalletId, stakingIds = stakingIds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should handle empty stakingIds`() = runTest {
|
||||||
|
// Act
|
||||||
|
cleaner(userWalletId = userWalletId, stakingIds = emptySet())
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
coVerifyOrder(inverse = true) {
|
||||||
|
stakingBalancesStore.clear(userWalletId = any(), stakingIds = any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should catch exception from stakingBalancesStore and not throw`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
coEvery { stakingBalancesStore.clear(userWalletId, stakingIds) } throws Exception()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
cleaner(userWalletId = userWalletId, stakingIds = stakingIds)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
coVerifyOrder {
|
||||||
|
stakingBalancesStore.clear(userWalletId = userWalletId, stakingIds = stakingIds)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Nested
|
||||||
fun `should handle empty stakingIds`() = runTest {
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
// Act
|
inner class ClearByCurrencies {
|
||||||
cleaner(userWalletId = userWalletId, stakingIds = emptySet())
|
|
||||||
|
|
||||||
// Assert
|
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||||
coVerifyOrder(inverse = true) {
|
private val coin = cryptoCurrencyFactory.ethereum
|
||||||
stakingBalancesStore.clear(userWalletId = any(), stakingIds = any())
|
|
||||||
|
@Test
|
||||||
|
fun `should clear yields balances when called with single currency`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val stakingId = StakingID(
|
||||||
|
integrationId = "stake_kit_coin_eth",
|
||||||
|
address = "0xabc",
|
||||||
|
)
|
||||||
|
|
||||||
|
coEvery { stakingIdFactory.create(userWalletId, coin) } returns stakingId.right()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
cleaner(userWalletId = userWalletId, currency = coin)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
coVerifyOrder {
|
||||||
|
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = coin)
|
||||||
|
stakingBalancesStore.clear(userWalletId = userWalletId, stakingIds = setOf(stakingId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should handle empty list of currencies`() = runTest {
|
||||||
|
// Act
|
||||||
|
cleaner(userWalletId = userWalletId, currencies = emptyList())
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
coVerifyOrder(inverse = true) {
|
||||||
|
stakingIdFactory.create(userWalletId = any(), cryptoCurrency = any())
|
||||||
|
stakingBalancesStore.clear(userWalletId = any(), stakingIds = any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should catch exception from stakingBalancesStore and not throw`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val stakingId = StakingID(
|
||||||
|
integrationId = "stake_kit_coin_eth",
|
||||||
|
address = "0xabc",
|
||||||
|
)
|
||||||
|
|
||||||
|
coEvery { stakingIdFactory.create(userWalletId, coin) } returns stakingId.right()
|
||||||
|
coEvery {
|
||||||
|
stakingBalancesStore.clear(userWalletId = userWalletId, stakingIds = setOf(stakingId))
|
||||||
|
} throws Exception()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
cleaner(userWalletId = userWalletId, currency = coin)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
coVerifyOrder {
|
||||||
|
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = coin)
|
||||||
|
stakingBalancesStore.clear(userWalletId = userWalletId, stakingIds = setOf(stakingId))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,11 +5,13 @@ import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier
|
||||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||||
import com.tangem.domain.account.status.usecase.*
|
import com.tangem.domain.account.status.usecase.*
|
||||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||||
|
import com.tangem.domain.account.status.utils.CryptoCurrencyMetadataCleaner
|
||||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||||
import com.tangem.domain.express.ExpressServiceFetcher
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||||
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
|
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
|
||||||
import com.tangem.domain.networks.utils.NetworksCleaner
|
import com.tangem.domain.networks.utils.NetworksCleaner
|
||||||
|
import com.tangem.domain.nft.utils.NFTCleaner
|
||||||
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
|
import com.tangem.domain.quotes.multi.MultiQuoteStatusFetcher
|
||||||
import com.tangem.domain.staking.StakingIdFactory
|
import com.tangem.domain.staking.StakingIdFactory
|
||||||
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
|
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
|
||||||
|
|
@ -98,9 +100,7 @@ internal object AccountStatusUseCaseModule {
|
||||||
derivationsRepository: DerivationsRepository,
|
derivationsRepository: DerivationsRepository,
|
||||||
walletManagersFacade: WalletManagersFacade,
|
walletManagersFacade: WalletManagersFacade,
|
||||||
cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
||||||
stakingIdFactory: StakingIdFactory,
|
cryptoCurrencyMetadataCleaner: CryptoCurrencyMetadataCleaner,
|
||||||
networksCleaner: NetworksCleaner,
|
|
||||||
stakingCleaner: StakingCleaner,
|
|
||||||
expressServiceFetcher: ExpressServiceFetcher,
|
expressServiceFetcher: ExpressServiceFetcher,
|
||||||
dispatchers: CoroutineDispatcherProvider,
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
): ManageCryptoCurrenciesUseCase {
|
): ManageCryptoCurrenciesUseCase {
|
||||||
|
|
@ -111,9 +111,7 @@ internal object AccountStatusUseCaseModule {
|
||||||
derivationsRepository = derivationsRepository,
|
derivationsRepository = derivationsRepository,
|
||||||
walletManagersFacade = walletManagersFacade,
|
walletManagersFacade = walletManagersFacade,
|
||||||
cryptoCurrencyBalanceFetcher = cryptoCurrencyBalanceFetcher,
|
cryptoCurrencyBalanceFetcher = cryptoCurrencyBalanceFetcher,
|
||||||
stakingIdFactory = stakingIdFactory,
|
cryptoCurrencyMetadataCleaner = cryptoCurrencyMetadataCleaner,
|
||||||
networksCleaner = networksCleaner,
|
|
||||||
stakingCleaner = stakingCleaner,
|
|
||||||
expressServiceFetcher = expressServiceFetcher,
|
expressServiceFetcher = expressServiceFetcher,
|
||||||
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default),
|
parallelUpdatingScope = CoroutineScope(SupervisorJob() + dispatchers.default),
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
|
|
@ -157,4 +155,20 @@ internal object AccountStatusUseCaseModule {
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideCryptoCurrencyMetadataCleaner(
|
||||||
|
networksCleaner: NetworksCleaner,
|
||||||
|
stakingCleaner: StakingCleaner,
|
||||||
|
nftCleaner: NFTCleaner,
|
||||||
|
dispatchers: CoroutineDispatcherProvider,
|
||||||
|
): CryptoCurrencyMetadataCleaner {
|
||||||
|
return CryptoCurrencyMetadataCleaner(
|
||||||
|
networksCleaner = networksCleaner,
|
||||||
|
stakingCleaner = stakingCleaner,
|
||||||
|
nftCleaner = nftCleaner,
|
||||||
|
dispatchers = dispatchers,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import com.tangem.domain.account.repository.AccountsCRUDRepository
|
||||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||||
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
import com.tangem.domain.account.status.utils.CryptoCurrencyBalanceFetcher
|
||||||
|
import com.tangem.domain.account.status.utils.CryptoCurrencyMetadataCleaner
|
||||||
import com.tangem.domain.core.utils.eitherOn
|
import com.tangem.domain.core.utils.eitherOn
|
||||||
import com.tangem.domain.express.ExpressServiceFetcher
|
import com.tangem.domain.express.ExpressServiceFetcher
|
||||||
import com.tangem.domain.express.models.ExpressAsset
|
import com.tangem.domain.express.models.ExpressAsset
|
||||||
|
|
@ -17,9 +18,6 @@ import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.networks.utils.NetworksCleaner
|
|
||||||
import com.tangem.domain.staking.StakingIdFactory
|
|
||||||
import com.tangem.domain.staking.utils.StakingCleaner
|
|
||||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||||
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
import com.tangem.domain.wallets.derivations.DerivationsRepository
|
||||||
|
|
@ -36,9 +34,7 @@ import timber.log.Timber
|
||||||
* @property currenciesRepository Repository for managing currencies.
|
* @property currenciesRepository Repository for managing currencies.
|
||||||
* @property derivationsRepository Repository for deriving public keys.
|
* @property derivationsRepository Repository for deriving public keys.
|
||||||
* @property cryptoCurrencyBalanceFetcher Fetcher for updating crypto currency balances.
|
* @property cryptoCurrencyBalanceFetcher Fetcher for updating crypto currency balances.
|
||||||
* @property stakingIdFactory Factory for creating staking IDs.
|
* @property cryptoCurrencyMetadataCleaner Cleaner for removing metadata of deleted currencies.
|
||||||
* @property networksCleaner Cleaner for removing obsolete network data.
|
|
||||||
* @property stakingCleaner Cleaner for removing obsolete staking data.
|
|
||||||
* @property expressServiceFetcher Fetcher for updating express service data.
|
* @property expressServiceFetcher Fetcher for updating express service data.
|
||||||
* @property parallelUpdatingScope Coroutine scope for parallel updates.
|
* @property parallelUpdatingScope Coroutine scope for parallel updates.
|
||||||
* @property dispatchers Coroutine dispatchers for managing threading.
|
* @property dispatchers Coroutine dispatchers for managing threading.
|
||||||
|
|
@ -53,9 +49,7 @@ class ManageCryptoCurrenciesUseCase(
|
||||||
private val derivationsRepository: DerivationsRepository,
|
private val derivationsRepository: DerivationsRepository,
|
||||||
private val walletManagersFacade: WalletManagersFacade,
|
private val walletManagersFacade: WalletManagersFacade,
|
||||||
private val cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
private val cryptoCurrencyBalanceFetcher: CryptoCurrencyBalanceFetcher,
|
||||||
private val stakingIdFactory: StakingIdFactory,
|
private val cryptoCurrencyMetadataCleaner: CryptoCurrencyMetadataCleaner,
|
||||||
private val networksCleaner: NetworksCleaner,
|
|
||||||
private val stakingCleaner: StakingCleaner,
|
|
||||||
private val expressServiceFetcher: ExpressServiceFetcher,
|
private val expressServiceFetcher: ExpressServiceFetcher,
|
||||||
private val parallelUpdatingScope: CoroutineScope,
|
private val parallelUpdatingScope: CoroutineScope,
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
|
@ -306,21 +300,12 @@ class ManageCryptoCurrenciesUseCase(
|
||||||
if (currencies.isEmpty()) return
|
if (currencies.isEmpty()) return
|
||||||
|
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
listOf(
|
launch {
|
||||||
launch { networksCleaner(userWalletId = userWalletId, currencies = currencies) },
|
cryptoCurrencyMetadataCleaner(userWalletId = userWalletId, currencies = currencies)
|
||||||
launch { clearStaking(userWalletId = userWalletId, currencies = currencies) },
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun clearStaking(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
|
||||||
val stakingIds = currencies.mapNotNullTo(hashSetOf()) {
|
|
||||||
stakingIdFactory.create(userWalletId = userWalletId, cryptoCurrency = it).getOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
stakingCleaner(userWalletId = userWalletId, stakingIds = stakingIds)
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class TempID(
|
private data class TempID(
|
||||||
val networkId: String,
|
val networkId: String,
|
||||||
val derivationPath: Network.DerivationPath,
|
val derivationPath: Network.DerivationPath,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.tangem.domain.account.status.utils
|
||||||
|
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.networks.utils.NetworksCleaner
|
||||||
|
import com.tangem.domain.nft.utils.NFTCleaner
|
||||||
|
import com.tangem.domain.staking.utils.StakingCleaner
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to clean up all data related to a specific cryptocurrency in a user's wallet.
|
||||||
|
*
|
||||||
|
* @property networksCleaner Utility to clean network-related data.
|
||||||
|
* @property stakingCleaner Utility to clean staking-related data.
|
||||||
|
* @property nftCleaner Utility to clean NFT-related data.
|
||||||
|
* @property dispatchers Coroutine dispatchers for managing threading.
|
||||||
|
*
|
||||||
|
* @see <a href="https://www.notion.so/tangem/2be5d34eb67880008f95cb779dcafac9">Notion</a>
|
||||||
|
*
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
class CryptoCurrencyMetadataCleaner(
|
||||||
|
private val networksCleaner: NetworksCleaner,
|
||||||
|
private val stakingCleaner: StakingCleaner,
|
||||||
|
private val nftCleaner: NFTCleaner,
|
||||||
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up data for a single cryptocurrency in the specified user wallet.
|
||||||
|
*
|
||||||
|
* @param userWalletId The ID of the user's wallet.
|
||||||
|
* @param currency The cryptocurrency to be cleaned.
|
||||||
|
*/
|
||||||
|
suspend operator fun invoke(userWalletId: UserWalletId, currency: CryptoCurrency) {
|
||||||
|
invoke(userWalletId = userWalletId, currencies = listOf(currency))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up data for multiple cryptocurrencies in the specified user wallet.
|
||||||
|
*
|
||||||
|
* @param userWalletId The ID of the user's wallet.
|
||||||
|
* @param currencies The list of cryptocurrencies to be cleaned.
|
||||||
|
*/
|
||||||
|
suspend operator fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||||
|
if (currencies.isEmpty()) return
|
||||||
|
|
||||||
|
return withContext(dispatchers.default) {
|
||||||
|
awaitAll(
|
||||||
|
async { networksCleaner(userWalletId = userWalletId, currencies = currencies) },
|
||||||
|
async { clearStaking(userWalletId = userWalletId, currencies = currencies) },
|
||||||
|
async { clearNFTs(userWalletId = userWalletId, currencies = currencies) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun clearStaking(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||||
|
stakingCleaner(userWalletId = userWalletId, currencies = currencies)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun clearNFTs(userWalletId: UserWalletId, currencies: List<CryptoCurrency>) {
|
||||||
|
val networks = currencies.mapTo(destination = hashSetOf(), transform = CryptoCurrency::network)
|
||||||
|
|
||||||
|
nftCleaner(userWalletId = userWalletId, networks = networks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.tangem.domain.account.status.utils
|
||||||
|
|
||||||
|
import com.tangem.blockchain.common.Blockchain
|
||||||
|
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.networks.utils.NetworksCleaner
|
||||||
|
import com.tangem.domain.nft.utils.NFTCleaner
|
||||||
|
import com.tangem.domain.staking.utils.StakingCleaner
|
||||||
|
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||||
|
import io.mockk.clearMocks
|
||||||
|
import io.mockk.coVerify
|
||||||
|
import io.mockk.mockk
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
|
||||||
|
/**
|
||||||
|
[REDACTED_AUTHOR]
|
||||||
|
*/
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class CryptoCurrencyCleanerTest {
|
||||||
|
|
||||||
|
private val networksCleaner: NetworksCleaner = mockk(relaxUnitFun = true)
|
||||||
|
private val stakingCleaner: StakingCleaner = mockk(relaxUnitFun = true)
|
||||||
|
private val nftCleaner: NFTCleaner = mockk(relaxUnitFun = true)
|
||||||
|
|
||||||
|
private val cleaner = CryptoCurrencyMetadataCleaner(
|
||||||
|
networksCleaner = networksCleaner,
|
||||||
|
stakingCleaner = stakingCleaner,
|
||||||
|
nftCleaner = nftCleaner,
|
||||||
|
dispatchers = TestingCoroutineDispatcherProvider(),
|
||||||
|
)
|
||||||
|
|
||||||
|
private val userWalletId = UserWalletId("011")
|
||||||
|
|
||||||
|
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
|
||||||
|
private val coin = cryptoCurrencyFactory.ethereum
|
||||||
|
private val token = cryptoCurrencyFactory.createToken(Blockchain.Ethereum)
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
fun tearDown() {
|
||||||
|
clearMocks(networksCleaner, stakingCleaner, nftCleaner)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should not call cleaners when currencies list is empty`() = runTest {
|
||||||
|
// Act
|
||||||
|
cleaner(userWalletId = userWalletId, currencies = emptyList())
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
coVerify(inverse = true) {
|
||||||
|
networksCleaner(userWalletId = any(), currencies = any())
|
||||||
|
stakingCleaner(userWalletId = any(), currencies = any())
|
||||||
|
nftCleaner(userWalletId = any(), networks = any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `should call both cleaners when currencies list is not empty`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val currencies = listOf(coin, token)
|
||||||
|
|
||||||
|
// Act
|
||||||
|
cleaner(userWalletId = userWalletId, currencies = currencies)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
coVerify {
|
||||||
|
networksCleaner(userWalletId = userWalletId, currencies = currencies)
|
||||||
|
stakingCleaner(userWalletId = userWalletId, currencies = currencies)
|
||||||
|
nftCleaner(userWalletId = userWalletId, networks = setOf(coin.network, token.network))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,16 @@ import com.tangem.domain.models.wallet.UserWalletId
|
||||||
*/
|
*/
|
||||||
interface NetworksCleaner {
|
interface NetworksCleaner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up network-related data for the given [userWalletId] and [currency].
|
||||||
|
*
|
||||||
|
* @param userWalletId The ID of the user wallet for which to clean up data.
|
||||||
|
* @param currency The cryptocurrency whose associated network data should be cleaned.
|
||||||
|
*/
|
||||||
|
suspend operator fun invoke(userWalletId: UserWalletId, currency: CryptoCurrency) {
|
||||||
|
invoke(userWalletId = userWalletId, currencies = listOf(currency))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans up network-related data for the given [userWalletId] and list of [currencies].
|
* Cleans up network-related data for the given [userWalletId] and list of [currencies].
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
package com.tangem.domain.nft
|
|
||||||
|
|
||||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
|
||||||
import com.tangem.domain.account.models.AccountList
|
|
||||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
|
||||||
import com.tangem.domain.models.network.Network
|
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
|
||||||
import com.tangem.domain.nft.utils.NFTCleaner
|
|
||||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
|
||||||
import kotlinx.coroutines.flow.*
|
|
||||||
|
|
||||||
class ObserveAndClearNFTCacheIfNeedUseCase(
|
|
||||||
private val nftCleaner: NFTCleaner,
|
|
||||||
private val currenciesRepository: CurrenciesRepository,
|
|
||||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
|
||||||
private val singleAccountListSupplier: SingleAccountListSupplier,
|
|
||||||
) {
|
|
||||||
|
|
||||||
operator fun invoke(userWalletId: UserWalletId): Flow<Set<Network>> = getCryptoCurrencies(userWalletId)
|
|
||||||
.map { it.map(CryptoCurrency::network) }
|
|
||||||
.mapDiff { old, new ->
|
|
||||||
// calculate networks sets difference to determine which networks were removed
|
|
||||||
old.toSet() - new.toSet()
|
|
||||||
}
|
|
||||||
.distinctUntilChanged()
|
|
||||||
.onEach { removedNetworks ->
|
|
||||||
if (removedNetworks.isNotEmpty()) {
|
|
||||||
nftCleaner(userWalletId, removedNetworks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getCryptoCurrencies(userWalletId: UserWalletId): Flow<Collection<CryptoCurrency>> {
|
|
||||||
return if (accountsFeatureToggles.isFeatureEnabled) {
|
|
||||||
singleAccountListSupplier(userWalletId).map(AccountList::flattenCurrencies)
|
|
||||||
} else {
|
|
||||||
currenciesRepository.getWalletCurrenciesUpdates(userWalletId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T, R> Flow<T>.mapDiff(diff: (old: T, new: T) -> R): Flow<R> = flow {
|
|
||||||
var previous: T? = null
|
|
||||||
collect { current ->
|
|
||||||
val prev = previous
|
|
||||||
if (prev != null) {
|
|
||||||
emit(diff(prev, current))
|
|
||||||
}
|
|
||||||
previous = current
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tangem.domain.staking.utils
|
package com.tangem.domain.staking.utils
|
||||||
|
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.staking.StakingID
|
import com.tangem.domain.models.staking.StakingID
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
||||||
|
|
@ -10,6 +11,34 @@ import com.tangem.domain.models.wallet.UserWalletId
|
||||||
*/
|
*/
|
||||||
interface StakingCleaner {
|
interface StakingCleaner {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up staking-related data for the given [userWalletId] and single [currency].
|
||||||
|
*
|
||||||
|
* @param userWalletId The ID of the user wallet for which to clean up data.
|
||||||
|
* @param currency The cryptocurrency whose associated staking data should be cleaned.
|
||||||
|
*/
|
||||||
|
suspend operator fun invoke(userWalletId: UserWalletId, currency: CryptoCurrency) {
|
||||||
|
invoke(userWalletId = userWalletId, currencies = listOf(currency))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up staking-related data for the given [userWalletId] and list of [currencies].
|
||||||
|
*
|
||||||
|
* @param userWalletId The ID of the user wallet for which to clean up data.
|
||||||
|
* @param currencies The list of cryptocurrencies whose associated staking data should be cleaned.
|
||||||
|
*/
|
||||||
|
suspend operator fun invoke(userWalletId: UserWalletId, currencies: List<CryptoCurrency>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up staking-related data for the given [userWalletId] and single [stakingId].
|
||||||
|
*
|
||||||
|
* @param userWalletId The ID of the user wallet for which to clean up data.
|
||||||
|
* @param stakingId The staking ID whose associated data should be cleaned.
|
||||||
|
*/
|
||||||
|
suspend operator fun invoke(userWalletId: UserWalletId, stakingId: StakingID) {
|
||||||
|
invoke(userWalletId = userWalletId, stakingIds = setOf(stakingId))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans up staking-related data for the given [userWalletId] and set of [stakingIds].
|
* Cleans up staking-related data for the given [userWalletId] and set of [stakingIds].
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import com.tangem.domain.apptheme.model.AppThemeMode
|
||||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||||
import com.tangem.domain.models.wallet.*
|
import com.tangem.domain.models.wallet.*
|
||||||
import com.tangem.domain.nft.ObserveAndClearNFTCacheIfNeedUseCase
|
|
||||||
import com.tangem.domain.notifications.GetIsHuaweiDeviceWithoutGoogleServicesUseCase
|
import com.tangem.domain.notifications.GetIsHuaweiDeviceWithoutGoogleServicesUseCase
|
||||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||||
|
|
@ -84,7 +83,6 @@ internal class WalletModel @Inject constructor(
|
||||||
private val onrampStatusFactory: OnrampStatusFactory,
|
private val onrampStatusFactory: OnrampStatusFactory,
|
||||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||||
private val walletContentFetcher: WalletContentFetcher,
|
private val walletContentFetcher: WalletContentFetcher,
|
||||||
private val observeAndClearNFTCacheIfNeedUseCase: ObserveAndClearNFTCacheIfNeedUseCase,
|
|
||||||
private val walletDeepLinkActionListener: WalletDeepLinkActionListener,
|
private val walletDeepLinkActionListener: WalletDeepLinkActionListener,
|
||||||
private val notificationsRepository: NotificationsRepository,
|
private val notificationsRepository: NotificationsRepository,
|
||||||
private val getWalletsListForEnablingUseCase: GetWalletsForAutomaticallyPushEnablingUseCase,
|
private val getWalletsListForEnablingUseCase: GetWalletsForAutomaticallyPushEnablingUseCase,
|
||||||
|
|
@ -114,7 +112,6 @@ internal class WalletModel @Inject constructor(
|
||||||
|
|
||||||
private val walletsUpdateJobHolder = JobHolder()
|
private val walletsUpdateJobHolder = JobHolder()
|
||||||
private val refreshWalletJobHolder = JobHolder()
|
private val refreshWalletJobHolder = JobHolder()
|
||||||
private val clearNFTCacheJobHolder = JobHolder()
|
|
||||||
private val updateTangemPayJobHolder = JobHolder()
|
private val updateTangemPayJobHolder = JobHolder()
|
||||||
|
|
||||||
private var needToRefreshWallet = false
|
private var needToRefreshWallet = false
|
||||||
|
|
@ -334,7 +331,6 @@ internal class WalletModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribeOnExpressTransactionsUpdates(selectedWallet)
|
subscribeOnExpressTransactionsUpdates(selectedWallet)
|
||||||
observeAndClearNFTCacheIfNeedUseCase(selectedWallet)
|
|
||||||
}
|
}
|
||||||
.flowOn(dispatchers.main)
|
.flowOn(dispatchers.main)
|
||||||
.launchIn(modelScope)
|
.launchIn(modelScope)
|
||||||
|
|
@ -390,13 +386,6 @@ internal class WalletModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeAndClearNFTCacheIfNeedUseCase(selectedWallet: UserWallet) {
|
|
||||||
observeAndClearNFTCacheIfNeedUseCase
|
|
||||||
.invoke(selectedWallet.walletId)
|
|
||||||
.launchIn(modelScope)
|
|
||||||
.saveIn(clearNFTCacheJobHolder)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun subscribeTangemPayOnWalletState() {
|
private fun subscribeTangemPayOnWalletState() {
|
||||||
/**
|
/**
|
||||||
* Update state each time a user opens/returns to wallet screen
|
* Update state each time a user opens/returns to wallet screen
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue