diff --git a/common/ui-charts/build.gradle.kts b/common/ui-charts/build.gradle.kts
index 0520d97bb3..5abca7eada 100644
--- a/common/ui-charts/build.gradle.kts
+++ b/common/ui-charts/build.gradle.kts
@@ -11,6 +11,7 @@ android {
dependencies {
/** Project - Core */
implementation(projects.core.ui)
+ implementation(projects.core.utils)
/** Compose */
implementation(tangemDeps.vico.core)
diff --git a/common/ui-charts/detekt-baseline-debug.xml b/common/ui-charts/detekt-baseline-debug.xml
deleted file mode 100644
index 05694da92a..0000000000
--- a/common/ui-charts/detekt-baseline-debug.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
- SuspendFunSwallowedCancellation:MarketChartDataProducer.kt$MarketChartDataProducer$runCatching
-
-
diff --git a/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/state/MarketChartDataProducer.kt b/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/state/MarketChartDataProducer.kt
index 74adf8e118..3eaa12fe0d 100644
--- a/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/state/MarketChartDataProducer.kt
+++ b/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/state/MarketChartDataProducer.kt
@@ -6,6 +6,7 @@ import com.patrykandpatrick.vico.core.cartesian.data.LineCartesianLayerModel
import com.tangem.common.ui.charts.state.converter.PointValuesConverter
import com.tangem.common.ui.charts.state.converter.PriceAndTimePointValuesConverter
import com.tangem.common.ui.charts.state.formatter.FormatterWrapWithCache
+import com.tangem.utils.coroutines.runSuspendCatching
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.sync.Mutex
@@ -126,7 +127,7 @@ class MarketChartDataProducer private constructor(
currentCoroutineContext().ensureActive()
- runCatching {
+ runSuspendCatching {
modelProducer.runTransaction {
add(LineCartesianLayerModel.Partial(series = listOf(entriesLocal)))
}
diff --git a/data/balance-hiding/detekt-baseline-debug.xml b/data/balance-hiding/detekt-baseline-debug.xml
deleted file mode 100644
index eca9bec2a0..0000000000
--- a/data/balance-hiding/detekt-baseline-debug.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
- CastNullableToNonNullableType:DefaultDeviceFlipDetector.kt$DefaultDeviceFlipDetector$as
-
-
diff --git a/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/DefaultDeviceFlipDetector.kt b/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/DefaultDeviceFlipDetector.kt
index f69cc30dd9..94dc429337 100644
--- a/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/DefaultDeviceFlipDetector.kt
+++ b/data/balance-hiding/src/main/java/com/tangem/data/balancehiding/DefaultDeviceFlipDetector.kt
@@ -16,11 +16,19 @@ import javax.inject.Singleton
@Singleton
class DefaultDeviceFlipDetector @Inject constructor(
- @ApplicationContext context: Context,
+ @ApplicationContext private val context: Context,
) : DeviceFlipDetector, DefaultLifecycleObserver {
- private val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
- private val gravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
+ private val sensorManager: SensorManager by lazy {
+ val manager = context.getSystemService(Context.SENSOR_SERVICE) as? SensorManager
+
+ requireNotNull(manager)
+ }
+
+ private val gravitySensor by lazy {
+ sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
+ }
+
private val isResumedState = AtomicBoolean(false)
override fun onPause(owner: LifecycleOwner) {
diff --git a/data/hot-wallet/detekt-baseline-debug.xml b/data/hot-wallet/detekt-baseline-debug.xml
deleted file mode 100644
index e0f29a8922..0000000000
--- a/data/hot-wallet/detekt-baseline-debug.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
- MultilineLambdaItParameter:DefaultHotWalletRepository.kt$DefaultHotWalletRepository${ it.setObjectMap( key = PreferencesKeys.ACCESS_CODE_SKIPPED_STATES_KEY, value = it.getObjectMap<Boolean>(PreferencesKeys.ACCESS_CODE_SKIPPED_STATES_KEY) .plus(userWalletId.stringValue to skipped), ) }
-
-
diff --git a/data/hot-wallet/src/main/java/com/tangem/data/hotwallet/DefaultHotWalletRepository.kt b/data/hot-wallet/src/main/java/com/tangem/data/hotwallet/DefaultHotWalletRepository.kt
index d4064d0089..4670884954 100644
--- a/data/hot-wallet/src/main/java/com/tangem/data/hotwallet/DefaultHotWalletRepository.kt
+++ b/data/hot-wallet/src/main/java/com/tangem/data/hotwallet/DefaultHotWalletRepository.kt
@@ -17,10 +17,10 @@ internal class DefaultHotWalletRepository(
.map { it[userWalletId.stringValue] == true }
override suspend fun setAccessCodeSkipped(userWalletId: UserWalletId, skipped: Boolean) {
- appPreferencesStore.editData {
- it.setObjectMap(
+ appPreferencesStore.editData { mutablePreferences ->
+ mutablePreferences.setObjectMap(
key = PreferencesKeys.ACCESS_CODE_SKIPPED_STATES_KEY,
- value = it.getObjectMap(PreferencesKeys.ACCESS_CODE_SKIPPED_STATES_KEY)
+ value = mutablePreferences.getObjectMap(PreferencesKeys.ACCESS_CODE_SKIPPED_STATES_KEY)
.plus(userWalletId.stringValue to skipped),
)
}
diff --git a/data/networks/src/main/java/com/tangem/data/networks/repository/DefaultNetworksRepository.kt b/data/networks/src/main/java/com/tangem/data/networks/repository/DefaultNetworksRepository.kt
index 1209bcfb5b..0ec3c30598 100644
--- a/data/networks/src/main/java/com/tangem/data/networks/repository/DefaultNetworksRepository.kt
+++ b/data/networks/src/main/java/com/tangem/data/networks/repository/DefaultNetworksRepository.kt
@@ -79,6 +79,10 @@ internal class DefaultNetworksRepository(
}
}
+ override suspend fun hasCachedStatuses(userWalletId: UserWalletId): Boolean {
+ return networksStatusesStore.contains(userWalletId)
+ }
+
private suspend fun fetchPendingTransactions(
userWalletId: UserWalletId,
network: Network,
diff --git a/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStore.kt b/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStore.kt
index 9cba360e2e..1131c43dfc 100644
--- a/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStore.kt
+++ b/data/networks/src/main/java/com/tangem/data/networks/store/DefaultNetworksStatusesStore.kt
@@ -124,6 +124,10 @@ internal class DefaultNetworksStatusesStore(
}
}
+ override suspend fun contains(userWalletId: UserWalletId): Boolean {
+ return runtimeStore.getSyncOrDefault(emptyMap()).containsKey(userWalletId.stringValue)
+ }
+
private suspend fun updateInRuntime(
userWalletId: UserWalletId,
networks: Set,
diff --git a/data/networks/src/main/java/com/tangem/data/networks/store/NetworksStatusesStore.kt b/data/networks/src/main/java/com/tangem/data/networks/store/NetworksStatusesStore.kt
index 680ee077ab..2e63a27f7b 100644
--- a/data/networks/src/main/java/com/tangem/data/networks/store/NetworksStatusesStore.kt
+++ b/data/networks/src/main/java/com/tangem/data/networks/store/NetworksStatusesStore.kt
@@ -44,4 +44,7 @@ internal interface NetworksStatusesStore {
/** Clear statuses of [networks] by [userWalletId] */
suspend fun clear(userWalletId: UserWalletId, networks: Set)
+
+ /** Check if there are statuses for given [userWalletId] */
+ suspend fun contains(userWalletId: UserWalletId): Boolean
}
\ No newline at end of file
diff --git a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
index a0dada9a61..c8261d32ea 100644
--- a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
+++ b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
@@ -19,10 +19,7 @@ import com.tangem.domain.promo.models.StoryContent
import com.tangem.feature.referral.domain.ReferralRepository
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.runCatching
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.*
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
@@ -41,23 +38,25 @@ internal class DefaultPromoRepository(
return appPreferencesStore.get(
key = PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name),
default = true,
- ).map { shouldShow ->
- when (promoId) {
- PromoId.Referral -> runCatching {
- !referralRepository.isReferralParticipant(userWalletId) && shouldShow
- }.getOrDefault(false)
- PromoId.Sepa -> {
- val isActive = getSepaPromoBanner()?.isActive ?: false
+ )
+ .distinctUntilChanged()
+ .map { shouldShow ->
+ when (promoId) {
+ PromoId.Referral -> runCatching {
+ !referralRepository.isReferralParticipant(userWalletId) && shouldShow
+ }.getOrDefault(false)
+ PromoId.Sepa -> {
+ val isActive = getSepaPromoBanner()?.isActive ?: false
- isActive && shouldShow
- }
- PromoId.VisaPresale -> {
- val isActive = getVisaPromoBanner()?.isActive ?: false
+ isActive && shouldShow
+ }
+ PromoId.VisaPresale -> {
+ val isActive = getVisaPromoBanner()?.isActive ?: false
- isActive && shouldShow
+ isActive && shouldShow
+ }
}
}
- }
}
override fun isReadyToShowTokenPromo(promoId: PromoId): Flow {
diff --git a/data/quotes/detekt-baseline-debug.xml b/data/quotes/detekt-baseline-debug.xml
deleted file mode 100644
index c69926fdf1..0000000000
--- a/data/quotes/detekt-baseline-debug.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
- MultilineLambdaItParameter:DefaultMultiQuoteStatusFetcher.kt$DefaultMultiQuoteStatusFetcher${ Timber.e(it) quotesStatusesStore.setSourceAsOnlyCache(currenciesIds = params.currenciesIds) }
-
-
diff --git a/data/quotes/src/main/java/com/tangem/data/quotes/multi/DefaultMultiQuoteStatusFetcher.kt b/data/quotes/src/main/java/com/tangem/data/quotes/multi/DefaultMultiQuoteStatusFetcher.kt
index 618f75178d..1660472cb9 100644
--- a/data/quotes/src/main/java/com/tangem/data/quotes/multi/DefaultMultiQuoteStatusFetcher.kt
+++ b/data/quotes/src/main/java/com/tangem/data/quotes/multi/DefaultMultiQuoteStatusFetcher.kt
@@ -66,8 +66,8 @@ internal class DefaultMultiQuoteStatusFetcher @Inject constructor(
quotesStatusesStore.store(values = updatedResponse.quotes)
}
- .onLeft {
- Timber.e(it)
+ .onLeft { throwable ->
+ Timber.e(throwable)
quotesStatusesStore.setSourceAsOnlyCache(currenciesIds = params.currenciesIds)
}
diff --git a/data/settings/detekt-baseline-debug.xml b/data/settings/detekt-baseline-debug.xml
deleted file mode 100644
index 5489dc6b26..0000000000
--- a/data/settings/detekt-baseline-debug.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
- SuspendFunSwallowedCancellation:DefaultSettingsRepository.kt$DefaultSettingsRepository$runCatching
-
-
diff --git a/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt b/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt
index 9c1b8171f0..dea79f4d3a 100644
--- a/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt
+++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultSettingsRepository.kt
@@ -13,6 +13,7 @@ import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.settings.usercountry.models.GB_COUNTRY
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import com.tangem.utils.coroutines.runSuspendCatching
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -153,7 +154,7 @@ internal class DefaultSettingsRepository(
}
withContext(dispatchers.io) {
- val country = runCatching { tangemTechApi.getUserCountryCode() }
+ val country = runSuspendCatching { tangemTechApi.getUserCountryCode() }
.fold(
onSuccess = GeoResponse::code,
onFailure = { Locale.getDefault().country },
diff --git a/detekt_baseline_report.txt b/detekt_baseline_report.txt
index dd34c82236..2017064be7 100644
--- a/detekt_baseline_report.txt
+++ b/detekt_baseline_report.txt
@@ -1,9 +1,13 @@
==========================================
Detekt Baseline Updater & Issue Counter
==========================================
-Date: 2025-11-21 15:22:23
+Date: 2025-11-24 18:35:00
-Updating detekt baseline for debug variant...
+Step 1: Running detekt to check for new issues...
+
+✓ Detekt passed - no new issues found
+
+Step 2: Updating detekt baseline for debug variant...
Baseline updated successfully!
@@ -13,13 +17,13 @@ Counting issues in baseline files...
==========================================
Summary:
- Total Issues: 1720
- Modules with Issues: 90
- Average Issues per Module: 19
+ Total Issues: 1700
+ Modules with Issues: 83
+ Average Issues per Module: 20
Progress:
- Fixed: 82 out of 1802 (4%)
- Remaining: 1720
+ Fixed: 102 out of 1802 (5%)
+ Remaining: 1700
==========================================
All Modules with Issues (sorted by count)
@@ -27,7 +31,7 @@ All Modules with Issues (sorted by count)
Module Issues
────────────────────────────────────────────────────────────────
-features/wallet/impl 170
+features/wallet/impl 169
features/markets/impl 155
features/onboarding-v2/impl 131
features/onramp/impl 89
@@ -43,10 +47,10 @@ features/manage-tokens/impl 45
domain/wallets 39
features/nft/impl 36
features/tester/impl 31
-features/yield-supply/impl 28
domain/tokens 28
features/swap/domain 27
core/ui 27
+features/yield-supply/impl 26
common/ui 26
data/visa 23
features/tangempay/details/impl 22
@@ -54,9 +58,9 @@ data/nft 20
data/wallets 18
features/swap/data 15
data/swap 13
-domain/account/status 12
features/token-recieve/impl 11
features/qr-scanning/impl 11
+domain/account/status 11
data/onramp 11
data/manage-tokens 11
core/datasource 11
@@ -104,16 +108,10 @@ domain/nft 2
data/express 2
core/ab-tests 2
common/test 2
-features/referral/data 1
features/manage-tokens/api 1
features/kyc/impl 1
features/hot-wallet/api 1
features/disclaimer/impl 1
domain/wallet-connect 1
domain/visa 1
-data/settings 1
-data/quotes 1
-data/hot-wallet 1
-data/balance-hiding 1
-common/ui-charts 1
────────────────────────────────────────────────────────────────
\ No newline at end of file
diff --git a/domain/account/status/detekt-baseline-debug.xml b/domain/account/status/detekt-baseline-debug.xml
index 5b524e1585..1f66781ca0 100644
--- a/domain/account/status/detekt-baseline-debug.xml
+++ b/domain/account/status/detekt-baseline-debug.xml
@@ -2,7 +2,6 @@
- ExplicitCollectionElementAccessMethod:ManageCryptoCurrenciesUseCase.kt$ManageCryptoCurrenciesUseCase$mutableMap.put(id, currency)
MultilineLambdaItParameter:AccountCryptoCurrencyStatusFinder.kt$AccountCryptoCurrencyStatusFinder${ val cryptoPortfolio = it.account as? Account.CryptoPortfolio ?: return@filter false cryptoPortfolio.derivationIndex.value in possibleAccountIndexes }
MultilineLambdaItParameter:AccountCryptoCurrencyStatusFinder.kt$AccountCryptoCurrencyStatusFinder${ val cryptoPortfolio = it.account as? Account.CryptoPortfolio ?: return@firstOrNull false cryptoPortfolio.derivationIndex.value == possibleAccountIndex }
MultilineLambdaItParameter:AccountCryptoCurrencyStatusFinder.kt$AccountCryptoCurrencyStatusFinder${ val currency = it.currency val isContractAddressMatch = contractAddress == null || currency.id.contractAddress.equals(contractAddress, ignoreCase = true) currency.network.rawId == networkId.rawId.value && currency.network.derivationPath.value == derivationPath.value && isContractAddressMatch }
diff --git a/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducer.kt b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducer.kt
index 8d8683c595..8b3efcfba9 100644
--- a/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducer.kt
+++ b/domain/account/status/src/main/java/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducer.kt
@@ -2,20 +2,24 @@ package com.tangem.domain.account.status.producer
import arrow.core.Option
import arrow.core.none
+import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.models.AccountStatusList
-import com.tangem.domain.account.producer.SingleAccountListProducer
import com.tangem.domain.account.repository.AccountsCRUDRepository
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusesFlowFactory
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.core.utils.lceContent
+import com.tangem.domain.core.utils.lceLoading
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.TokensGroupType
import com.tangem.domain.models.TokensSortType
+import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountStatus
+import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.quote.PriceChange
import com.tangem.domain.models.tokenlist.TokenList
import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.networks.repository.NetworksRepository
import com.tangem.domain.tokens.operations.PriceChangeCalculator
import com.tangem.domain.tokens.operations.TokenListFactory
import com.tangem.domain.tokens.operations.TotalFiatBalanceCalculator
@@ -24,8 +28,10 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.*
import java.math.BigDecimal
+import kotlin.time.Duration.Companion.milliseconds
/**
* Produces a flow of [AccountStatusList] for a single user wallet.
@@ -43,6 +49,7 @@ internal class DefaultSingleAccountStatusListProducer @AssistedInject constructo
@Assisted private val params: SingleAccountStatusListProducer.Params,
private val accountsCRUDRepository: AccountsCRUDRepository,
private val singleAccountListSupplier: SingleAccountListSupplier,
+ private val networksRepository: NetworksRepository,
private val cryptoCurrencyStatusesFlowFactory: CryptoCurrencyStatusesFlowFactory,
private val dispatchers: CoroutineDispatcherProvider,
) : SingleAccountStatusListProducer {
@@ -50,31 +57,11 @@ internal class DefaultSingleAccountStatusListProducer @AssistedInject constructo
override val fallback: Option = none()
override fun produce(): Flow {
- val accountListFlow = singleAccountListSupplier(
- params = SingleAccountListProducer.Params(params.userWalletId),
- )
-
- return accountListFlow.flatMapLatest { accountList ->
- val accountStatusFlows = accountList.accounts.mapNotNull { account ->
- if (account !is Account.CryptoPortfolio) return@mapNotNull null
-
- if (account.cryptoCurrencies.isEmpty()) {
- createEmptyAccountStatusFlow(account)
- } else {
- val userWallet = accountsCRUDRepository.getUserWallet(userWalletId = params.userWalletId)
-
- getAccountStatusFlow(
- userWallet = userWallet,
- account = account,
- groupType = accountList.groupType,
- sortType = accountList.sortType,
- )
- }
- .distinctUntilChanged()
- }
+ return singleAccountListSupplier(userWalletId = params.userWalletId).flatMapLatest { accountList ->
+ val accountStatusFlows = createAccountStatusFlows(accountList)
combine(accountStatusFlows) { accountStatuses ->
- val balances = accountStatuses.map { it.tokenList.totalFiatBalance }
+ val balances = accountStatuses.flattenTotalFiatBalance()
AccountStatusList(
userWalletId = accountList.userWalletId,
@@ -85,11 +72,34 @@ internal class DefaultSingleAccountStatusListProducer @AssistedInject constructo
groupType = accountList.groupType,
)
}
+ .onStartCheckCachedNetworks(accountList)
}
.distinctUntilChanged()
.flowOn(dispatchers.default)
}
+ private fun createAccountStatusFlows(accountList: AccountList): List> {
+ return accountList.accounts.map { account ->
+ when (account) {
+ is Account.CryptoPortfolio -> {
+ if (account.cryptoCurrencies.isEmpty()) {
+ createEmptyAccountStatusFlow(account)
+ } else {
+ val userWallet = accountsCRUDRepository.getUserWallet(userWalletId = params.userWalletId)
+
+ getAccountStatusFlow(
+ userWallet = userWallet,
+ account = account,
+ groupType = accountList.groupType,
+ sortType = accountList.sortType,
+ )
+ }
+ .distinctUntilChanged()
+ }
+ }
+ }
+ }
+
private fun createEmptyAccountStatusFlow(account: Account.CryptoPortfolio): Flow {
return flowOf(
AccountStatus.CryptoPortfolio(
@@ -109,25 +119,85 @@ internal class DefaultSingleAccountStatusListProducer @AssistedInject constructo
groupType: TokensGroupType,
sortType: TokensSortType,
): Flow {
+ val statusesFlows = getCryptoCurrencyStatusesFlow(userWallet, account)
+
+ return statusesFlows
+ .map { statusList ->
+ AccountStatus.CryptoPortfolio(
+ account = account,
+ tokenList = TokenListFactory.create(
+ statuses = statusList,
+ groupType = groupType,
+ sortType = sortType,
+ ),
+ priceChangeLce = PriceChangeCalculator.calculate(statuses = statusList),
+ )
+ }
+ .distinctUntilChanged()
+ }
+
+ @OptIn(FlowPreview::class)
+ private fun getCryptoCurrencyStatusesFlow(
+ userWallet: UserWallet,
+ account: Account.CryptoPortfolio,
+ ): Flow> {
val statusesFlows = account.cryptoCurrencies.map { currency ->
cryptoCurrencyStatusesFlowFactory.create(userWallet = userWallet, currency = currency)
+ .onStart { emit(CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)) }
.distinctUntilChanged()
}
- return combine(statusesFlows) { statuses ->
- val statusList = statuses.toList()
-
- AccountStatus.CryptoPortfolio(
- account = account,
- tokenList = TokenListFactory.create(
- statuses = statusList,
- groupType = groupType,
- sortType = sortType,
- ),
- priceChangeLce = PriceChangeCalculator.calculate(statuses = statusList),
- )
- }
+ return combine(statusesFlows) { it.toList() }
.distinctUntilChanged()
+ .debounce(50.milliseconds)
+ }
+
+ private fun Array.flattenTotalFiatBalance(): List {
+ return map { accountStatus ->
+ when (accountStatus) {
+ is AccountStatus.CryptoPortfolio -> accountStatus.tokenList.totalFiatBalance
+ }
+ }
+ }
+
+ private fun Flow.onStartCheckCachedNetworks(accountList: AccountList): Flow {
+ return onStart {
+ val hasCachedNetworks = networksRepository.hasCachedStatuses(userWalletId = accountList.userWalletId)
+
+ if (hasCachedNetworks) return@onStart
+
+ val loading = createLoadingAccountStatusList(accountList)
+ emit(loading)
+ }
+ }
+
+ private fun createLoadingAccountStatusList(accountList: AccountList): AccountStatusList {
+ return AccountStatusList(
+ userWalletId = accountList.userWalletId,
+ accountStatuses = accountList.accounts.map { account ->
+ when (account) {
+ is Account.CryptoPortfolio -> {
+ val currencyStatuses = account.cryptoCurrencies.map {
+ CryptoCurrencyStatus(currency = it, value = CryptoCurrencyStatus.Loading)
+ }
+
+ AccountStatus.CryptoPortfolio(
+ account = account,
+ tokenList = TokenListFactory.create(
+ statuses = currencyStatuses,
+ groupType = accountList.groupType,
+ sortType = accountList.sortType,
+ ),
+ priceChangeLce = lceLoading(),
+ )
+ }
+ }
+ },
+ totalAccounts = accountList.totalAccounts,
+ totalFiatBalance = TotalFiatBalance.Loading,
+ sortType = accountList.sortType,
+ groupType = accountList.groupType,
+ )
}
@AssistedFactory
diff --git a/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducerTest.kt b/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducerTest.kt
index a02e8b9108..15c437d7aa 100644
--- a/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducerTest.kt
+++ b/domain/account/status/src/test/kotlin/com/tangem/domain/account/status/producer/DefaultSingleAccountStatusListProducerTest.kt
@@ -5,7 +5,6 @@ import com.google.common.truth.Truth
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.models.AccountStatusList
-import com.tangem.domain.account.producer.SingleAccountListProducer
import com.tangem.domain.account.repository.AccountsCRUDRepository
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusesFlowFactory
import com.tangem.domain.account.supplier.SingleAccountListSupplier
@@ -20,9 +19,11 @@ import com.tangem.domain.models.quote.PriceChange
import com.tangem.domain.models.tokenlist.TokenList
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
+import com.tangem.domain.networks.repository.NetworksRepository
import com.tangem.test.core.getEmittedValues
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.*
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
@@ -40,6 +41,7 @@ class DefaultSingleAccountStatusListProducerTest {
private val accountsCRUDRepository: AccountsCRUDRepository = mockk()
private val singleAccountListSupplier: SingleAccountListSupplier = mockk()
+ private val networksRepository: NetworksRepository = mockk()
private val cryptoCurrencyStatusesFlowFactory: CryptoCurrencyStatusesFlowFactory = mockk()
private val userWalletId = UserWalletId("011")
@@ -51,13 +53,19 @@ class DefaultSingleAccountStatusListProducerTest {
params = SingleAccountStatusListProducer.Params(userWalletId),
accountsCRUDRepository = accountsCRUDRepository,
singleAccountListSupplier = singleAccountListSupplier,
+ networksRepository = networksRepository,
cryptoCurrencyStatusesFlowFactory = cryptoCurrencyStatusesFlowFactory,
dispatchers = TestingCoroutineDispatcherProvider(),
)
@AfterEach
fun tearDown() {
- clearMocks(accountsCRUDRepository, singleAccountListSupplier, cryptoCurrencyStatusesFlowFactory)
+ clearMocks(
+ accountsCRUDRepository,
+ singleAccountListSupplier,
+ networksRepository,
+ cryptoCurrencyStatusesFlowFactory,
+ )
}
@Test
@@ -65,9 +73,8 @@ class DefaultSingleAccountStatusListProducerTest {
// Arrange
val accountList = AccountList.empty(userWalletId = userWalletId)
- every {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- } returns flowOf(accountList)
+ every { singleAccountListSupplier(userWalletId) } returns flowOf(accountList)
+ coEvery { networksRepository.hasCachedStatuses(userWalletId) } returns true
// Act
val actual = producer.produce().let(::getEmittedValues)
@@ -89,8 +96,14 @@ class DefaultSingleAccountStatusListProducerTest {
)
Truth.assertThat(actual).containsExactly(expected)
- coVerify(ordering = Ordering.SEQUENCE) {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
+ coVerifySequence {
+ singleAccountListSupplier(userWalletId)
+ networksRepository.hasCachedStatuses(userWalletId)
+ }
+
+ coVerify(inverse = true) {
+ accountsCRUDRepository.getUserWallet(userWalletId = any())
+ cryptoCurrencyStatusesFlowFactory.create(userWallet = any(), currency = any())
}
}
@@ -102,9 +115,8 @@ class DefaultSingleAccountStatusListProducerTest {
val accountListFlow = MutableStateFlow(value = accountList)
- every {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- } returns accountListFlow
+ every { singleAccountListSupplier(userWalletId) } returns accountListFlow
+ coEvery { networksRepository.hasCachedStatuses(userWalletId) } returns true
// Act (first emission)
val actual1 = producer.produce().let(::getEmittedValues)
@@ -147,9 +159,17 @@ class DefaultSingleAccountStatusListProducerTest {
)
Truth.assertThat(actual2).containsExactly(expected2)
- coVerify(ordering = Ordering.SEQUENCE) {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
+ coVerifySequence {
+ singleAccountListSupplier(userWalletId)
+ networksRepository.hasCachedStatuses(userWalletId)
+ networksRepository.hasCachedStatuses(userWalletId)
+ singleAccountListSupplier(userWalletId)
+ networksRepository.hasCachedStatuses(userWalletId)
+ }
+
+ coVerify(inverse = true) {
+ accountsCRUDRepository.getUserWallet(userWalletId = any())
+ cryptoCurrencyStatusesFlowFactory.create(userWallet = any(), currency = any())
}
}
@@ -159,9 +179,8 @@ class DefaultSingleAccountStatusListProducerTest {
val accountList = AccountList.empty(userWalletId)
val accountListFlow = MutableStateFlow(value = accountList)
- every {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- } returns accountListFlow
+ every { singleAccountListSupplier(userWalletId) } returns accountListFlow
+ coEvery { networksRepository.hasCachedStatuses(userWalletId) } returns true
val expected = AccountStatusList(
userWalletId = userWalletId,
@@ -191,9 +210,16 @@ class DefaultSingleAccountStatusListProducerTest {
// Assert (second emission)
Truth.assertThat(actual2).containsExactly(expected)
- coVerify(ordering = Ordering.SEQUENCE) {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
+ coVerifySequence {
+ singleAccountListSupplier(userWalletId)
+ networksRepository.hasCachedStatuses(userWalletId)
+ singleAccountListSupplier(userWalletId)
+ networksRepository.hasCachedStatuses(userWalletId)
+ }
+
+ coVerify(inverse = true) {
+ accountsCRUDRepository.getUserWallet(userWalletId = any())
+ cryptoCurrencyStatusesFlowFactory.create(userWallet = any(), currency = any())
}
}
@@ -208,9 +234,8 @@ class DefaultSingleAccountStatusListProducerTest {
coEvery { accountsCRUDRepository.getUserWallet(userWalletId = userWalletId) } returns userWallet
- every {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
- } returns flowOf(accountList)
+ every { singleAccountListSupplier(userWalletId) } returns flowOf(accountList)
+ coEvery { networksRepository.hasCachedStatuses(userWalletId) } returns true
val ethereumStatus = CryptoCurrencyStatus(
currency = cryptoCurrencyFactory.ethereum,
@@ -229,7 +254,9 @@ class DefaultSingleAccountStatusListProducerTest {
} returns flowOf(stellarStatus)
// Act
- val actual = producer.produce().let(::getEmittedValues)
+ val flow = producer.produce()
+ delay(1000)
+ val actual = flow.let(::getEmittedValues)
// Assert
val expected = AccountStatusList(
@@ -252,8 +279,12 @@ class DefaultSingleAccountStatusListProducerTest {
)
Truth.assertThat(actual).containsExactly(expected)
- coVerify(ordering = Ordering.SEQUENCE) {
- singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
+ coVerifySequence {
+ singleAccountListSupplier(userWalletId)
+ accountsCRUDRepository.getUserWallet(userWalletId = userWalletId)
+ cryptoCurrencyStatusesFlowFactory.create(userWallet = userWallet, currency = cryptoCurrencyFactory.ethereum)
+ cryptoCurrencyStatusesFlowFactory.create(userWallet = userWallet, currency = cryptoCurrencyFactory.stellar)
+ networksRepository.hasCachedStatuses(userWalletId)
}
}
}
\ No newline at end of file
diff --git a/domain/networks/src/main/java/com/tangem/domain/networks/repository/NetworksRepository.kt b/domain/networks/src/main/java/com/tangem/domain/networks/repository/NetworksRepository.kt
index be5ea651d0..e27c6b2b47 100644
--- a/domain/networks/src/main/java/com/tangem/domain/networks/repository/NetworksRepository.kt
+++ b/domain/networks/src/main/java/com/tangem/domain/networks/repository/NetworksRepository.kt
@@ -22,5 +22,14 @@ interface NetworksRepository {
*/
suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network): List
+ /**
+ * Returns addresses and crypto currency
+ *
+ * @param userWalletId the unique identifier of the user wallet
+ * @param network network id
+ */
suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network.RawID): List
+
+ /** Checks if there are cached statuses for given [userWalletId] */
+ suspend fun hasCachedStatuses(userWalletId: UserWalletId): Boolean
}
\ No newline at end of file
diff --git a/features/wallet/impl/detekt-baseline-debug.xml b/features/wallet/impl/detekt-baseline-debug.xml
index db463f017b..cf87ca958c 100644
--- a/features/wallet/impl/detekt-baseline-debug.xml
+++ b/features/wallet/impl/detekt-baseline-debug.xml
@@ -72,7 +72,6 @@
MultilineLambdaItParameter:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor${ analyticsEventHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(currency.symbol)) clipboardManager.setText(text = it, isSensitive = true) }
MultilineLambdaItParameter:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor${ analyticsEventHandler.send(TokenReceiveAnalyticsEvent.ButtonShareAddress(currency.symbol)) shareManager.shareText(text = it) }
MultilineLambdaItParameter:WalletCurrencyActionsClickIntents.kt$WalletCurrencyActionsClickIntentsImplementor${ onAddressTypeSelected( userWalletId = userWalletId, currency = currency, addressModel = it, ) }
- MultilineLambdaItParameter:WalletDropDownItemsSubscriber.kt$WalletDropDownItemsSubscriber${ stateHolder.update( SetWalletCardDropDownItemsTransformer( dropdownEnabled = it, clickIntents = clickIntents, ), ) }
MultilineLambdaItParameter:WalletLoaderStorage.kt$WalletLoaderStorage${ it.forEach(Job::cancel) loaders.remove(id) }
MultilineLambdaItParameter:WalletModel.kt$WalletModel${ it .conflate() .distinctUntilChanged() .onEach { selectedWallet -> if (selectedWallet.isMultiCurrency) { selectedWalletAnalyticsSender.send(selectedWallet) } subscribeOnExpressTransactionsUpdates(selectedWallet) observeAndClearNFTCacheIfNeedUseCase(selectedWallet) } .flowOn(dispatchers.main) .launchIn(modelScope) }
MultilineLambdaItParameter:WalletModel.kt$WalletModel${ walletScreenContentLoader.load( userWallet = it, clickIntents = clickIntents, coroutineScope = modelScope, isRefresh = true, ) }
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt
index 4ef699e9c4..5d05c74f9f 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt
@@ -45,6 +45,7 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import timber.log.Timber
import javax.inject.Inject
@@ -95,13 +96,16 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
return combine(
// todo account just use it, after delete accountsFeatureToggles
// accountStatusListFlow,
- isReadyToShowRateAppUseCase(),
- isNeedToBackupUseCase(userWallet.walletId),
- seedPhraseNotificationUseCase(userWalletId = userWallet.walletId),
- shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.VisaPresale),
- shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Sepa),
- notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key),
- getAccessCodeSkippedUseCase(userWallet.walletId),
+ isReadyToShowRateAppUseCase().distinctUntilChanged(),
+ isNeedToBackupUseCase(userWallet.walletId).distinctUntilChanged(),
+ seedPhraseNotificationUseCase(userWalletId = userWallet.walletId).distinctUntilChanged(),
+ shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.VisaPresale)
+ .distinctUntilChanged(),
+ shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Sepa)
+ .distinctUntilChanged(),
+ notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key)
+ .distinctUntilChanged(),
+ getAccessCodeSkippedUseCase(userWallet.walletId).distinctUntilChanged(),
) { array -> array }
.combine(tokenListFlow()) { array, any: Any -> arrayOf(any).plus(elements = array) }
.map { array ->
diff --git a/features/yield-supply/impl/detekt-baseline-debug.xml b/features/yield-supply/impl/detekt-baseline-debug.xml
index 0fef2c630f..1063bc0890 100644
--- a/features/yield-supply/impl/detekt-baseline-debug.xml
+++ b/features/yield-supply/impl/detekt-baseline-debug.xml
@@ -5,12 +5,10 @@
BooleanPropertyNaming:YieldSupplyApyComponent.kt$YieldSupplyApyComponent$val state by loadingState.collectAsState()
BooleanPropertyNaming:YieldSupplyModel.kt$YieldSupplyModel$private val handleNavigation = params.handleNavigation
BooleanPropertyNaming:YieldSupplyModel.kt$YieldSupplyModel$val processing = uiState.value is YieldSupplyUM.Processing
- BooleanPropertyNaming:YieldSupplyModel.kt$YieldSupplyModel$val showInfoIcon = cryptoCurrencyStatus.yieldSupplyNotAllAmountSupplied()
BooleanPropertyNaming:YieldSupplyModel.kt$YieldSupplyModel$val showWarningIcon = !yieldSupplyStatus.isAllowedToSpend
BooleanPropertyNaming:YieldSupplyUM.kt$YieldSupplyUM.Content$val showInfoIcon: Boolean
BooleanPropertyNaming:YieldSupplyUM.kt$YieldSupplyUM.Content$val showWarningIcon: Boolean
MultilineLambdaItParameter:YieldSupplyApproveModel.kt$YieldSupplyApproveModel${ Timber.e(it) return }
- MultilineLambdaItParameter:YieldSupplyModel.kt$YieldSupplyModel${ Timber.e(it) uiState.update { YieldSupplyUM.Content( title = resourceReference( R.string.yield_module_token_details_earn_notification_earning_on_your_balance_title, ), subtitle = resourceReference( R.string.yield_module_token_details_earn_notification_earning_on_your_balance_subtitle, ), rewardsApy = TextReference.EMPTY, onClick = ::onActiveClick, showWarningIcon = showWarningIcon, showInfoIcon = showInfoIcon, apy = "", ) } }
MultilineLambdaItParameter:YieldSupplyModel.kt$YieldSupplyModel${ Timber.e(it) uiState.update { YieldSupplyUM.Initial } }
MultilineLambdaItParameter:YieldSupplyModel.kt$YieldSupplyModel${ Timber.w(it.toString()) return@launch }
MultilineLambdaItParameter:YieldSupplyStartEarningModel.kt$YieldSupplyStartEarningModel${ Timber.w(it.toString()) showAlertError() }