Updated on 2026-08-14

This commit is contained in:
Tangem 2023-08-29 12:17:17 +03:00
commit 41a9075fa0
11 changed files with 136 additions and 5 deletions

View file

@ -26,6 +26,15 @@ internal object TokensDomainModule {
return GetTokenListUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
}
@Provides
@ViewModelScoped
fun provideRemoveCurrencyUseCase(
currenciesRepository: CurrenciesRepository,
dispatchers: CoroutineDispatcherProvider,
): RemoveCurrencyUseCase {
return RemoveCurrencyUseCase(currenciesRepository, dispatchers)
}
@Provides
@ViewModelScoped
fun provideGetCurrencyUseCase(

View file

@ -53,6 +53,22 @@ internal class DefaultCurrenciesRepository(
storeAndPushTokens(userWalletId, response)
}
override suspend fun removeCurrency(userWalletId: UserWalletId, currency: CryptoCurrency) =
withContext(dispatchers.io) {
val savedCurrencies = requireNotNull(
value = userTokensStore.getSyncOrNull(userWalletId),
lazyMessage = { "Saved tokens empty. Can not perform remove currency action" },
)
val token = userTokensResponseFactory.createResponseToken(currency)
storeAndPushTokens(
userWalletId = userWalletId,
response = savedCurrencies.copy(
tokens = savedCurrencies.tokens.filter { it != token },
),
)
}
override suspend fun getSingleCurrencyWalletPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency {
return withContext(dispatchers.io) {
val userWallet = getUserWallet(userWalletId)
@ -78,6 +94,21 @@ internal class DefaultCurrenciesRepository(
}
}
override suspend fun getMultiCurrencyWalletCurrenciesSync(
userWalletId: UserWalletId,
refresh: Boolean,
): List<CryptoCurrency> {
val userWallet = getUserWallet(userWalletId)
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true)
fetchTokensIfCacheExpired(userWallet, refresh)
val storedTokens = requireNotNull(userTokensStore.getSyncOrNull(userWallet.walletId))
return responseCurrenciesFactory.createCurrencies(
response = storedTokens,
card = userWallet.scanResponse.card,
)
}
override suspend fun getMultiCurrencyWalletCurrency(
userWalletId: UserWalletId,
id: CryptoCurrency.ID,

View file

@ -26,7 +26,7 @@ internal class UserTokensResponseFactory {
)
}
private fun createResponseToken(currency: CryptoCurrency): UserTokensResponse.Token {
fun createResponseToken(currency: CryptoCurrency): UserTokensResponse.Token {
val blockchain = getBlockchain(currency.network.id)
return UserTokensResponse.Token(

View file

@ -73,8 +73,6 @@ data class Network(
}
/** Represents a network that does not adhere to a predefined standard type. */
class Unspecified(val networkName: String) : StandardType() {
override val name: String = networkName
}
data class Unspecified(override val name: String) : StandardType()
}
}

View file

@ -0,0 +1,8 @@
package com.tangem.domain.tokens.models.remove
import com.tangem.domain.tokens.models.CryptoCurrency
sealed class RemoveCurrencyError : Throwable() {
data class HasLinkedTokens(val currency: CryptoCurrency) : RemoveCurrencyError()
data class DataError(override val cause: Throwable) : RemoveCurrencyError()
}

View file

@ -0,0 +1,40 @@
package com.tangem.domain.tokens
import arrow.core.Either
import arrow.core.raise.catch
import arrow.core.raise.either
import arrow.core.raise.ensure
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.domain.tokens.models.remove.RemoveCurrencyError
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
class RemoveCurrencyUseCase(
internal val currenciesRepository: CurrenciesRepository,
internal val dispatchers: CoroutineDispatcherProvider,
) {
suspend operator fun invoke(
userWalletId: UserWalletId,
currency: CryptoCurrency,
): Either<RemoveCurrencyError, Unit> {
return either {
ensure(
condition = !currency.hasLinkedTokens(userWalletId),
raise = { RemoveCurrencyError.HasLinkedTokens(currency) },
)
catch(
block = { currenciesRepository.removeCurrency(userWalletId, currency) },
catch = { raise(RemoveCurrencyError.DataError(it)) },
)
}
}
private suspend fun CryptoCurrency.hasLinkedTokens(userWalletId: UserWalletId): Boolean {
val walletCurrencies = currenciesRepository
.getMultiCurrencyWalletCurrenciesSync(userWalletId = userWalletId, refresh = false)
return this is CryptoCurrency.Coin && walletCurrencies.any { it != this && it.network.id == this.network.id }
}
}

View file

@ -27,6 +27,16 @@ interface CurrenciesRepository {
isSortedByBalance: Boolean,
)
/**
* Removes currency from a specific user wallet.
*
* @param userWalletId The unique identifier of the user wallet.
* @param currency The currency which must be removed.
* @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If multi-currency user wallet
* ID provided.
*/
suspend fun removeCurrency(userWalletId: UserWalletId, currency: CryptoCurrency)
/**
* Retrieves the primary cryptocurrency for a specific single-currency user wallet.
*
@ -48,6 +58,17 @@ interface CurrenciesRepository {
*/
fun getMultiCurrencyWalletCurrencies(userWalletId: UserWalletId, refresh: Boolean): Flow<List<CryptoCurrency>>
/**
* Retrieves the list of cryptocurrencies within a multi-currency wallet.
*
* @param userWalletId The unique identifier of the user wallet.
* @param refresh A boolean flag indicating whether the data should be refreshed.
* @return A list of [CryptoCurrency].
* @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet
* ID provided.
*/
suspend fun getMultiCurrencyWalletCurrenciesSync(userWalletId: UserWalletId, refresh: Boolean): List<CryptoCurrency>
/**
* Retrieves the cryptocurrency for a specific multi-currency user wallet.
*

View file

@ -190,8 +190,16 @@ internal class ApplyTokenListSortingUseCaseTest {
private fun getTokensRepository(
sortTokensResult: Either<DataError, Unit> = Unit.right(),
removeCurrencyResult: Either<DataError, Unit> = Unit.right(),
tokens: Flow<Either<DataError, List<CryptoCurrency>>> = flowOf(MockTokens.tokens.right()),
): MockCurrenciesRepository {
return MockCurrenciesRepository(sortTokensResult, MockTokens.token1.right(), tokens, emptyFlow(), emptyFlow())
return MockCurrenciesRepository(
sortTokensResult = sortTokensResult,
removeCurrencyResult = removeCurrencyResult,
token = MockTokens.token1.right(),
tokens = tokens,
isGrouped = emptyFlow(),
isSortedByBalance = emptyFlow(),
)
}
}

View file

@ -150,12 +150,14 @@ internal class GetPrimaryCurrencyUseCaseTest {
private fun getUseCase(
token: Either<DataError, CryptoCurrency> = MockTokens.token1.right(),
removeCurrencyResult: Either<DataError, Unit> = Unit.right(),
quotes: Flow<Either<DataError, Set<Quote>>> = flowOf(MockQuotes.quotes.right()),
statuses: Flow<Either<DataError, Set<NetworkStatus>>> = flowOf(MockNetworks.verifiedNetworksStatuses.right()),
) = GetPrimaryCurrencyUseCase(
dispatchers = dispatchers,
currenciesRepository = MockCurrenciesRepository(
sortTokensResult = Unit.right(),
removeCurrencyResult = removeCurrencyResult,
token = token,
tokens = flowOf(),
isGrouped = flowOf(),

View file

@ -342,6 +342,7 @@ internal class GetTokenListUseCaseTest {
dispatchers = dispatchers,
currenciesRepository = MockCurrenciesRepository(
sortTokensResult = Unit.right(),
removeCurrencyResult = Unit.right(),
token = MockTokens.token1.right(),
tokens = tokens,
isGrouped = isGrouped,

View file

@ -6,10 +6,12 @@ import com.tangem.domain.core.error.DataError
import com.tangem.domain.tokens.models.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
internal class MockCurrenciesRepository(
private val sortTokensResult: Either<DataError, Unit>,
private val removeCurrencyResult: Either<DataError, Unit>,
private val token: Either<DataError, CryptoCurrency>,
private val tokens: Flow<Either<DataError, List<CryptoCurrency>>>,
private val isGrouped: Flow<Either<DataError, Boolean>>,
@ -38,6 +40,17 @@ internal class MockCurrenciesRepository(
isTokensSortedByBalanceAfterSortingApply = isSortedByBalance
}
override suspend fun removeCurrency(userWalletId: UserWalletId, currency: CryptoCurrency) {
removeCurrencyResult.onLeft { throw it }
}
override suspend fun getMultiCurrencyWalletCurrenciesSync(
userWalletId: UserWalletId,
refresh: Boolean,
): List<CryptoCurrency> {
return tokens.first().getOrElse { e -> throw e }
}
override suspend fun getSingleCurrencyWalletPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency {
return token.getOrElse { e -> throw e }
}