Updated on 2026-08-14
This commit is contained in:
parent
097e9c1bce
commit
2437fa39d8
9 changed files with 120 additions and 22 deletions
|
|
@ -11,9 +11,11 @@ import com.tangem.domain.common.wallets.getSyncStrict
|
|||
import com.tangem.domain.core.flow.FlowProducerTools
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import com.tangem.hot.sdk.model.HotWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -21,6 +23,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
/**
|
||||
* Default implementation of [SingleAccountListProducer].
|
||||
|
|
@ -43,23 +46,45 @@ internal class DefaultSingleAccountListProducer @AssistedInject constructor(
|
|||
|
||||
override val fallback: Option<AccountList> = none()
|
||||
|
||||
private val logger = TangemLogger.withTag(TAG)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun produce(): Flow<AccountList> {
|
||||
val walletId = params.userWalletId
|
||||
logger.i("produce() called for $walletId")
|
||||
val accountListFlow: Flow<AccountList> = if (tangemPayFeatureToggles.isTangemPayAccountsRefactorEnabled) {
|
||||
combineWithPaymentAccount()
|
||||
combineWithPaymentAccount(walletId)
|
||||
} else {
|
||||
walletAccountListFlowFactory.create(userWalletId = params.userWalletId)
|
||||
walletAccountListFlowFactory.create(userWalletId = walletId)
|
||||
}
|
||||
|
||||
return accountListFlow.flowOn(dispatchers.default)
|
||||
return accountListFlow
|
||||
.onEach { accountList ->
|
||||
logger.i(
|
||||
"produce()[$walletId] emit: accounts=${accountList.accounts.size}, " +
|
||||
"currencies=${accountList.flattenMapCurrencies().size}",
|
||||
)
|
||||
}
|
||||
.flowOn(dispatchers.default)
|
||||
}
|
||||
|
||||
private fun combineWithPaymentAccount(): Flow<AccountList> {
|
||||
return walletAccountListFlowFactory.create(params.userWalletId)
|
||||
private fun combineWithPaymentAccount(walletId: UserWalletId): Flow<AccountList> {
|
||||
return walletAccountListFlowFactory.create(walletId)
|
||||
.onEach { accountList ->
|
||||
logger.i(
|
||||
"produce()[$walletId]: accountList received accounts=${accountList.accounts.size}, " +
|
||||
"currencies=${accountList.flattenMapCurrencies().size}",
|
||||
)
|
||||
}
|
||||
.map { accountList ->
|
||||
val userWallet = userWalletsListRepository.getSyncStrict(id = params.userWalletId)
|
||||
if (userWallet.isPaymentAccountSupported()) {
|
||||
accountList.plus(Account.Payment(params.userWalletId)).getOrElse { throwable ->
|
||||
val userWallet = userWalletsListRepository.getSyncStrict(id = walletId)
|
||||
val isPaymentSupported = userWallet.isPaymentAccountSupported()
|
||||
logger.i(
|
||||
"produce()[$walletId]: userWallet resolved (type=${userWallet::class.simpleName}), " +
|
||||
"isPaymentAccountSupported=$isPaymentSupported",
|
||||
)
|
||||
if (isPaymentSupported) {
|
||||
accountList.plus(Account.Payment(walletId)).getOrElse { throwable ->
|
||||
error("Can not combine account list and payment account status: $throwable")
|
||||
}
|
||||
} else {
|
||||
|
|
@ -77,4 +102,8 @@ internal class DefaultSingleAccountListProducer @AssistedInject constructor(
|
|||
interface Factory : SingleAccountListProducer.Factory {
|
||||
override fun create(params: SingleAccountListProducer.Params): DefaultSingleAccountListProducer
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val TAG = "SingleAccountListProducer"
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.models.wallet.requireColdWallet
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.flow.*
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -47,7 +48,15 @@ internal class WalletAccountListFlowFactory @Inject constructor(
|
|||
|
||||
return accountsResponseStoreFactory.create(userWallet.walletId).data
|
||||
.filterNotNull()
|
||||
.filter { it.accounts.isNotEmpty() }
|
||||
.filter { response ->
|
||||
val hasAccounts = response.accounts.isNotEmpty()
|
||||
|
||||
if (!hasAccounts) {
|
||||
TangemLogger.e("Account list is empty for ${userWallet.walletId}")
|
||||
}
|
||||
|
||||
hasAccounts
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.map { converter.convert(it) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,10 +135,9 @@ internal class DefaultCardCryptoCurrencyFactory(
|
|||
userWallet: UserWallet,
|
||||
networks: Set<Network>,
|
||||
): Map<Network, List<CryptoCurrency>> {
|
||||
val response = walletAccountsFetcher.getSaved(userWallet.walletId)
|
||||
?: return emptyMap()
|
||||
val response = walletAccountsFetcher.getSaved(userWallet.walletId) ?: return emptyMap()
|
||||
|
||||
val existingNetworkWithCurrencies = response.accounts.flatMapTo(hashSetOf()) { accountDTO ->
|
||||
val currenciesByNetworkId = response.accounts.flatMapTo(hashSetOf()) { accountDTO ->
|
||||
val accountIndex = DerivationIndex(accountDTO.derivationIndex).getOrNull()
|
||||
?: return@flatMapTo emptySet()
|
||||
|
||||
|
|
@ -152,9 +151,11 @@ internal class DefaultCardCryptoCurrencyFactory(
|
|||
accountIndex = accountIndex,
|
||||
)
|
||||
}
|
||||
.groupBy(CryptoCurrency::network)
|
||||
.groupBy { it.network.id }
|
||||
|
||||
return networks.associateWith { emptyList<CryptoCurrency>() } + existingNetworkWithCurrencies
|
||||
return networks.associateWith { network ->
|
||||
currenciesByNetworkId[network.id].orEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getMultiWalletCurrenciesByRawId(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.data.networks.fetcher
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.right
|
||||
import com.tangem.data.networks.store.NetworksStatusesStore
|
||||
import com.tangem.data.networks.store.setSourceAsOnlyCache
|
||||
import com.tangem.data.networks.store.storeStatus
|
||||
|
|
@ -44,6 +45,13 @@ internal class CommonNetworkStatusFetcher @Inject constructor(
|
|||
network: Network,
|
||||
networkCurrencies: Set<CryptoCurrency>,
|
||||
): Either<Throwable, Unit> {
|
||||
// Guard: empty networkCurrencies would result in NetworkStatus.Verified(amounts=emptyMap()),
|
||||
// which overwrites any valid cached status and leaves all currencies in this network as Loading.
|
||||
if (networkCurrencies.isEmpty()) {
|
||||
TangemLogger.w("Skipping fetch for $userWalletId [${network.rawId}]: networkCurrencies is empty")
|
||||
return Unit.right()
|
||||
}
|
||||
|
||||
return Either.catchOn(dispatchers.default) {
|
||||
val result = withContext(dispatchers.io) {
|
||||
walletManagersFacade.update(
|
||||
|
|
|
|||
|
|
@ -73,6 +73,32 @@ internal class CommonNetworkStatusFetcherTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `fetch skips wallet manager update when networkCurrencies is empty`() = runTest {
|
||||
// Arrange
|
||||
val userWalletId = UserWalletId("011")
|
||||
val network = cryptoCurrencyFactory.ethereum.network
|
||||
|
||||
// Act
|
||||
val actual = fetcher.fetch(
|
||||
userWalletId = userWalletId,
|
||||
network = network,
|
||||
networkCurrencies = emptySet(),
|
||||
)
|
||||
|
||||
// Assert: returns success without touching the wallet manager facade — guard prevents storing
|
||||
// a NetworkStatus.Verified with empty `amounts` over an existing valid cached status.
|
||||
Truth.assertThat(actual).isEqualTo(Either.Right(Unit))
|
||||
|
||||
coVerify(inverse = true) {
|
||||
walletManagersFacade.update(
|
||||
userWalletId = any(),
|
||||
network = any(),
|
||||
extraTokens = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ProvideTestModels
|
||||
fun `fetch successfully for any result of walletManagersFacade`(model: SuccessTestModel) = runTest {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue