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 8fc08144df..274f5bbe46 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 @@ -1,9 +1,9 @@ package com.tangem.tap.di.domain import com.tangem.domain.tokens.* +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.tokens.repository.TokensRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides @@ -18,23 +18,23 @@ internal object TokensDomainModule { @Provides @ViewModelScoped fun provideGetTokenListUseCase( - tokensRepository: TokensRepository, + currenciesRepository: CurrenciesRepository, quotesRepository: QuotesRepository, networksRepository: NetworksRepository, dispatchers: CoroutineDispatcherProvider, ): GetTokenListUseCase { - return GetTokenListUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers) + return GetTokenListUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers) } @Provides @ViewModelScoped fun provideGetPrimaryCurrencyUseCase( - tokensRepository: TokensRepository, + currenciesRepository: CurrenciesRepository, quotesRepository: QuotesRepository, networksRepository: NetworksRepository, dispatchers: CoroutineDispatcherProvider, - ): GetPrimaryCurrencyUseCase { - return GetPrimaryCurrencyUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers) + ): GetCurrencyUseCase { + return GetCurrencyUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers) } @Provides @@ -55,9 +55,9 @@ internal object TokensDomainModule { @Provides @ViewModelScoped fun provideApplyTokenListSortingUseCase( - tokensRepository: TokensRepository, + currenciesRepository: CurrenciesRepository, dispatchers: CoroutineDispatcherProvider, ): ApplyTokenListSortingUseCase { - return ApplyTokenListSortingUseCase(tokensRepository, dispatchers) + return ApplyTokenListSortingUseCase(currenciesRepository, dispatchers) } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt index 25f7f9defd..a5adf06ad8 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt @@ -1,15 +1,15 @@ package com.tangem.data.tokens.di import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.tokens.repository.DefaultCurrenciesRepository import com.tangem.data.tokens.repository.DefaultNetworksRepository -import com.tangem.data.tokens.repository.DefaultTokensRepository import com.tangem.data.tokens.repository.MockQuotesRepository import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.local.token.UserTokensStore import com.tangem.datasource.local.userwallet.UserWalletsStore +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.tokens.repository.TokensRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module @@ -24,14 +24,14 @@ internal object TokensDataModule { @Provides @Singleton - fun provideTokensRepository( + fun provideCurrenciesRepository( tangemTechApi: TangemTechApi, userTokensStore: UserTokensStore, userWalletsStore: UserWalletsStore, cacheRegistry: CacheRegistry, dispatchers: CoroutineDispatcherProvider, - ): TokensRepository { - return DefaultTokensRepository(tangemTechApi, userTokensStore, userWalletsStore, cacheRegistry, dispatchers) + ): CurrenciesRepository { + return DefaultCurrenciesRepository(tangemTechApi, userTokensStore, userWalletsStore, cacheRegistry, dispatchers) } @Provides diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt similarity index 55% rename from data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt rename to data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 704e25fa68..21c8020e2d 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -8,23 +8,27 @@ import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.local.token.UserTokensStore import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.core.error.DataError import com.tangem.domain.demo.DemoConfig import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.repository.TokensRepository +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import timber.log.Timber -internal class DefaultTokensRepository( +internal class DefaultCurrenciesRepository( private val tangemTechApi: TangemTechApi, private val userTokensStore: UserTokensStore, private val userWalletsStore: UserWalletsStore, private val cacheRegistry: CacheRegistry, private val dispatchers: CoroutineDispatcherProvider, -) : TokensRepository { +) : CurrenciesRepository { private val demoConfig = DemoConfig() private val responseCurrenciesFactory = ResponseCurrenciesFactory(demoConfig) @@ -37,6 +41,8 @@ internal class DefaultTokensRepository( isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ) = withContext(dispatchers.io) { + ensureIsCorrectUserWallet(userWalletId, isMultiCurrencyWalletExpected = true) + val response = userTokensResponseFactory.createUserTokensResponse( currencies = currencies, isGroupedByNetwork = isGroupedByNetwork, @@ -46,58 +52,72 @@ internal class DefaultTokensRepository( storeAndPushTokens(userWalletId, response) } - override suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency { - val userWallet = withContext(dispatchers.io) { - requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { - "Unable to find a user wallet with provided ID: $userWalletId" - } - } - require(!userWallet.isMultiCurrency) { - "Single currency wallet excepted, but multi currency wallet was found: $userWalletId" - } + override suspend fun getSingleCurrencyWalletPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency { + return withContext(dispatchers.io) { + val userWallet = getUserWallet(userWalletId) + ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false) - return cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse) + cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse) + } } override fun getMultiCurrencyWalletCurrencies( userWalletId: UserWalletId, refresh: Boolean, - ): Flow> { + ): Flow> = channelFlow { + val userWallet = getUserWallet(userWalletId) + ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true) + + launch(dispatchers.io) { + getMultiCurrencyWalletCurrencies(userWallet).collect(::send) + } + + launch(dispatchers.io) { + fetchTokensIfCacheExpired(userWallet, refresh) + } + } + + override suspend fun getMultiCurrencyWalletCurrency( + userWalletId: UserWalletId, + id: CryptoCurrency.ID, + ): CryptoCurrency = withContext(dispatchers.io) { + val userWallet = getUserWallet(userWalletId) + ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true) + + val response = requireNotNull(userTokensStore.getSyncOrNull(userWalletId)) { + "Unable to find tokens response for user wallet with provided ID: $userWalletId" + } + + responseCurrenciesFactory.createCurrency(id, response, userWallet.scanResponse.card) + } + + override fun isTokensGrouped(userWalletId: UserWalletId): Flow { return channelFlow { - val userWallet = withContext(dispatchers.io) { - requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { - "Unable to find a user wallet with provided ID: $userWalletId" - } - } - require(userWallet.isMultiCurrency) { - "Multi currency wallet excepted, but single currency wallet was found: $userWalletId" - } + ensureIsCorrectUserWallet(userWalletId, isMultiCurrencyWalletExpected = true) launch(dispatchers.io) { - getMultiCurrencyWalletCurrencies(userWallet).collectLatest(::send) - } - - launch(dispatchers.io) { - fetchTokensIfCacheExpired(userWallet, refresh) + userTokensStore.get(userWalletId) + .map { it.group == UserTokensResponse.GroupType.NETWORK } + .collect(::send) } } } - override fun isTokensGrouped(userWalletId: UserWalletId): Flow { - return userTokensStore.get(userWalletId) - .map { it.group == UserTokensResponse.GroupType.NETWORK } - .flowOn(dispatchers.io) - } - override fun isTokensSortedByBalance(userWalletId: UserWalletId): Flow { - return userTokensStore.get(userWalletId) - .map { it.sort == UserTokensResponse.SortType.BALANCE } - .flowOn(dispatchers.io) + return channelFlow { + ensureIsCorrectUserWallet(userWalletId, isMultiCurrencyWalletExpected = true) + + launch(dispatchers.io) { + userTokensStore.get(userWalletId) + .map { it.sort == UserTokensResponse.SortType.BALANCE } + .collect(::send) + } + } } private fun getMultiCurrencyWalletCurrencies(userWallet: UserWallet): Flow> { return userTokensStore.get(userWallet.walletId).map { storedTokens -> - responseCurrenciesFactory.createTokens( + responseCurrenciesFactory.createCurrencies( response = storedTokens, card = userWallet.scanResponse.card, ) @@ -146,6 +166,39 @@ internal class DefaultTokensRepository( } } + private suspend fun getUserWallet(userWalletId: UserWalletId): UserWallet { + return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { + "Unable to find a user wallet with provided ID: $userWalletId" + } + } + + private suspend fun ensureIsCorrectUserWallet(userWalletId: UserWalletId, isMultiCurrencyWalletExpected: Boolean) { + val userWallet = getUserWallet(userWalletId) + + ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected) + } + + private fun ensureIsCorrectUserWallet(userWallet: UserWallet, isMultiCurrencyWalletExpected: Boolean) { + val userWalletId = userWallet.walletId + + val message = when { + !userWallet.isMultiCurrency && isMultiCurrencyWalletExpected -> { + "Multi currency wallet expected, but single currency wallet was found: $userWalletId" + } + userWallet.isMultiCurrency && !isMultiCurrencyWalletExpected -> { + "Single currency wallet expected, but multi currency wallet was found: $userWalletId" + } + else -> null + } + + if (message != null) { + val error = DataError.UserWalletError.WrongUserWallet(message) + + Timber.e(error) + throw error + } + } + private fun getTokensCacheKey(userWalletId: UserWalletId): String = "tokens_cache_key_${userWalletId.stringValue}" private companion object { diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt index dbde700089..bf4206d543 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -18,7 +18,10 @@ import com.tangem.utils.extensions.addOrReplace import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch internal class DefaultNetworksRepository( @@ -44,7 +47,9 @@ internal class DefaultNetworksRepository( networks: Set, refresh: Boolean, ): Flow> = channelFlow { - networksStatuses.collectLatest(::send) + launch(dispatchers.io) { + networksStatuses.collect(::send) + } launch(dispatchers.io) { fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh) @@ -99,7 +104,7 @@ internal class DefaultNetworksRepository( "Unable to find tokens response for user wallet with provided ID: $userWalletId" } - return responseCurrenciesFactory.createTokens(response, userWallet.scanResponse.card) + return responseCurrenciesFactory.createCurrencies(response, userWallet.scanResponse.card) } private fun getNetworksStatusesCacheKey(userWalletId: UserWalletId): String = "network_status_$userWalletId" diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt index d27da42e10..f76f363033 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt @@ -40,7 +40,7 @@ internal class NetworkStatusFactory { is CryptoCurrency.Coin -> amounts.singleOrNull { it is CryptoCurrencyAmount.Coin } is CryptoCurrency.Token -> amounts.singleOrNull { it is CryptoCurrencyAmount.Token && - it.id == getTokenIdString(currency) && + it.id == getTokenIdString(currency.id) && it.tokenContractAddress == currency.contractAddress } }?.value diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt index 0a6c78b0e5..cbecf6a792 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt @@ -12,11 +12,23 @@ import com.tangem.blockchain.common.Token as SdkToken internal class ResponseCurrenciesFactory(private val demoConfig: DemoConfig) { - fun createTokens(response: UserTokensResponse, card: CardDTO): Set { - return response.tokens.mapNotNull { createToken(it, card) }.toSet() + fun createCurrency(currencyId: CryptoCurrency.ID, response: UserTokensResponse, card: CardDTO): CryptoCurrency { + val responseTokenId = getTokenIdString(currencyId) + + val token = requireNotNull(response.tokens.firstOrNull { it.id == responseTokenId }) { + "Unable find a token with provided ID: $responseTokenId" + } + + return requireNotNull(createCurrency(token, card)) { + "Unable to create a currency with provided ID: $currencyId" + } } - private fun createToken(responseToken: UserTokensResponse.Token, card: CardDTO): CryptoCurrency? { + fun createCurrencies(response: UserTokensResponse, card: CardDTO): Set { + return response.tokens.mapNotNull { createCurrency(it, card) }.toSet() + } + + private fun createCurrency(responseToken: UserTokensResponse.Token, card: CardDTO): CryptoCurrency? { var blockchain = Blockchain.fromNetworkId(responseToken.networkId) if (blockchain == null || blockchain == Blockchain.Unknown) { Timber.e("Unable to find a blockchain with the network ID: ${responseToken.networkId}") diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt index 1ff2268f53..e7704bdc9e 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt @@ -49,9 +49,14 @@ internal fun getTokenId(blockchain: Blockchain, token: SdkToken): CryptoCurrency return getTokenOrCoinId(blockchain, token) } -internal fun getTokenIdString(currency: CryptoCurrency): String? { - return currency.id.value.substringAfter(TOKEN_ID_DELIMITER) - .takeUnless { currency is CryptoCurrency.Token && currency.isCustom } +internal fun getTokenIdString(currencyId: CryptoCurrency.ID): String? { + val idValue = currencyId.value + + return if (idValue.startsWith(CUSTOM_TOKEN_ID_PREFIX)) { + null + } else { + idValue.substringAfter(TOKEN_ID_DELIMITER) + } } internal fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? { diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt index 898c62c8b3..437100ce08 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt @@ -30,7 +30,7 @@ internal class UserTokensResponseFactory { val blockchain = getBlockchain(currency.networkId) return UserTokensResponse.Token( - id = getTokenIdString(currency), + id = getTokenIdString(currency.id), networkId = blockchain.toNetworkId(), derivationPath = currency.derivationPath, name = currency.name, diff --git a/domain/core/src/main/kotlin/com/tangem/domain/core/error/DataError.kt b/domain/core/src/main/kotlin/com/tangem/domain/core/error/DataError.kt index 11567ca43b..35c115a1e8 100644 --- a/domain/core/src/main/kotlin/com/tangem/domain/core/error/DataError.kt +++ b/domain/core/src/main/kotlin/com/tangem/domain/core/error/DataError.kt @@ -6,4 +6,9 @@ sealed class DataError : Exception() { object NoInternetConnection : NetworkError() } + + sealed class UserWalletError : DataError() { + + data class WrongUserWallet(override val message: String) : UserWalletError() + } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt index 89d9776a18..907fae6af5 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt @@ -9,14 +9,14 @@ import arrow.core.toNonEmptySetOrNull import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.models.Network -import com.tangem.domain.tokens.repository.TokensRepository +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.withContext class ApplyTokenListSortingUseCase( - private val tokensRepository: TokensRepository, + private val currenciesRepository: CurrenciesRepository, private val dispatchers: CoroutineDispatcherProvider, ) { @@ -67,7 +67,9 @@ class ApplyTokenListSortingUseCase( private suspend fun Raise.getCurrencies(userWalletId: UserWalletId): Set { val tokens = catch( - block = { tokensRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh = false).firstOrNull() }, + block = { + currenciesRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh = false).firstOrNull() + }, catch = { raise(TokenListSortingError.DataError(it)) }, ) @@ -83,7 +85,7 @@ class ApplyTokenListSortingUseCase( isSortedByBalance: Boolean, ) = withContext(dispatchers.io) { catch( - block = { tokensRepository.saveTokens(userWalletId, tokens, isGrouped, isSortedByBalance) }, + block = { currenciesRepository.saveTokens(userWalletId, tokens, isGrouped, isSortedByBalance) }, catch = { raise(TokenListSortingError.DataError(it)) }, ) } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt new file mode 100644 index 0000000000..f49378314a --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt @@ -0,0 +1,82 @@ +package com.tangem.domain.tokens + +import arrow.core.Either +import arrow.core.left +import arrow.core.raise.Raise +import arrow.core.raise.recover +import arrow.core.right +import com.tangem.domain.tokens.error.CurrencyError +import com.tangem.domain.tokens.error.mapper.mapToTokenError +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations +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 +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.collectLatest + +/** + * Use case for fetching the status of a specific cryptocurrency associated with a user wallet. + * + * @property currenciesRepository Repository for managing and fetching cryptocurrencies. + * @property quotesRepository Repository for managing and fetching cryptocurrency quotes. + * @property networksRepository Repository for managing and fetching information related to blockchain networks. + * @property dispatchers Provides coroutine dispatchers. + */ +class GetCurrencyUseCase( + private val currenciesRepository: CurrenciesRepository, + private val quotesRepository: QuotesRepository, + private val networksRepository: NetworksRepository, + private val dispatchers: CoroutineDispatcherProvider, +) { + + /** + * Invokes the use case. + * + * @param userWalletId The unique identifier of the user's wallet. + * @param currencyId The unique identifier of the cryptocurrency. + * @param refresh A boolean flag indicating whether the data should be refreshed. + * @return A [Flow] emitting either a [CurrencyError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation. + */ + operator fun invoke( + userWalletId: UserWalletId, + currencyId: CryptoCurrency.ID, + refresh: Boolean = false, + ): Flow> { + return channelFlow { + recover( + block = { + getCurrency(userWalletId, currencyId, refresh).collectLatest { currencyStatus -> + send(currencyStatus.right()) + } + }, + recover = { error -> + send(error.left()) + }, + ) + } + } + + private suspend fun Raise.getCurrency( + userWalletId: UserWalletId, + currencyId: CryptoCurrency.ID, + refresh: Boolean, + ): Flow { + val operations = CurrenciesStatusesOperations( + currenciesRepository = currenciesRepository, + quotesRepository = quotesRepository, + networksRepository = networksRepository, + userWalletId = userWalletId, + refresh = refresh, + dispatchers = dispatchers, + raise = this, + transformError = CurrenciesStatusesOperations.Error::mapToTokenError, + ) + + return operations.getCurrencyStatusFlow(currencyId) + } +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt index bfc25b58da..3d0f9c2011 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt @@ -5,35 +5,50 @@ import arrow.core.left import arrow.core.raise.Raise import arrow.core.raise.recover import arrow.core.right -import com.tangem.domain.tokens.error.TokenError +import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.error.mapper.mapToTokenError import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations +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.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.collectLatest +/** + * Use case for fetching the status of the primary cryptocurrency associated with a user wallet. + * + * @property currenciesRepository Repository for managing and fetching cryptocurrencies. + * @property quotesRepository Repository for managing and fetching cryptocurrency quotes. + * @property networksRepository Repository for managing and fetching information related to blockchain networks. + * @property dispatchers Provides coroutine dispatchers. + */ class GetPrimaryCurrencyUseCase( - private val tokensRepository: TokensRepository, + private val currenciesRepository: CurrenciesRepository, private val quotesRepository: QuotesRepository, private val networksRepository: NetworksRepository, private val dispatchers: CoroutineDispatcherProvider, ) { + /** + * Invokes the use case. + * + * @param userWalletId The unique identifier of the user's wallet. + * @param refresh A boolean flag indicating whether the data should be refreshed. + * @return A [Flow] emitting either a [CurrencyError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation. + */ operator fun invoke( userWalletId: UserWalletId, refresh: Boolean = false, - ): Flow> { + ): Flow> { return channelFlow { recover( block = { - getToken(userWalletId, refresh).collectLatest { token -> - send(token.right()) + getCurrency(userWalletId, refresh).collectLatest { currencyStatus -> + send(currencyStatus.right()) } }, recover = { error -> @@ -43,12 +58,12 @@ class GetPrimaryCurrencyUseCase( } } - private suspend fun Raise.getToken( + private suspend fun Raise.getCurrency( userWalletId: UserWalletId, refresh: Boolean, ): Flow { val operations = CurrenciesStatusesOperations( - tokensRepository = tokensRepository, + currenciesRepository = currenciesRepository, quotesRepository = quotesRepository, networksRepository = networksRepository, userWalletId = userWalletId, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt index 0a142e1241..ea19873b22 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt @@ -11,9 +11,9 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations import com.tangem.domain.tokens.operations.TokenListOperations +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.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow @@ -22,7 +22,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.flatMapConcat class GetTokenListUseCase( - internal val tokensRepository: TokensRepository, + internal val currenciesRepository: CurrenciesRepository, internal val quotesRepository: QuotesRepository, internal val networksRepository: NetworksRepository, internal val dispatchers: CoroutineDispatcherProvider, @@ -60,7 +60,7 @@ class GetTokenListUseCase( transformError = CurrenciesStatusesOperations.Error::mapToTokenListError, ) - return operations.getMultiCurrencyWalletStatusesFlow() + return operations.getCurrenciesStatusesFlow() } private fun Raise.createTokenList( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/CurrencyError.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/CurrencyError.kt new file mode 100644 index 0000000000..5d1ab7e2d8 --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/CurrencyError.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.tokens.error + +sealed class CurrencyError { + + object UnableToCreateCurrency : CurrencyError() + + data class DataError(val cause: Throwable) : CurrencyError() +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/TokenError.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/TokenError.kt deleted file mode 100644 index b8b3e0631b..0000000000 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/TokenError.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.domain.tokens.error - -sealed class TokenError { - - object UnableToCreateToken : TokenError() - - data class DataError(val cause: Throwable) : TokenError() -} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt index e129b9fc6f..981f8785f2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt @@ -1,15 +1,15 @@ package com.tangem.domain.tokens.error.mapper -import com.tangem.domain.tokens.error.TokenError +import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations -internal fun CurrenciesStatusesOperations.Error.mapToTokenError(): TokenError { +internal fun CurrenciesStatusesOperations.Error.mapToTokenError(): CurrencyError { return when (this) { - is CurrenciesStatusesOperations.Error.DataError -> TokenError.DataError(this.cause) + is CurrenciesStatusesOperations.Error.DataError -> CurrencyError.DataError(this.cause) is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses, is CurrenciesStatusesOperations.Error.EmptyQuotes, is CurrenciesStatusesOperations.Error.EmptyCurrencies, is CurrenciesStatusesOperations.Error.UnableToCreateCurrencyStatus, - -> TokenError.UnableToCreateToken + -> CurrencyError.UnableToCreateCurrency } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 7b49295f4a..1b23b9910c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -7,9 +7,9 @@ import com.tangem.domain.core.raise.DelegatedRaise import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.model.* import com.tangem.domain.tokens.models.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.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* @@ -17,7 +17,7 @@ import kotlinx.coroutines.withContext @Suppress("LongParameterList") internal class CurrenciesStatusesOperations( - private val tokensRepository: TokensRepository, + private val currenciesRepository: CurrenciesRepository, private val quotesRepository: QuotesRepository, private val networksRepository: NetworksRepository, private val userWalletId: UserWalletId, @@ -34,7 +34,7 @@ internal class CurrenciesStatusesOperations( raise: Raise, transformError: (Error) -> E, ) : this( - tokensRepository = useCase.tokensRepository, + currenciesRepository = useCase.currenciesRepository, quotesRepository = useCase.quotesRepository, networksRepository = useCase.networksRepository, userWalletId = userWalletId, @@ -44,7 +44,7 @@ internal class CurrenciesStatusesOperations( transformError = transformError, ) - fun getMultiCurrencyWalletStatusesFlow(): Flow> { + fun getCurrenciesStatusesFlow(): Flow> { return getMultiCurrencyWalletCurrencies().flatMapConcat { val currencies = it.toNonEmptySetOrNull() @@ -68,9 +68,19 @@ internal class CurrenciesStatusesOperations( } } + suspend fun getCurrencyStatusFlow(currencyId: CryptoCurrency.ID): Flow { + val currency = getMultiCurrencyWalletCurrency(currencyId) + + return getCurrencyStatusFlow(currency) + } + suspend fun getPrimaryCurrencyStatusFlow(): Flow { val currency = getPrimaryCurrency() + return getCurrencyStatusFlow(currency) + } + + private fun getCurrencyStatusFlow(currency: CryptoCurrency): Flow { val quoteFlow = getQuotes(nonEmptySetOf(currency.id)) .map { quotes -> quotes.singleOrNull { it.currencyId == currency.id } @@ -116,34 +126,36 @@ internal class CurrenciesStatusesOperations( return currencyStatusOperations.createTokenStatus() } + private suspend fun getMultiCurrencyWalletCurrency(currencyId: CryptoCurrency.ID): CryptoCurrency { + return catch( + block = { currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, currencyId) }, + catch = { raise(Error.DataError(it)) }, + ) + } + private fun getMultiCurrencyWalletCurrencies(): Flow> { - return tokensRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh) + return currenciesRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh) .catch { raise(Error.DataError(it)) } .onEmpty { raise(Error.EmptyCurrencies) } - .flowOn(dispatchers.io) } private suspend fun getPrimaryCurrency(): CryptoCurrency { - return withContext(dispatchers.io) { - catch( - block = { tokensRepository.getPrimaryCurrency(userWalletId) }, - catch = { raise(Error.DataError(it)) }, - ) - } + return catch( + block = { currenciesRepository.getSingleCurrencyWalletPrimaryCurrency(userWalletId) }, + catch = { raise(Error.DataError(it)) }, + ) } private fun getQuotes(tokensIds: NonEmptySet): Flow> { return quotesRepository.getQuotes(tokensIds, refresh) .catch { raise(Error.DataError(it)) } .onEmpty { raise(Error.EmptyQuotes) } - .flowOn(dispatchers.io) } private fun getNetworksStatues(networks: NonEmptySet): Flow> { return networksRepository.getNetworkStatuses(userWalletId, networks, refresh) .catch { raise(Error.DataError(it)) } .onEmpty { raise(Error.EmptyNetworksStatuses) } - .flowOn(dispatchers.io) } sealed class Error { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt index 37b96a781c..da6c780e24 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt @@ -10,8 +10,8 @@ import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.models.Network +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository -import com.tangem.domain.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* @@ -19,7 +19,7 @@ import kotlinx.coroutines.withContext @Suppress("LongParameterList") internal class TokenListOperations( - private val tokensRepository: TokensRepository, + private val currenciesRepository: CurrenciesRepository, private val networksRepository: NetworksRepository, private val userWalletId: UserWalletId, private val tokens: Set, @@ -35,7 +35,7 @@ internal class TokenListOperations( raise: Raise, transform: (Error) -> E, ) : this( - tokensRepository = useCase.tokensRepository, + currenciesRepository = useCase.currenciesRepository, networksRepository = useCase.networksRepository, userWalletId = userWalletId, tokens = tokens, @@ -149,14 +149,14 @@ internal class TokenListOperations( } private fun getIsGrouped(): Flow { - return tokensRepository.isTokensGrouped(userWalletId) + return currenciesRepository.isTokensGrouped(userWalletId) .catch { raise(Error.DataError(it)) } .onEmpty { emit(value = false) } .flowOn(dispatchers.io) } private fun getIsSortedByBalance(): Flow { - return tokensRepository.isTokensSortedByBalance(userWalletId) + return currenciesRepository.isTokensSortedByBalance(userWalletId) .catch { raise(Error.DataError(it)) } .onEmpty { emit(value = false) } .flowOn(dispatchers.io) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokensRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt similarity index 63% rename from domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokensRepository.kt rename to domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt index 865c5fc4da..8b5e0fe9e5 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokensRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.Flow /** * Repository for everything related to the tokens of user wallet * */ -interface TokensRepository { +interface CurrenciesRepository { /** * Saves the given set of cryptocurrencies, along with the preferences for grouping and sorting, for a specific @@ -17,6 +17,8 @@ interface TokensRepository { * @param currencies The set of cryptocurrencies to be saved. * @param isGroupedByNetwork A boolean flag indicating whether the tokens should be grouped by network. * @param isSortedByBalance A boolean flag indicating whether the tokens should be sorted by balance. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. */ suspend fun saveTokens( userWalletId: UserWalletId, @@ -30,8 +32,10 @@ interface TokensRepository { * * @param userWalletId The unique identifier of the user wallet. * @return The primary cryptocurrency associated with the user wallet. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If multi-currency user wallet + * ID provided. */ - suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency + suspend fun getSingleCurrencyWalletPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency /** * Retrieves the set of cryptocurrencies within a multi-currency wallet. @@ -39,14 +43,29 @@ interface TokensRepository { * @param userWalletId The unique identifier of the user wallet. * @param refresh A boolean flag indicating whether the data should be refreshed. * @return A [Flow] emitting the set of cryptocurrencies associated with the user wallet. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. */ fun getMultiCurrencyWalletCurrencies(userWalletId: UserWalletId, refresh: Boolean): Flow> + /** + * Retrieves the cryptocurrency for a specific multi-currency user wallet. + * + * @param userWalletId The unique identifier of the user wallet. + * @param id The unique identifier of the cryptocurrency to be retrieved. + * @return The cryptocurrency associated with the user wallet and ID. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. + */ + suspend fun getMultiCurrencyWalletCurrency(userWalletId: UserWalletId, id: CryptoCurrency.ID): CryptoCurrency + /** * Determines whether the tokens within a specific multi-currency user wallet are grouped. * * @param userWalletId The unique identifier of the user wallet. * @return A [Flow] emitting a boolean value indicating whether the tokens are grouped. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. */ fun isTokensGrouped(userWalletId: UserWalletId): Flow @@ -55,6 +74,8 @@ interface TokensRepository { * * @param userWalletId The unique identifier of the user wallet. * @return A [Flow] emitting a boolean value indicating whether the tokens are sorted by balance. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. */ fun isTokensSortedByBalance(userWalletId: UserWalletId): Flow } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt index 0bdf923b6b..16ee99fff2 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt @@ -7,7 +7,7 @@ import com.tangem.domain.core.error.DataError import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.mock.MockTokens import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.repository.MockTokensRepository +import com.tangem.domain.tokens.repository.MockCurrenciesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import junit.framework.TestCase.assertEquals @@ -183,16 +183,16 @@ internal class ApplyTokenListSortingUseCaseTest { .sortedBy { Random.nextInt(0, MockTokens.tokens.size) } .toSet() - private fun getUseCase(tokensRepository: MockTokensRepository = getTokensRepository()) = + private fun getUseCase(tokensRepository: MockCurrenciesRepository = getTokensRepository()) = ApplyTokenListSortingUseCase( - tokensRepository = tokensRepository, + currenciesRepository = tokensRepository, dispatchers = TestingCoroutineDispatcherProvider(), ) private fun getTokensRepository( sortTokensResult: Either = Unit.right(), tokens: Flow>> = flowOf(MockTokens.tokens.right()), - ): MockTokensRepository { - return MockTokensRepository(sortTokensResult, MockTokens.token1.right(), tokens, emptyFlow(), emptyFlow()) + ): MockCurrenciesRepository { + return MockCurrenciesRepository(sortTokensResult, MockTokens.token1.right(), tokens, emptyFlow(), emptyFlow()) } } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt index 95026c6926..c524f8d3ba 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt @@ -4,7 +4,7 @@ import arrow.core.Either import arrow.core.left import arrow.core.right import com.tangem.domain.core.error.DataError -import com.tangem.domain.tokens.error.TokenError +import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.mock.MockNetworks import com.tangem.domain.tokens.mock.MockQuotes import com.tangem.domain.tokens.mock.MockTokens @@ -13,9 +13,9 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.repository.MockCurrenciesRepository import com.tangem.domain.tokens.repository.MockNetworksRepository import com.tangem.domain.tokens.repository.MockQuotesRepository -import com.tangem.domain.tokens.repository.MockTokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import junit.framework.TestCase.assertEquals @@ -47,7 +47,7 @@ internal class GetPrimaryCurrencyUseCaseTest { @Test fun `when token getting failed then error should be received`() = runTest { // Given - val expectedResult = TokenError.DataError(DataError.NetworkError.NoInternetConnection).left() + val expectedResult = CurrencyError.DataError(DataError.NetworkError.NoInternetConnection).left() val useCase = getUseCase(token = DataError.NetworkError.NoInternetConnection.left()) @@ -61,7 +61,7 @@ internal class GetPrimaryCurrencyUseCaseTest { @Test fun `when quotes getting failed then error should be received`() = runTest { // Given - val expectedResult = TokenError.DataError(DataError.NetworkError.NoInternetConnection).left() + val expectedResult = CurrencyError.DataError(DataError.NetworkError.NoInternetConnection).left() val useCase = getUseCase(quotes = flowOf(DataError.NetworkError.NoInternetConnection.left())) @@ -75,7 +75,7 @@ internal class GetPrimaryCurrencyUseCaseTest { @Test fun `when networks statuses getting failed then error should be received`() = runTest { // Given - val expectedResult = TokenError.DataError(DataError.NetworkError.NoInternetConnection).left() + val expectedResult = CurrencyError.DataError(DataError.NetworkError.NoInternetConnection).left() val useCase = getUseCase(statuses = flowOf(DataError.NetworkError.NoInternetConnection.left())) @@ -88,7 +88,7 @@ internal class GetPrimaryCurrencyUseCaseTest { @Test fun `when networks statuses flow is empty then error should be received`() = runTest { - val expectedResult = TokenError.UnableToCreateToken.left() + val expectedResult = CurrencyError.UnableToCreateCurrency.left() val useCase = getUseCase(statuses = flowOf()) @@ -101,7 +101,7 @@ internal class GetPrimaryCurrencyUseCaseTest { @Test fun `when quotes flow is empty then error should be received`() = runTest { - val expectedResult = TokenError.UnableToCreateToken.left() + val expectedResult = CurrencyError.UnableToCreateCurrency.left() val useCase = getUseCase(quotes = flowOf()) @@ -154,7 +154,7 @@ internal class GetPrimaryCurrencyUseCaseTest { statuses: Flow>> = flowOf(MockNetworks.verifiedNetworksStatuses.right()), ) = GetPrimaryCurrencyUseCase( dispatchers = dispatchers, - tokensRepository = MockTokensRepository( + currenciesRepository = MockCurrenciesRepository( sortTokensResult = Unit.right(), token = token, tokens = flowOf(), diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt index 204bf4af9c..1d5cfd09c5 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt @@ -13,9 +13,9 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.model.Quote import com.tangem.domain.tokens.models.Network +import com.tangem.domain.tokens.repository.MockCurrenciesRepository import com.tangem.domain.tokens.repository.MockNetworksRepository import com.tangem.domain.tokens.repository.MockQuotesRepository -import com.tangem.domain.tokens.repository.MockTokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import junit.framework.TestCase.assertEquals @@ -326,7 +326,7 @@ internal class GetTokenListUseCaseTest { isSortedByBalance: Flow> = flowOf(MockTokenLists.isSortedByBalance.right()), ) = GetTokenListUseCase( dispatchers = dispatchers, - tokensRepository = MockTokensRepository( + currenciesRepository = MockCurrenciesRepository( sortTokensResult = Unit.right(), token = MockTokens.token1.right(), tokens = tokens, diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockTokensRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt similarity index 81% rename from domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockTokensRepository.kt rename to domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt index ad5d4e0851..8ebf978969 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockTokensRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt @@ -8,13 +8,13 @@ import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -internal class MockTokensRepository( +internal class MockCurrenciesRepository( private val sortTokensResult: Either, private val token: Either, private val tokens: Flow>>, private val isGrouped: Flow>, private val isSortedByBalance: Flow>, -) : TokensRepository { +) : CurrenciesRepository { var tokensIdsAfterSortingApply: Set? = null private set @@ -38,7 +38,7 @@ internal class MockTokensRepository( isTokensSortedByBalanceAfterSortingApply = isSortedByBalance } - override suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency { + override suspend fun getSingleCurrencyWalletPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency { return token.getOrElse { e -> throw e } } @@ -49,6 +49,17 @@ internal class MockTokensRepository( return tokens.map { it.getOrElse { e -> throw e } } } + override suspend fun getMultiCurrencyWalletCurrency( + userWalletId: UserWalletId, + id: CryptoCurrency.ID, + ): CryptoCurrency { + val token = token.getOrElse { e -> throw e } + + require(token.id == id) + + return token + } + override fun isTokensGrouped(userWalletId: UserWalletId): Flow { return isGrouped.map { it.getOrElse { e -> throw e } } }