Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-03 10:59:01 +07:00
parent 8aba7b07d1
commit 57fc519d5f
3 changed files with 63 additions and 33 deletions

View file

@ -2,6 +2,7 @@ package com.tangem.domain.account.models
import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.account.AccountStatus import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -29,4 +30,8 @@ data class AccountStatusList(
is AccountStatus.CryptoPortfolio -> accountStatus.account.isMainAccount is AccountStatus.CryptoPortfolio -> accountStatus.account.isMainAccount
} }
} }
fun flattenCurrencies(): List<CryptoCurrencyStatus> = accountStatuses
.map { accountStatus -> accountStatus.flattenCurrencies() }
.flatten()
} }

View file

@ -1,6 +1,7 @@
package com.tangem.domain.models.account package com.tangem.domain.models.account
import com.tangem.domain.core.lce.Lce import com.tangem.domain.core.lce.Lce
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.quote.PriceChange import com.tangem.domain.models.quote.PriceChange
import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.tokenlist.TokenList
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -29,4 +30,8 @@ sealed interface AccountStatus {
val tokenList: TokenList, val tokenList: TokenList,
val priceChangeLce: Lce<Unit, PriceChange>, val priceChangeLce: Lce<Unit, PriceChange>,
) : AccountStatus ) : AccountStatus
fun flattenCurrencies(): List<CryptoCurrencyStatus> = when (this) {
is CryptoPortfolio -> tokenList.flattenCurrencies()
}
} }

View file

@ -9,6 +9,8 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState import com.tangem.core.ui.components.notifications.NotificationConfig.ButtonsState
import com.tangem.core.ui.components.notifications.NotificationConfig.IconTint import com.tangem.core.ui.components.notifications.NotificationConfig.IconTint
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
import com.tangem.domain.card.CardTypesResolver import com.tangem.domain.card.CardTypesResolver
import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.core.lce.Lce import com.tangem.domain.core.lce.Lce
@ -33,6 +35,7 @@ import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
import com.tangem.lib.crypto.BlockchainUtils.isBitcoin import com.tangem.lib.crypto.BlockchainUtils.isBitcoin
@ -60,14 +63,21 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private val getOnrampCountryUseCase: GetOnrampCountryUseCase, private val getOnrampCountryUseCase: GetOnrampCountryUseCase,
private val notificationsRepository: NotificationsRepository, private val notificationsRepository: NotificationsRepository,
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles, private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
private val accountDependencies: AccountDependencies,
) { ) {
@Suppress("UNCHECKED_CAST", "MagicNumber") @Suppress("UNCHECKED_CAST", "MagicNumber")
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotification>> { fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): Flow<ImmutableList<WalletNotification>> {
val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver val cardTypesResolver = (userWallet as? UserWallet.Cold)?.scanResponse?.cardTypesResolver
val tokenListFlow = if (accountDependencies.accountsFeatureToggles.isFeatureEnabled) {
val params = SingleAccountStatusListProducer.Params(userWallet.walletId)
accountDependencies.singleAccountStatusListSupplier(params)
} else {
tokenListStore.getOrThrow(userWallet.walletId)
}
return combine( return combine(
tokenListStore.getOrThrow(userWallet.walletId), tokenListFlow,
isReadyToShowRateAppUseCase(), isReadyToShowRateAppUseCase(),
isNeedToBackupUseCase(userWallet.walletId), isNeedToBackupUseCase(userWallet.walletId),
seedPhraseNotificationUseCase(userWalletId = userWallet.walletId), seedPhraseNotificationUseCase(userWalletId = userWallet.walletId),
@ -75,7 +85,18 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Sepa), shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.Sepa),
notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key), notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key),
) { array -> ) { array ->
val maybeTokenList = array[0] as Lce<TokenListError, TokenList> val totalFiatBalance: Lce<TokenListError, TotalFiatBalance>
val flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>
if (accountDependencies.accountsFeatureToggles.isFeatureEnabled) {
val accountStatusList = array[0] as AccountStatusList
totalFiatBalance = Lce.Content(accountStatusList.totalFiatBalance)
flattenCurrencies = Lce.Content(accountStatusList.flattenCurrencies())
} else {
val maybeTokenList = array[0] as Lce<TokenListError, TokenList>
totalFiatBalance = maybeTokenList.map { it.totalFiatBalance }
flattenCurrencies = maybeTokenList.map { it.flattenCurrencies() }
}
val isReadyToShowRating = array[1] as Boolean val isReadyToShowRating = array[1] as Boolean
val isNeedToBackup = array[2] as Boolean val isNeedToBackup = array[2] as Boolean
val seedPhraseIssueStatus = array[3] as SeedPhraseNotificationsStatus val seedPhraseIssueStatus = array[3] as SeedPhraseNotificationsStatus
@ -84,19 +105,19 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val shouldShowEnablePushesReminderNotification = array[6] as Boolean val shouldShowEnablePushesReminderNotification = array[6] as Boolean
buildList { buildList {
addUsedOutdatedDataNotification(maybeTokenList) addUsedOutdatedDataNotification(totalFiatBalance)
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents) addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
addFinishWalletActivationNotification(userWallet, maybeTokenList, clickIntents) addFinishWalletActivationNotification(userWallet, totalFiatBalance, clickIntents)
addReferralPromoNotification(cardTypesResolver, clickIntents, shouldShowReferralPromo) addReferralPromoNotification(cardTypesResolver, clickIntents, shouldShowReferralPromo)
addSepaPromoNotification(userWallet, clickIntents, shouldShowSepaBanner) addSepaPromoNotification(userWallet, clickIntents, shouldShowSepaBanner)
addInformationalNotifications(userWallet, cardTypesResolver, maybeTokenList, clickIntents) addInformationalNotifications(userWallet, cardTypesResolver, flattenCurrencies, clickIntents)
addWarningNotifications(cardTypesResolver, maybeTokenList, isNeedToBackup, clickIntents) addWarningNotifications(cardTypesResolver, flattenCurrencies, isNeedToBackup, clickIntents)
addPushReminderNotification( addPushReminderNotification(
clickIntents = clickIntents, clickIntents = clickIntents,
@ -104,7 +125,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
!notificationsRepository.isUserAllowToSubscribeOnPushNotifications(), !notificationsRepository.isUserAllowToSubscribeOnPushNotifications(),
) )
addYieldSupplyNotifications(maybeTokenList) addYieldSupplyNotifications(flattenCurrencies)
val hasCriticalOrWarning = any { notification -> val hasCriticalOrWarning = any { notification ->
notification is WalletNotification.Critical || notification is WalletNotification.Warning notification is WalletNotification.Critical || notification is WalletNotification.Warning
@ -118,16 +139,16 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
} }
private fun MutableList<WalletNotification>.addUsedOutdatedDataNotification( private fun MutableList<WalletNotification>.addUsedOutdatedDataNotification(
maybeTokenList: Lce<TokenListError, TokenList>, totalFiatBalance: Lce<TokenListError, TotalFiatBalance>,
) { ) {
addIf( addIf(
element = WalletNotification.UsedOutdatedData, element = WalletNotification.UsedOutdatedData,
condition = maybeTokenList.fold( condition = totalFiatBalance.fold(
ifLoading = { ifLoading = {
(it?.totalFiatBalance as? TotalFiatBalance.Loaded)?.source == StatusSource.ONLY_CACHE (it as? TotalFiatBalance.Loaded)?.source == StatusSource.ONLY_CACHE
}, },
ifContent = { ifContent = {
(it.totalFiatBalance as? TotalFiatBalance.Loaded)?.source == StatusSource.ONLY_CACHE (it as? TotalFiatBalance.Loaded)?.source == StatusSource.ONLY_CACHE
}, },
ifError = { false }, ifError = { false },
), ),
@ -205,7 +226,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private fun MutableList<WalletNotification>.addInformationalNotifications( private fun MutableList<WalletNotification>.addInformationalNotifications(
userWallet: UserWallet, userWallet: UserWallet,
cardTypesResolver: CardTypesResolver?, cardTypesResolver: CardTypesResolver?,
maybeTokenList: Lce<TokenListError, TokenList>, flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
clickIntents: WalletClickIntents, clickIntents: WalletClickIntents,
) { ) {
addIf( addIf(
@ -213,15 +234,15 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
condition = cardTypesResolver != null && isDemoCardUseCase(cardId = cardTypesResolver.getCardId()), condition = cardTypesResolver != null && isDemoCardUseCase(cardId = cardTypesResolver.getCardId()),
) )
addMissingAddressesNotification(userWallet, maybeTokenList, clickIntents) addMissingAddressesNotification(userWallet, flattenCurrencies, clickIntents)
} }
private fun MutableList<WalletNotification>.addMissingAddressesNotification( private fun MutableList<WalletNotification>.addMissingAddressesNotification(
userWallet: UserWallet, userWallet: UserWallet,
maybeTokenList: Lce<TokenListError, TokenList>, flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
clickIntents: WalletClickIntents, clickIntents: WalletClickIntents,
) { ) {
val currencies = maybeTokenList.getMissingAddressCurrencies() val currencies = flattenCurrencies.getMissingAddressCurrencies()
.ifEmpty { return } .ifEmpty { return }
addIf( addIf(
@ -236,11 +257,10 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
) )
} }
private fun Lce<TokenListError, TokenList>.getMissingAddressCurrencies(): List<CryptoCurrency> { private fun Lce<TokenListError, List<CryptoCurrencyStatus>>.getMissingAddressCurrencies(): List<CryptoCurrency> {
val tokenList = getOrNull(isPartialContentAccepted = true) ?: return emptyList() val flattenCurrencies = getOrNull(isPartialContentAccepted = true) ?: return emptyList()
return tokenList return flattenCurrencies
.flattenCurrencies()
.filter { it.value is CryptoCurrencyStatus.MissedDerivation } .filter { it.value is CryptoCurrencyStatus.MissedDerivation }
.map(CryptoCurrencyStatus::currency) .map(CryptoCurrencyStatus::currency)
} }
@ -292,17 +312,17 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
} }
private fun MutableList<WalletNotification>.addYieldSupplyNotifications( private fun MutableList<WalletNotification>.addYieldSupplyNotifications(
tokenList: Lce<TokenListError, TokenList>, flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
) { ) {
addIf( addIf(
element = WalletNotification.Warning.YeildSupplyApprove, element = WalletNotification.Warning.YeildSupplyApprove,
condition = tokenList.hasTokensWithActivatedSupplyWithoutApprove(), condition = flattenCurrencies.hasTokensWithActivatedSupplyWithoutApprove(),
) )
} }
private fun MutableList<WalletNotification>.addWarningNotifications( private fun MutableList<WalletNotification>.addWarningNotifications(
cardTypesResolver: CardTypesResolver?, cardTypesResolver: CardTypesResolver?,
tokenList: Lce<TokenListError, TokenList>, flattenCurrencies: Lce<TokenListError, List<CryptoCurrencyStatus>>,
isNeedToBackup: Boolean, isNeedToBackup: Boolean,
clickIntents: WalletClickIntents, clickIntents: WalletClickIntents,
) { ) {
@ -320,7 +340,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
addIf( addIf(
element = WalletNotification.Warning.SomeNetworksUnreachable, element = WalletNotification.Warning.SomeNetworksUnreachable,
condition = tokenList.hasUnreachableNetworks(), condition = flattenCurrencies.hasUnreachableNetworks(),
) )
} }
@ -337,16 +357,16 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
) )
} }
private fun Lce<TokenListError, TokenList>.hasUnreachableNetworks(): Boolean { private fun Lce<TokenListError, List<CryptoCurrencyStatus>>.hasUnreachableNetworks(): Boolean {
val tokenList = getOrNull(isPartialContentAccepted = false) ?: return false val flattenCurrencies = getOrNull(isPartialContentAccepted = false) ?: return false
return tokenList.flattenCurrencies().any { it.value is CryptoCurrencyStatus.Unreachable } return flattenCurrencies.any { it.value is CryptoCurrencyStatus.Unreachable }
} }
private fun Lce<TokenListError, TokenList>.hasTokensWithActivatedSupplyWithoutApprove(): Boolean { private fun Lce<TokenListError, List<CryptoCurrencyStatus>>.hasTokensWithActivatedSupplyWithoutApprove(): Boolean {
val tokenList = getOrNull(isPartialContentAccepted = false) ?: return false val flattenCurrencies = getOrNull(isPartialContentAccepted = false) ?: return false
val yieldSupplyEnabled = yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled val yieldSupplyEnabled = yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled
return yieldSupplyEnabled && tokenList.flattenCurrencies().any { return yieldSupplyEnabled && flattenCurrencies.any {
it.value.yieldSupplyStatus?.isAllowedToSpend == false it.value.yieldSupplyStatus?.isAllowedToSpend == false
} }
} }
@ -365,8 +385,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
) )
} }
private fun TokenList?.getFinishWalletActivationType(): WalletActivationBannerType { private fun TotalFiatBalance?.getFinishWalletActivationType(): WalletActivationBannerType {
return if ((this?.totalFiatBalance as? TotalFiatBalance.Loaded)?.amount?.isPositive() == true) { return if ((this as? TotalFiatBalance.Loaded)?.amount?.isPositive() == true) {
WalletActivationBannerType.Warning WalletActivationBannerType.Warning
} else { } else {
WalletActivationBannerType.Attention WalletActivationBannerType.Attention
@ -375,14 +395,14 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private fun MutableList<WalletNotification>.addFinishWalletActivationNotification( private fun MutableList<WalletNotification>.addFinishWalletActivationNotification(
userWallet: UserWallet, userWallet: UserWallet,
maybeTokenList: Lce<TokenListError, TokenList>, totalFiatBalance: Lce<TokenListError, TotalFiatBalance>,
clickIntents: WalletClickIntents, clickIntents: WalletClickIntents,
) { ) {
if (userWallet !is UserWallet.Hot) return if (userWallet !is UserWallet.Hot) return
val shouldShowFinishActivation = !userWallet.backedUp val shouldShowFinishActivation = !userWallet.backedUp
val type = maybeTokenList.fold( val type = totalFiatBalance.fold(
ifLoading = { it.getFinishWalletActivationType() }, ifLoading = { it.getFinishWalletActivationType() },
ifContent = { it.getFinishWalletActivationType() }, ifContent = { it.getFinishWalletActivationType() },
ifError = { WalletActivationBannerType.Attention }, ifError = { WalletActivationBannerType.Attention },