Updated on 2026-08-14
This commit is contained in:
parent
8d3d9fc22e
commit
d677abc44d
30 changed files with 423 additions and 164 deletions
|
|
@ -15,6 +15,16 @@ import dagger.hilt.android.scopes.ViewModelScoped
|
|||
@InstallIn(ViewModelComponent::class)
|
||||
internal object TokensDomainModule {
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideFetchTokenListUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
): FetchTokenListUseCase {
|
||||
return FetchTokenListUseCase(currenciesRepository, networksRepository, quotesRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideGetTokenListUseCase(
|
||||
|
|
@ -42,8 +52,8 @@ internal object TokensDomainModule {
|
|||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetCurrencyUseCase {
|
||||
return GetCurrencyUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
|
||||
): GetCurrencyStatusUpdatesUseCase {
|
||||
return GetCurrencyStatusUpdatesUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
@ -53,8 +63,23 @@ internal object TokensDomainModule {
|
|||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetPrimaryCurrencyUseCase {
|
||||
return GetPrimaryCurrencyUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers)
|
||||
): GetPrimaryCurrencyStatusUpdatesUseCase {
|
||||
return GetPrimaryCurrencyStatusUpdatesUseCase(
|
||||
currenciesRepository,
|
||||
quotesRepository,
|
||||
networksRepository,
|
||||
dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideFetchCurrencyStatusUseCase(
|
||||
currenciesRepository: CurrenciesRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
): FetchCurrencyStatusUseCase {
|
||||
return FetchCurrencyStatusUseCase(currenciesRepository, networksRepository, quotesRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ interface SelectedAppCurrencyStore {
|
|||
|
||||
fun get(): Flow<CurrenciesResponse.Currency>
|
||||
|
||||
suspend fun getSyncOrNull(): CurrenciesResponse.Currency?
|
||||
|
||||
suspend fun store(item: CurrenciesResponse.Currency)
|
||||
|
||||
suspend fun isEmpty(): Boolean
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import com.tangem.datasource.local.datastore.core.StringKeyDataStore
|
|||
internal class DefaultSelectedAppCurrencyStore(
|
||||
dataStore: StringKeyDataStore<CurrenciesResponse.Currency>,
|
||||
) : SelectedAppCurrencyStore, KeylessDataStoreDecorator<CurrenciesResponse.Currency>(dataStore) {
|
||||
|
||||
override suspend fun isEmpty(): Boolean {
|
||||
return getSyncOrNull() == null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,19 +78,18 @@ internal class DefaultCurrenciesRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getMultiCurrencyWalletCurrencies(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean,
|
||||
): Flow<List<CryptoCurrency>> = channelFlow {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true)
|
||||
override fun getMultiCurrencyWalletCurrenciesUpdates(userWalletId: UserWalletId): Flow<List<CryptoCurrency>> {
|
||||
return channelFlow {
|
||||
val userWallet = getUserWallet(userWalletId)
|
||||
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true)
|
||||
|
||||
launch(dispatchers.io) {
|
||||
getMultiCurrencyWalletCurrencies(userWallet).collect(::send)
|
||||
}
|
||||
launch(dispatchers.io) {
|
||||
getMultiCurrencyWalletCurrencies(userWallet).collect(::send)
|
||||
}
|
||||
|
||||
launch(dispatchers.io) {
|
||||
fetchTokensIfCacheExpired(userWallet, refresh)
|
||||
launch(dispatchers.io) {
|
||||
fetchTokensIfCacheExpired(userWallet, refresh = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +101,11 @@ internal class DefaultCurrenciesRepository(
|
|||
ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true)
|
||||
|
||||
fetchTokensIfCacheExpired(userWallet, refresh)
|
||||
val storedTokens = requireNotNull(userTokensStore.getSyncOrNull(userWallet.walletId))
|
||||
|
||||
val storedTokens = requireNotNull(userTokensStore.getSyncOrNull(userWallet.walletId)) {
|
||||
"Unable to find tokens response for user wallet with provided ID: $userWalletId"
|
||||
}
|
||||
|
||||
return responseCurrenciesFactory.createCurrencies(
|
||||
response = storedTokens,
|
||||
card = userWallet.scanResponse.card,
|
||||
|
|
|
|||
|
|
@ -16,14 +16,8 @@ import com.tangem.domain.walletmanager.WalletManagersFacade
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.addOrReplace
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
internal class DefaultNetworksRepository(
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
|
|
@ -45,10 +39,9 @@ internal class DefaultNetworksRepository(
|
|||
return networkConverter.convertSet(networksIds)
|
||||
}
|
||||
|
||||
override fun getNetworkStatuses(
|
||||
override fun getNetworkStatusesUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
refresh: Boolean,
|
||||
): Flow<Set<NetworkStatus>> = channelFlow {
|
||||
launch(dispatchers.io) {
|
||||
networksStatuses.collect {
|
||||
|
|
@ -57,10 +50,19 @@ internal class DefaultNetworksRepository(
|
|||
}
|
||||
|
||||
launch(dispatchers.io) {
|
||||
fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh)
|
||||
fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh = false)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getNetworkStatusesSync(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
refresh: Boolean,
|
||||
): Set<NetworkStatus> = withContext(dispatchers.io) {
|
||||
fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh)
|
||||
networksStatuses.first().toSet()
|
||||
}
|
||||
|
||||
private suspend fun fetchNetworksStatusesIfCacheExpired(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
|
|
|
|||
|
|
@ -9,11 +9,9 @@ import com.tangem.domain.tokens.models.CryptoCurrency
|
|||
import com.tangem.domain.tokens.models.Quote
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
internal class DefaultQuotesRepository(
|
||||
|
|
@ -28,7 +26,7 @@ internal class DefaultQuotesRepository(
|
|||
|
||||
private var quotesFetchedForAppCurrency: String? = null
|
||||
|
||||
override fun getQuotes(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean): Flow<Set<Quote>> {
|
||||
override fun getQuotesUpdates(currenciesIds: Set<CryptoCurrency.ID>): Flow<Set<Quote>> {
|
||||
return channelFlow {
|
||||
launch(dispatchers.io) {
|
||||
quotesStore.get(currenciesIds)
|
||||
|
|
@ -38,12 +36,26 @@ internal class DefaultQuotesRepository(
|
|||
|
||||
launch(dispatchers.io) {
|
||||
selectedAppCurrencyStore.get().collectLatest { appCurrency ->
|
||||
fetchExpiredQuotes(currenciesIds, appCurrency.id, refresh)
|
||||
fetchExpiredQuotes(currenciesIds, appCurrency.id, refresh = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getQuotesSync(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean): Set<Quote> {
|
||||
return withContext(dispatchers.io) {
|
||||
val selectedAppCurrency = requireNotNull(selectedAppCurrencyStore.getSyncOrNull()) {
|
||||
"Unable to get selected application currency to update quotes"
|
||||
}
|
||||
|
||||
fetchExpiredQuotes(currenciesIds, selectedAppCurrency.id, refresh)
|
||||
|
||||
val quotes = quotesStore.get(currenciesIds).first()
|
||||
|
||||
quotesConverter.convertSet(quotes)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchExpiredQuotes(
|
||||
currenciesIds: Set<CryptoCurrency.ID>,
|
||||
appCurrencyId: String,
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ class ApplyTokenListSortingUseCase(
|
|||
private suspend fun Raise<TokenListSortingError>.getCurrencies(userWalletId: UserWalletId): List<CryptoCurrency> {
|
||||
val tokens = catch(
|
||||
block = {
|
||||
currenciesRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh = false).firstOrNull()
|
||||
currenciesRepository.getMultiCurrencyWalletCurrenciesUpdates(userWalletId).firstOrNull()
|
||||
},
|
||||
catch = { raise(TokenListSortingError.DataError(it)) },
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.Raise
|
||||
import arrow.core.raise.catch
|
||||
import arrow.core.raise.either
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
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.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
||||
/**
|
||||
* Use case responsible for fetching currency status information, including network status
|
||||
* and quotes for a given cryptocurrency. It provides methods to fetch currency status either
|
||||
* by providing a specific currency ID or fetching the status of the primary currency.
|
||||
*
|
||||
* @param currenciesRepository The repository for retrieving currency-related data.
|
||||
* @param networksRepository The repository for retrieving network-related data.
|
||||
* @param quotesRepository The repository for retrieving cryptocurrency quotes.
|
||||
*/
|
||||
// TODO: Add tests
|
||||
class FetchCurrencyStatusUseCase(
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Fetches the status of a specific cryptocurrency for a given user wallet.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param id The ID of the cryptocurrency.
|
||||
* @param refresh Indicates whether to force a refresh of the status data.
|
||||
* @return An [Either] representing success (Right) or an error (Left) in fetching the status.
|
||||
*/
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
id: CryptoCurrency.ID,
|
||||
refresh: Boolean = false,
|
||||
): Either<CurrencyStatusError, Unit> {
|
||||
return either {
|
||||
val currency = getCurrency(userWalletId, id)
|
||||
|
||||
fetchCurrencyStatus(userWalletId, currency, refresh)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the status of the primary cryptocurrency for a given user wallet.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param refresh Indicates whether to force a refresh of the status data.
|
||||
* @return An [Either] representing success (Right) or an error (Left) in fetching the status.
|
||||
*/
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean = false,
|
||||
): Either<CurrencyStatusError, Unit> {
|
||||
return either {
|
||||
val currency = getPrimaryCurrency(userWalletId)
|
||||
|
||||
fetchCurrencyStatus(userWalletId, currency, refresh)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<CurrencyStatusError>.fetchCurrencyStatus(
|
||||
userWalletId: UserWalletId,
|
||||
currency: CryptoCurrency,
|
||||
refresh: Boolean,
|
||||
) = coroutineScope {
|
||||
val fetchStatus = async {
|
||||
fetchNetworkStatus(userWalletId, currency.network.id, refresh)
|
||||
}
|
||||
val fetchQuote = async {
|
||||
fetchQuote(currency.id, refresh)
|
||||
}
|
||||
|
||||
awaitAll(fetchStatus, fetchQuote)
|
||||
}
|
||||
|
||||
private suspend fun Raise<CurrencyStatusError>.getCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
id: CryptoCurrency.ID,
|
||||
): CryptoCurrency {
|
||||
return catch({ currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, id) }) {
|
||||
raise(CurrencyStatusError.DataError(it))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<CurrencyStatusError>.getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency {
|
||||
return catch({ currenciesRepository.getSingleCurrencyWalletPrimaryCurrency(userWalletId) }) {
|
||||
raise(CurrencyStatusError.DataError(it))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<CurrencyStatusError>.fetchNetworkStatus(
|
||||
userWalletId: UserWalletId,
|
||||
networkId: Network.ID,
|
||||
refresh: Boolean,
|
||||
) {
|
||||
catch(
|
||||
block = { networksRepository.getNetworkStatusesSync(userWalletId, setOf(networkId), refresh) },
|
||||
) {
|
||||
raise(CurrencyStatusError.DataError(it))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<CurrencyStatusError>.fetchQuote(currencyId: CryptoCurrency.ID, refresh: Boolean) {
|
||||
catch(
|
||||
block = { quotesRepository.getQuotesSync(setOf(currencyId), refresh) },
|
||||
) {
|
||||
raise(CurrencyStatusError.DataError(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.raise.Raise
|
||||
import arrow.core.raise.catch
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.raise.ensureNotNull
|
||||
import arrow.core.toNonEmptyListOrNull
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
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.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
||||
/**
|
||||
* Use case responsible for fetching token list information, including currency data,
|
||||
* network statuses, and quotes for tokens associated with a user's wallet.
|
||||
*
|
||||
* @param currenciesRepository The repository for retrieving currency-related data.
|
||||
* @param networksRepository The repository for retrieving network-related data.
|
||||
* @param quotesRepository The repository for retrieving cryptocurrency quotes.
|
||||
*/
|
||||
// TODO: Add tests
|
||||
class FetchTokenListUseCase(
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Fetches the token list information for a user's wallet, including currency data,
|
||||
* network statuses, and quotes for associated tokens.
|
||||
*
|
||||
* @param userWalletId The ID of the user's wallet.
|
||||
* @param refresh Indicates whether to force a refresh of the token list data.
|
||||
* @return An [Either] representing success (Right) or an error (Left) in fetching the token list.
|
||||
*/
|
||||
suspend operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = false): Either<TokenListError, Unit> {
|
||||
return either {
|
||||
val currencies = fetchCurrencies(userWalletId, refresh)
|
||||
|
||||
coroutineScope {
|
||||
val fetchStatuses = async {
|
||||
fetchNetworksStatuses(
|
||||
userWalletId,
|
||||
currencies.mapTo(hashSetOf()) { it.network.id },
|
||||
refresh,
|
||||
)
|
||||
}
|
||||
val fetchQuotes = async {
|
||||
fetchQuotes(
|
||||
currencies.mapTo(hashSetOf()) { it.id },
|
||||
refresh,
|
||||
)
|
||||
}
|
||||
|
||||
awaitAll(fetchStatuses, fetchQuotes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<TokenListError>.fetchCurrencies(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean,
|
||||
): List<CryptoCurrency> {
|
||||
val currencies = catch(
|
||||
block = { currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId, refresh) },
|
||||
) {
|
||||
raise(TokenListError.DataError(it))
|
||||
}
|
||||
|
||||
return ensureNotNull(currencies.toNonEmptyListOrNull()) {
|
||||
TokenListError.EmptyTokens
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<TokenListError>.fetchNetworksStatuses(
|
||||
userWalletId: UserWalletId,
|
||||
networksIds: Set<Network.ID>,
|
||||
refresh: Boolean,
|
||||
) {
|
||||
catch(
|
||||
block = { networksRepository.getNetworkStatusesSync(userWalletId, networksIds, refresh) },
|
||||
) {
|
||||
raise(TokenListError.DataError(it))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<TokenListError>.fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean) {
|
||||
catch(
|
||||
block = { quotesRepository.getQuotesSync(currenciesIds, refresh) },
|
||||
) {
|
||||
raise(TokenListError.DataError(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.tokens.error.CurrencyError
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
|
|
@ -14,14 +14,13 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|||
import kotlinx.coroutines.flow.*
|
||||
|
||||
/**
|
||||
* Use case for fetching the status of a specific cryptocurrency associated with a user wallet.
|
||||
* Use case for fetching the status of a 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(
|
||||
class GetCurrencyStatusUpdatesUseCase(
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
|
|
@ -33,30 +32,26 @@ class GetCurrencyUseCase(
|
|||
*
|
||||
* @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.
|
||||
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
|
||||
*/
|
||||
operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
currencyId: CryptoCurrency.ID,
|
||||
refresh: Boolean = false,
|
||||
): Flow<Either<CurrencyError, CryptoCurrencyStatus>> {
|
||||
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
|
||||
return flow {
|
||||
emitAll(getCurrency(userWalletId, currencyId, refresh))
|
||||
emitAll(getCurrency(userWalletId, currencyId))
|
||||
}.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
private suspend fun getCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
currencyId: CryptoCurrency.ID,
|
||||
refresh: Boolean,
|
||||
): Flow<Either<CurrencyError, CryptoCurrencyStatus>> {
|
||||
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
|
||||
val operations = CurrenciesStatusesOperations(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
userWalletId = userWalletId,
|
||||
refresh = refresh,
|
||||
)
|
||||
|
||||
return operations.getCurrencyStatusFlow(currencyId).map { maybeCurrency ->
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.tokens.error.CurrencyError
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
|
|
@ -20,7 +20,7 @@ import kotlinx.coroutines.flow.*
|
|||
* @property networksRepository Repository for managing and fetching information related to blockchain networks.
|
||||
* @property dispatchers Provides coroutine dispatchers.
|
||||
*/
|
||||
class GetPrimaryCurrencyUseCase(
|
||||
class GetPrimaryCurrencyStatusUpdatesUseCase(
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
|
|
@ -32,27 +32,22 @@ class GetPrimaryCurrencyUseCase(
|
|||
*
|
||||
* @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.
|
||||
* @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation.
|
||||
*/
|
||||
operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean = false,
|
||||
): Flow<Either<CurrencyError, CryptoCurrencyStatus>> {
|
||||
operator fun invoke(userWalletId: UserWalletId): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
|
||||
return flow {
|
||||
emitAll(getPrimaryCurrency(userWalletId, refresh))
|
||||
emitAll(getPrimaryCurrency(userWalletId))
|
||||
}.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
private suspend fun getPrimaryCurrency(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean,
|
||||
): Flow<Either<CurrencyError, CryptoCurrencyStatus>> {
|
||||
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
|
||||
val operations = CurrenciesStatusesOperations(
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
userWalletId = userWalletId,
|
||||
refresh = refresh,
|
||||
)
|
||||
|
||||
return operations.getPrimaryCurrencyStatusFlow().map { maybeCurrency ->
|
||||
|
|
@ -14,7 +14,10 @@ import com.tangem.domain.tokens.repository.QuotesRepository
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flatMapMerge
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class GetTokenListUseCase(
|
||||
internal val currenciesRepository: CurrenciesRepository,
|
||||
|
|
@ -24,8 +27,8 @@ class GetTokenListUseCase(
|
|||
) {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = false): Flow<Either<TokenListError, TokenList>> {
|
||||
return getTokensStatuses(userWalletId, refresh).flatMapMerge { maybeTokens ->
|
||||
operator fun invoke(userWalletId: UserWalletId): Flow<Either<TokenListError, TokenList>> {
|
||||
return getTokensStatuses(userWalletId).flatMapMerge { maybeTokens ->
|
||||
maybeTokens.fold(
|
||||
ifLeft = { error ->
|
||||
flowOf(error.left())
|
||||
|
|
@ -39,11 +42,9 @@ class GetTokenListUseCase(
|
|||
|
||||
private fun getTokensStatuses(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean,
|
||||
): Flow<Either<TokenListError, List<CryptoCurrencyStatus>>> {
|
||||
val operations = CurrenciesStatusesOperations(
|
||||
userWalletId = userWalletId,
|
||||
refresh = refresh,
|
||||
useCase = this@GetTokenListUseCase,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
package com.tangem.domain.tokens.error
|
||||
|
||||
sealed class CurrencyError {
|
||||
|
||||
object UnableToCreateCurrency : CurrencyError()
|
||||
|
||||
data class DataError(val cause: Throwable) : CurrencyError()
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.domain.tokens.error
|
||||
|
||||
sealed class CurrencyStatusError {
|
||||
|
||||
object UnableToCreateCurrency : CurrencyStatusError()
|
||||
|
||||
data class DataError(val cause: Throwable) : CurrencyStatusError()
|
||||
}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
package com.tangem.domain.tokens.error.mapper
|
||||
|
||||
import com.tangem.domain.tokens.error.CurrencyError
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
|
||||
internal fun CurrenciesStatusesOperations.Error.mapToCurrencyError(): CurrencyError {
|
||||
internal fun CurrenciesStatusesOperations.Error.mapToCurrencyError(): CurrencyStatusError {
|
||||
return when (this) {
|
||||
is CurrenciesStatusesOperations.Error.DataError -> CurrencyError.DataError(this.cause)
|
||||
is CurrenciesStatusesOperations.Error.DataError -> CurrencyStatusError.DataError(this.cause)
|
||||
is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses,
|
||||
is CurrenciesStatusesOperations.Error.EmptyQuotes,
|
||||
is CurrenciesStatusesOperations.Error.EmptyCurrencies,
|
||||
is CurrenciesStatusesOperations.Error.UnableToCreateCurrencyStatus,
|
||||
-> CurrencyError.UnableToCreateCurrency
|
||||
-> CurrencyStatusError.UnableToCreateCurrency
|
||||
}
|
||||
}
|
||||
|
|
@ -20,19 +20,16 @@ internal class CurrenciesStatusesOperations(
|
|||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val userWalletId: UserWalletId,
|
||||
private val refresh: Boolean,
|
||||
) {
|
||||
|
||||
constructor(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean,
|
||||
useCase: GetTokenListUseCase,
|
||||
) : this(
|
||||
currenciesRepository = useCase.currenciesRepository,
|
||||
quotesRepository = useCase.quotesRepository,
|
||||
networksRepository = useCase.networksRepository,
|
||||
userWalletId = userWalletId,
|
||||
refresh = refresh,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
|
@ -51,16 +48,16 @@ internal class CurrenciesStatusesOperations(
|
|||
|
||||
emit(emptyCurrenciesStatuses.right())
|
||||
return@transformLatest
|
||||
} else if (!refresh) {
|
||||
val maybeLoadingCurrenciesStatuses = createCurrenciesStatuses(
|
||||
currencies = nonEmptyCurrencies,
|
||||
maybeNetworkStatuses = null,
|
||||
maybeQuotes = null,
|
||||
)
|
||||
|
||||
emit(maybeLoadingCurrenciesStatuses)
|
||||
}
|
||||
|
||||
val maybeLoadingCurrenciesStatuses = createCurrenciesStatuses(
|
||||
currencies = nonEmptyCurrencies,
|
||||
maybeNetworkStatuses = null,
|
||||
maybeQuotes = null,
|
||||
)
|
||||
|
||||
emit(maybeLoadingCurrenciesStatuses)
|
||||
|
||||
val (networksIds, currenciesIds) = getIds(nonEmptyCurrencies)
|
||||
|
||||
val currenciesFlow = combine(
|
||||
|
|
@ -168,7 +165,7 @@ internal class CurrenciesStatusesOperations(
|
|||
}
|
||||
|
||||
private fun getMultiCurrencyWalletCurrencies(): Flow<Either<Error, List<CryptoCurrency>>> {
|
||||
return currenciesRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh)
|
||||
return currenciesRepository.getMultiCurrencyWalletCurrenciesUpdates(userWalletId)
|
||||
.map<List<CryptoCurrency>, Either<Error, List<CryptoCurrency>>> { it.right() }
|
||||
.catch { emit(Error.DataError(it).left()) }
|
||||
.onEmpty { emit(Error.EmptyCurrencies.left()) }
|
||||
|
|
@ -188,14 +185,14 @@ internal class CurrenciesStatusesOperations(
|
|||
}
|
||||
|
||||
private fun getQuotes(tokensIds: NonEmptySet<CryptoCurrency.ID>): Flow<Either<Error, Set<Quote>>> {
|
||||
return quotesRepository.getQuotes(tokensIds, refresh)
|
||||
return quotesRepository.getQuotesUpdates(tokensIds)
|
||||
.map<Set<Quote>, Either<Error, Set<Quote>>> { it.right() }
|
||||
.catch { emit(Error.DataError(it).left()) }
|
||||
.onEmpty { emit(Error.EmptyQuotes.left()) }
|
||||
}
|
||||
|
||||
private fun getNetworksStatuses(networks: NonEmptySet<Network.ID>): Flow<Either<Error, Set<NetworkStatus>>> {
|
||||
return networksRepository.getNetworkStatuses(userWalletId, networks, refresh)
|
||||
return networksRepository.getNetworkStatusesUpdates(userWalletId, networks)
|
||||
.map<Set<NetworkStatus>, Either<Error, Set<NetworkStatus>>> { it.right() }
|
||||
.catch { emit(Error.DataError(it).left()) }
|
||||
.onEmpty { emit(Error.EmptyNetworksStatuses.left()) }
|
||||
|
|
|
|||
|
|
@ -48,19 +48,22 @@ interface CurrenciesRepository {
|
|||
suspend fun getSingleCurrencyWalletPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency
|
||||
|
||||
/**
|
||||
* Retrieves the list of cryptocurrencies within a multi-currency wallet.
|
||||
* Retrieves updates of the list of cryptocurrencies within a multi-currency wallet.
|
||||
*
|
||||
* Loads remote cryptocurrencies if they have expired.
|
||||
*
|
||||
* @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<List<CryptoCurrency>>
|
||||
fun getMultiCurrencyWalletCurrenciesUpdates(userWalletId: UserWalletId): Flow<List<CryptoCurrency>>
|
||||
|
||||
/**
|
||||
* Retrieves the list of cryptocurrencies within a multi-currency wallet.
|
||||
*
|
||||
* Loads cryptocurrencies if they have expired or if [refresh] is `true`.
|
||||
*
|
||||
* @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].
|
||||
|
|
|
|||
|
|
@ -19,16 +19,29 @@ interface NetworksRepository {
|
|||
fun getNetworks(networksIds: Set<Network.ID>): Set<Network>
|
||||
|
||||
/**
|
||||
* Retrieves the statuses of specified blockchain networks for a specific user wallet.
|
||||
* Retrieves updates of network statuses of specified blockchain networks for a specific user wallet.
|
||||
*
|
||||
* Loads remote network statuses if they have expired.
|
||||
*
|
||||
* @param userWalletId The unique identifier of the user wallet.
|
||||
* @param networks A set of network IDs which statuses are to be retrieved.
|
||||
* @return A [Flow] emitting a set of [NetworkStatus] objects corresponding to the specified networks.
|
||||
*/
|
||||
fun getNetworkStatusesUpdates(userWalletId: UserWalletId, networks: Set<Network.ID>): Flow<Set<NetworkStatus>>
|
||||
|
||||
/**
|
||||
* Retrieves network statuses of specified blockchain networks for a specific user wallet.
|
||||
*
|
||||
* Loads remote network statuses if they have expired or if [refresh] is `true`.
|
||||
*
|
||||
* @param userWalletId The unique identifier of the user wallet.
|
||||
* @param networks A set of network IDs which statuses are to be retrieved.
|
||||
* @param refresh A boolean flag indicating whether the data should be refreshed.
|
||||
* @return A [Flow] emitting a set of [NetworkStatus] objects corresponding to the specified networks.
|
||||
*/
|
||||
fun getNetworkStatuses(
|
||||
suspend fun getNetworkStatusesSync(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
refresh: Boolean,
|
||||
): Flow<Set<NetworkStatus>>
|
||||
): Set<NetworkStatus>
|
||||
}
|
||||
|
|
@ -10,11 +10,23 @@ import kotlinx.coroutines.flow.Flow
|
|||
interface QuotesRepository {
|
||||
|
||||
/**
|
||||
* Retrieves the quotes for a set of specified cryptocurrencies, identified by their unique IDs.
|
||||
* Retrieves updates of quotes for a set of specified cryptocurrencies, identified by their unique IDs.
|
||||
*
|
||||
* Loads remote quotes if they have expired.
|
||||
*
|
||||
* @param currenciesIds The unique identifiers of the cryptocurrencies for which quotes are to be retrieved.
|
||||
* @return A [Flow] emitting a set of quotes corresponding to the specified cryptocurrencies.
|
||||
*/
|
||||
fun getQuotesUpdates(currenciesIds: Set<CryptoCurrency.ID>): Flow<Set<Quote>>
|
||||
|
||||
/**
|
||||
* Retrieves quotes for a set of specified cryptocurrencies, identified by their unique IDs.
|
||||
*
|
||||
* Loads remote quotes if they have expired or if [refresh] is `true`.
|
||||
*
|
||||
* @param currenciesIds The unique identifiers of the cryptocurrencies for which quotes are to be retrieved.
|
||||
* @param refresh A boolean flag indicating whether the data should be refreshed.
|
||||
* @return A [Flow] emitting a set of quotes corresponding to the specified cryptocurrencies.
|
||||
*/
|
||||
fun getQuotes(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean): Flow<Set<Quote>>
|
||||
suspend fun getQuotesSync(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean): Set<Quote>
|
||||
}
|
||||
|
|
@ -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.CurrencyError
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.mock.MockNetworks
|
||||
import com.tangem.domain.tokens.mock.MockQuotes
|
||||
import com.tangem.domain.tokens.mock.MockTokens
|
||||
|
|
@ -25,7 +25,7 @@ import kotlinx.coroutines.flow.flowOf
|
|||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
internal class GetPrimaryCurrencyUseCaseTest {
|
||||
internal class GetPrimaryCurrencyStatusUpdatesUseCaseTest {
|
||||
|
||||
private val dispatchers = TestingCoroutineDispatcherProvider()
|
||||
private val userWalletId = UserWalletId(value = null)
|
||||
|
|
@ -47,7 +47,7 @@ internal class GetPrimaryCurrencyUseCaseTest {
|
|||
@Test
|
||||
fun `when token getting failed then error should be received`() = runTest {
|
||||
// Given
|
||||
val expectedResult = CurrencyError.DataError(DataError.NetworkError.NoInternetConnection).left()
|
||||
val expectedResult = CurrencyStatusError.DataError(DataError.NetworkError.NoInternetConnection).left()
|
||||
|
||||
val useCase = getUseCase(token = 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 = CurrencyError.DataError(DataError.NetworkError.NoInternetConnection).left()
|
||||
val expectedResult = CurrencyStatusError.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 = CurrencyError.UnableToCreateCurrency.left()
|
||||
val expectedResult = CurrencyStatusError.UnableToCreateCurrency.left()
|
||||
|
||||
val useCase = getUseCase(statuses = flowOf())
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ internal class GetPrimaryCurrencyUseCaseTest {
|
|||
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(
|
||||
) = GetPrimaryCurrencyStatusUpdatesUseCase(
|
||||
dispatchers = dispatchers,
|
||||
currenciesRepository = MockCurrenciesRepository(
|
||||
sortTokensResult = Unit.right(),
|
||||
|
|
@ -51,27 +51,6 @@ internal class GetTokenListUseCaseTest {
|
|||
assertEquals(expectedResult, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when list refreshed then correct token list should be returned`() = runTest {
|
||||
// Given
|
||||
val expectedResult = listOf(
|
||||
MockTokenLists.failedUngroupedTokenList.right(),
|
||||
)
|
||||
|
||||
val useCase = getUseCase(
|
||||
isGrouped = flowOf(false.right()),
|
||||
isSortedByBalance = flowOf(false.right()),
|
||||
)
|
||||
|
||||
// When
|
||||
val result = useCase(userWalletId, refresh = true)
|
||||
.take(count = 1)
|
||||
.toList()
|
||||
|
||||
// Then
|
||||
assertEquals(expectedResult, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when tokens getting failed then error should be received`() = runTest {
|
||||
// Given
|
||||
|
|
@ -108,20 +87,6 @@ internal class GetTokenListUseCaseTest {
|
|||
assertEquals(expectedResult, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when networks statuses getting failed then error should be received`() = runTest {
|
||||
// Given
|
||||
val expectedResult = TokenListError.DataError(DataError.NetworkError.NoInternetConnection).left()
|
||||
|
||||
val useCase = getUseCase(statuses = flowOf(DataError.NetworkError.NoInternetConnection.left()))
|
||||
|
||||
// When
|
||||
val result = useCase(userWalletId, refresh = true).first()
|
||||
|
||||
// Then
|
||||
assertEquals(expectedResult, result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when grouping type getting failed then error should be received`() = runTest {
|
||||
// Given
|
||||
|
|
|
|||
|
|
@ -55,10 +55,7 @@ internal class MockCurrenciesRepository(
|
|||
return token.getOrElse { e -> throw e }
|
||||
}
|
||||
|
||||
override fun getMultiCurrencyWalletCurrencies(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean,
|
||||
): Flow<List<CryptoCurrency>> {
|
||||
override fun getMultiCurrencyWalletCurrenciesUpdates(userWalletId: UserWalletId): Flow<List<CryptoCurrency>> {
|
||||
return tokens.map { it.getOrElse { e -> throw e } }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.domain.tokens.model.NetworkStatus
|
|||
import com.tangem.domain.tokens.models.Network
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
internal class MockNetworksRepository(
|
||||
|
|
@ -18,11 +19,18 @@ internal class MockNetworksRepository(
|
|||
return networks.getOrElse { throw it }
|
||||
}
|
||||
|
||||
override fun getNetworkStatuses(
|
||||
override fun getNetworkStatusesUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
refresh: Boolean,
|
||||
): Flow<Set<NetworkStatus>> {
|
||||
return statuses.map { it.getOrElse { e -> throw e } }
|
||||
}
|
||||
|
||||
override suspend fun getNetworkStatusesSync(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network.ID>,
|
||||
refresh: Boolean,
|
||||
): Set<NetworkStatus> {
|
||||
return getNetworkStatusesUpdates(userWalletId, networks).first()
|
||||
}
|
||||
}
|
||||
|
|
@ -6,13 +6,18 @@ import com.tangem.domain.core.error.DataError
|
|||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.domain.tokens.models.Quote
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
internal class MockQuotesRepository(
|
||||
private val quotes: Flow<Either<DataError, Set<Quote>>>,
|
||||
) : QuotesRepository {
|
||||
|
||||
override fun getQuotes(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean): Flow<Set<Quote>> {
|
||||
override fun getQuotesUpdates(currenciesIds: Set<CryptoCurrency.ID>): Flow<Set<Quote>> {
|
||||
return quotes.map { it.getOrElse { e -> throw e } }
|
||||
}
|
||||
|
||||
override suspend fun getQuotesSync(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean): Set<Quote> {
|
||||
return getQuotesUpdates(currenciesIds).first()
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
|||
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.error.CurrencyError
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
|
|
@ -16,9 +16,9 @@ import java.math.BigDecimal
|
|||
internal class TokenDetailsLoadedBalanceConverter(
|
||||
private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
) : Converter<Either<CurrencyError, CryptoCurrencyStatus>, TokenDetailsState> {
|
||||
) : Converter<Either<CurrencyStatusError, CryptoCurrencyStatus>, TokenDetailsState> {
|
||||
|
||||
override fun convert(value: Either<CurrencyError, CryptoCurrencyStatus>): TokenDetailsState {
|
||||
override fun convert(value: Either<CurrencyStatusError, CryptoCurrencyStatus>): TokenDetailsState {
|
||||
return value.fold(ifLeft = { convertError() }, ifRight = ::convert)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import androidx.paging.PagingData
|
|||
import arrow.core.Either
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.error.CurrencyError
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
|
|
@ -63,7 +63,7 @@ internal class TokenDetailsStateFactory(
|
|||
}
|
||||
|
||||
fun getCurrencyLoadedBalanceState(
|
||||
cryptoCurrencyEither: Either<CurrencyError, CryptoCurrencyStatus>,
|
||||
cryptoCurrencyEither: Either<CurrencyStatusError, CryptoCurrencyStatus>,
|
||||
): TokenDetailsState {
|
||||
return tokenDetailsLoadedBalanceConverter.convert(cryptoCurrencyEither)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||
import com.tangem.domain.tokens.GetCurrencyUseCase
|
||||
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
|
|
@ -39,7 +39,7 @@ import kotlin.properties.Delegates
|
|||
internal class TokenDetailsViewModel @Inject constructor(
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
|
||||
private val getCurrencyUseCase: GetCurrencyUseCase,
|
||||
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
|
|
@ -71,7 +71,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
getWallet()
|
||||
updateContent(selectedWallet = wallet, refresh = false)
|
||||
updateContent(selectedWallet = wallet)
|
||||
}
|
||||
|
||||
private fun getWallet() {
|
||||
|
|
@ -82,8 +82,8 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun updateContent(selectedWallet: UserWallet, refresh: Boolean) {
|
||||
updateMarketPrice(selectedWallet = selectedWallet, refresh = refresh)
|
||||
private fun updateContent(selectedWallet: UserWallet) {
|
||||
updateMarketPrice(selectedWallet = selectedWallet)
|
||||
updateButtons(userWalletId = selectedWallet.walletId, currencyId = cryptoCurrency.id.value)
|
||||
updateTxHistory()
|
||||
}
|
||||
|
|
@ -96,8 +96,11 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun updateMarketPrice(selectedWallet: UserWallet, refresh: Boolean) {
|
||||
getCurrencyUseCase(userWalletId = selectedWallet.walletId, currencyId = cryptoCurrency.id, refresh = refresh)
|
||||
private fun updateMarketPrice(selectedWallet: UserWallet) {
|
||||
getCurrencyStatusUpdatesUseCase(
|
||||
userWalletId = selectedWallet.walletId,
|
||||
currencyId = cryptoCurrency.id,
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.onEach { either ->
|
||||
uiState = stateFactory.getCurrencyLoadedBalanceState(either)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.core.ui.components.marketprice.PriceChangeConfig
|
|||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.tokens.error.CurrencyError
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
|
||||
|
|
@ -170,7 +170,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
|
|||
}
|
||||
|
||||
data class SingleCurrencyLoadedBalanceModel(
|
||||
val cryptoCurrencyEither: Either<CurrencyError, CryptoCurrencyStatus>,
|
||||
val cryptoCurrencyEither: Either<CurrencyStatusError, CryptoCurrencyStatus>,
|
||||
val isRefreshing: Boolean,
|
||||
)
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import arrow.core.Either
|
|||
import com.tangem.common.Provider
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.tokens.error.CurrencyError
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
|
|
@ -199,7 +199,7 @@ internal class WalletStateFactory(
|
|||
fun getLockedState(): WalletState = lockedConverter.convert(Unit)
|
||||
|
||||
fun getSingleCurrencyLoadedBalanceState(
|
||||
cryptoCurrencyEither: Either<CurrencyError, CryptoCurrencyStatus>,
|
||||
cryptoCurrencyEither: Either<CurrencyStatusError, CryptoCurrencyStatus>,
|
||||
isRefreshing: Boolean,
|
||||
): WalletState {
|
||||
return singleCurrencyLoadedBalanceConverter.convert(
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import com.tangem.domain.settings.CanUseBiometryUseCase
|
|||
import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase
|
||||
import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||
import com.tangem.domain.tokens.GetPrimaryCurrencyUseCase
|
||||
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
|
|
@ -73,7 +73,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val setAccessCodeRequestPolicyUseCase: SetAccessCodeRequestPolicyUseCase,
|
||||
private val getAccessCodeSavingStatusUseCase: GetAccessCodeSavingStatusUseCase,
|
||||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
private val getPrimaryCurrencyUseCase: GetPrimaryCurrencyUseCase,
|
||||
private val getPrimaryCurrencyUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
|
||||
private val getCardWasScannedUseCase: GetCardWasScannedUseCase,
|
||||
private val isUserAlreadyRateAppUseCase: IsUserAlreadyRateAppUseCase,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
|
|
@ -180,7 +180,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
"Impossible to update tokens list if state isn't WalletMultiCurrencyState"
|
||||
}
|
||||
|
||||
getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[index].id, refresh = isRefreshing)
|
||||
getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[index].id)
|
||||
.distinctUntilChanged()
|
||||
.onEach { tokenListEither ->
|
||||
uiState = stateFactory.getStateByTokensList(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue