Updated on 2026-08-14

This commit is contained in:
Tangem 2025-11-20 12:20:41 +04:00
parent 7cb4b57fe9
commit cf79b3b9a8
6 changed files with 49 additions and 14 deletions

View file

@ -38,9 +38,13 @@ internal object AccountDomainModule {
@Provides
@Singleton
fun provideUpdateCryptoPortfolioUseCase(
singleAccountListFetcher: SingleAccountListFetcher,
accountsCRUDRepository: AccountsCRUDRepository,
): UpdateCryptoPortfolioUseCase {
return UpdateCryptoPortfolioUseCase(crudRepository = accountsCRUDRepository)
return UpdateCryptoPortfolioUseCase(
singleAccountListFetcher = singleAccountListFetcher,
crudRepository = accountsCRUDRepository,
)
}
@Provides

View file

@ -54,9 +54,10 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
override suspend fun fetch(userWalletId: UserWalletId): GetWalletAccountsResponse {
val savedAccountsResponse = getAccountsResponseStore(userWalletId = userWalletId).getSyncOrNull()
val accountsResponse = fetchWalletAccounts(userWalletId, savedAccountsResponse)
val fetchResult = fetchWalletAccounts(userWalletId, savedAccountsResponse)
val accountsResponse = fetchResult.accountsResponse
return when {
val updatedResponse = when {
accountsResponse.accounts.isEmpty() -> {
initializeAccounts(userWalletId, accountsResponse)
}
@ -65,6 +66,12 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
}
else -> accountsResponse
}
if (fetchResult.error != null) {
throw fetchResult.error
}
return updatedResponse
}
override suspend fun getSaved(userWalletId: UserWalletId): GetWalletAccountsResponse? {
@ -123,7 +130,7 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
private suspend fun fetchWalletAccounts(
userWalletId: UserWalletId,
savedAccountsResponse: GetWalletAccountsResponse?,
): GetWalletAccountsResponse {
): FetchResult {
return safeApiCall(
call = {
val apiResponse = withContext(dispatchers.io) {
@ -138,7 +145,7 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
val responseBody = apiResponse.bind()
store(userWalletId = userWalletId, response = responseBody)
responseBody
FetchResult(responseBody)
},
onError = { throwable ->
// pushWalletAccounts and storeWalletAccounts help to avoid cyclic dependency
@ -211,4 +218,9 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
private fun getAccountsResponseStore(userWalletId: UserWalletId): AccountsResponseStore {
return accountsResponseStoreFactory.create(userWalletId = userWalletId)
}
data class FetchResult(
val accountsResponse: GetWalletAccountsResponse,
val error: Throwable? = null,
)
}

View file

@ -1,5 +1,6 @@
package com.tangem.data.account.fetcher
import com.tangem.data.account.fetcher.DefaultWalletAccountsFetcher.FetchResult
import com.tangem.data.account.utils.DefaultWalletAccountsResponseFactory
import com.tangem.data.common.cache.etag.ETagsStore
import com.tangem.data.common.currency.UserTokensResponseAccountIdEnricher
@ -69,13 +70,15 @@ internal class FetchWalletAccountsErrorHandler @Inject constructor(
savedAccountsResponse: GetWalletAccountsResponse?,
pushWalletAccounts: suspend (UserWalletId, List<WalletAccountDTO>) -> GetWalletAccountsResponse?,
storeWalletAccounts: suspend (UserWalletId, GetWalletAccountsResponse) -> Unit,
): GetWalletAccountsResponse {
): FetchResult {
val isResponseUpToDate = error.isNetworkError(code = Code.NOT_MODIFIED)
if (isResponseUpToDate) {
Timber.e("ETag is up to date, no need to update accounts for wallet: $userWalletId")
return requireNotNull(savedAccountsResponse) {
val response = requireNotNull(savedAccountsResponse) {
"Saved accounts response is null for wallet: $userWalletId"
}
return FetchResult(response)
}
val response = savedAccountsResponse ?: createDefaultResponse(userWalletId)
@ -95,7 +98,7 @@ internal class FetchWalletAccountsErrorHandler @Inject constructor(
storeWalletAccounts(userWalletId, response)
return response
return FetchResult(response, error)
}
private suspend fun createDefaultResponse(userWalletId: UserWalletId): GetWalletAccountsResponse {

View file

@ -151,9 +151,9 @@ internal class DefaultAccountsCRUDRepository(
)
val syncedResponse = walletAccountsSaver.push(userWalletId = accountList.userWalletId, accounts = accountDTOs)
if (syncedResponse != null) {
walletAccountsSaver.store(userWalletId = accountList.userWalletId, response = syncedResponse)
}
?: error("Failed to push accounts for wallet: ${accountList.userWalletId}")
walletAccountsSaver.store(userWalletId = accountList.userWalletId, response = syncedResponse)
}
override suspend fun saveAccount(account: Account.CryptoPortfolio) {

View file

@ -163,6 +163,7 @@ internal class DefaultManageTokensRepository(
)
}
@Suppress("CyclomaticComplexMethod")
private suspend fun createManagedCryptoCurrencyList(
params: ManageTokensListConfig.Account,
userWallet: UserWallet?,
@ -172,11 +173,15 @@ internal class DefaultManageTokensRepository(
updatedCoinsResponse: CoinsResponse,
): List<ManagedCryptoCurrency> {
val response = params.userWalletId?.let { userWalletId ->
if (loadUserTokensFromRemote && userWallet != null) {
val shouldFetch = loadUserTokensFromRemote && userWallet != null
val fetchedResponse = if (shouldFetch) {
runCatching { walletAccountsFetcher.fetch(userWalletId = userWallet.walletId) }.getOrNull()
} else {
walletAccountsFetcher.getSaved(userWalletId)
null
}
fetchedResponse ?: walletAccountsFetcher.getSaved(userWalletId)
}
val accountId = when {

View file

@ -3,6 +3,7 @@ package com.tangem.domain.account.usecase
import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.raise.*
import com.tangem.domain.account.fetcher.SingleAccountListFetcher
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.repository.AccountsCRUDRepository
import com.tangem.domain.models.account.Account
@ -14,11 +15,13 @@ import com.tangem.domain.models.wallet.UserWalletId
/**
* Use case for updating a crypto portfolio account.
*
* @property singleAccountListFetcher the fetcher used to retrieve a single account list
* @property crudRepository the repository used for performing CRUD operations on accounts
*
[REDACTED_AUTHOR]
*/
class UpdateCryptoPortfolioUseCase(
private val singleAccountListFetcher: SingleAccountListFetcher,
private val crudRepository: AccountsCRUDRepository,
) {
@ -37,6 +40,8 @@ class UpdateCryptoPortfolioUseCase(
): Either<Error, Account.CryptoPortfolio> = either {
validate(accountName, icon)
fetchAccountList(accountId.userWalletId)
val accountList = getAccountList(userWalletId = accountId.userWalletId)
val account = accountList.accounts
@ -64,6 +69,12 @@ class UpdateCryptoPortfolioUseCase(
ensure(accountName != null || icon != null) { Error.NothingToUpdate }
}
private suspend fun Raise<Error>.fetchAccountList(userWalletId: UserWalletId) {
singleAccountListFetcher(params = SingleAccountListFetcher.Params(userWalletId)).onLeft {
raise(Error.DataOperationFailed(cause = it))
}
}
private suspend fun Raise<Error>.getAccountList(userWalletId: UserWalletId): AccountList {
return catch(
block = { crudRepository.getAccountListSync(userWalletId = userWalletId) },
@ -91,7 +102,7 @@ class UpdateCryptoPortfolioUseCase(
private fun Raise<AccountList.Error>.checkDefaultName(accountList: AccountList, accountName: AccountName) {
withError({ AccountList.Error.DuplicateAccountNames }) {
Either.Companion.catch { crudRepository.checkDefaultAccountName(accountList, accountName) }.bind()
Either.catch { crudRepository.checkDefaultAccountName(accountList, accountName) }.bind()
}
}