Updated on 2026-08-14
This commit is contained in:
parent
26201c5c0b
commit
0be1358760
39 changed files with 581 additions and 124 deletions
|
|
@ -11,6 +11,7 @@ import com.tangem.common.ui.tokens.getUnavailabilityReasonText
|
|||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic.ButtonSupport
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.analytics.models.event.OfframpAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
|
|
@ -27,10 +28,12 @@ import com.tangem.domain.account.status.usecase.IsCryptoCurrencyCouldHideUseCase
|
|||
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.extenstions.unwrap
|
||||
import com.tangem.domain.card.IsWalletBackupProblematicUseCase
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.utils.lceError
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.feedback.SendBackupProblemEmailUseCase
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
|
|
@ -58,6 +61,7 @@ import com.tangem.domain.transaction.usecase.ReceiveAddressesFactory
|
|||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
|
|
@ -144,6 +148,9 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
private val isCryptoCurrencyCouldHideUseCase: IsCryptoCurrencyCouldHideUseCase,
|
||||
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
private val isWalletBackupProblematicUseCase: IsWalletBackupProblematicUseCase,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val sendBackupProblemEmailUseCase: SendBackupProblemEmailUseCase,
|
||||
) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents {
|
||||
|
||||
override fun onSendClick(
|
||||
|
|
@ -196,6 +203,8 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
|
||||
event?.let { analyticsEventHandler.send(it) }
|
||||
|
||||
if (isTopUpBlockedByBackupError(accountId.userWalletId)) return
|
||||
|
||||
modelScope.launch(dispatchers.main) {
|
||||
if (needShowYieldSupplyWarning(cryptoCurrencyStatus)) {
|
||||
stateHolder.hideBottomSheet()
|
||||
|
|
@ -339,6 +348,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
if (isTopUpBlockedByBackupError(accountId.userWalletId)) return
|
||||
|
||||
appRouter.push(
|
||||
AppRoute.Onramp(
|
||||
|
|
@ -363,6 +373,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
if (isTopUpBlockedByBackupError(accountId.userWalletId)) return
|
||||
|
||||
modelScope.launch(dispatchers.main) {
|
||||
if (needShowYieldSupplyWarning(cryptoCurrencyStatus)) {
|
||||
|
|
@ -451,6 +462,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onMultiWalletSwapClick(userWalletId: UserWalletId) {
|
||||
if (isTopUpBlockedByBackupError(userWalletId)) return
|
||||
if (!isMultiWalletTokensLoaded()) return
|
||||
|
||||
modelScope.launch {
|
||||
|
|
@ -469,6 +481,8 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onMultiWalletBuyClick(userWalletId: UserWalletId, screenType: String) {
|
||||
if (isTopUpBlockedByBackupError(userWalletId)) return
|
||||
|
||||
onMultiWalletActionClick(
|
||||
statusFlow = rampStateManager.getExpressInitializationStatus(userWalletId),
|
||||
route = AppRoute.BuyCrypto(userWalletId = userWalletId),
|
||||
|
|
@ -565,6 +579,26 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
return true
|
||||
}
|
||||
|
||||
private fun isTopUpBlockedByBackupError(userWalletId: UserWalletId): Boolean {
|
||||
val userWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: return false
|
||||
if (!isWalletBackupProblematicUseCase(userWallet)) return false
|
||||
|
||||
stateHolder.hideBottomSheet()
|
||||
uiMessageSender.send(
|
||||
WalletAlertUM.addFundsDisabledForBackupError(
|
||||
onContactSupport = { contactBackupSupport(userWallet) },
|
||||
),
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun contactBackupSupport(userWallet: UserWallet) {
|
||||
analyticsEventHandler.send(ButtonSupport(source = AnalyticsParam.ScreensSources.Main))
|
||||
modelScope.launch {
|
||||
sendBackupProblemEmailUseCase(userWallet.walletId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isMultiWalletTokensLoaded(): Boolean {
|
||||
return if (stateHolder.value.isRedesignEnabled) {
|
||||
val selectedWalletUM = stateHolder.getSelectedWalletUM() as? WalletUM.Content ?: return false
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import com.tangem.common.card.EllipticCurve
|
||||
import com.tangem.domain.card.configs.CardConfig
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import javax.inject.Inject
|
||||
|
||||
class BackupValidator @Inject constructor() {
|
||||
|
||||
fun isValidFull(cardDTO: CardDTO): Boolean {
|
||||
return validateBackupStatus(cardDTO) && validateCurves(cardDTO)
|
||||
}
|
||||
|
||||
fun isValidBackupStatus(cardDTO: CardDTO): Boolean {
|
||||
return validateBackupStatus(cardDTO)
|
||||
}
|
||||
|
||||
private fun validateCurves(cardDTO: CardDTO): Boolean {
|
||||
val config = CardConfig.createConfig(cardDTO)
|
||||
// / Since the curve `bls12381_G2_AUG` was added later into first generation of wallets,
|
||||
// we cannot determine whether this curve is missing due to an error or because the user
|
||||
// did not want to recreate the wallet.
|
||||
val expectedCurves = config.mandatoryCurves
|
||||
.filterNot { it == EllipticCurve.Bls12381G2Aug }
|
||||
val curves = cardDTO.wallets.map { it.curve }
|
||||
for (expectedCurve in expectedCurves) {
|
||||
val cardCurvesCount = curves.count { it == expectedCurve }
|
||||
|
||||
// missing curve
|
||||
if (cardCurvesCount == 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
// duplicated curve
|
||||
if (cardCurvesCount > 1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun validateBackupStatus(cardDTO: CardDTO): Boolean {
|
||||
val backupStatus = cardDTO.backupStatus
|
||||
backupStatus ?: return true // for card with null backup status, validation should always returns true
|
||||
return backupStatus !is CardDTO.BackupStatus.CardLinked
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
|||
import com.tangem.domain.assetsdiscovery.model.AssetsDiscoveryProgress
|
||||
import com.tangem.domain.assetsdiscovery.usecase.ObserveAssetsDiscoveryUseCase
|
||||
import com.tangem.domain.card.CardTypesResolver
|
||||
import com.tangem.domain.card.IsWalletBackupProblematicUseCase
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.hotwallet.CheckHotWalletUpgradeBannerUseCase
|
||||
|
|
@ -56,7 +57,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||
private val backupValidator: BackupValidator,
|
||||
private val isWalletBackupProblematicUseCase: IsWalletBackupProblematicUseCase,
|
||||
private val notificationsRepository: NotificationsRepository,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val getAccessCodeSkippedUseCase: GetAccessCodeSkippedUseCase,
|
||||
|
|
@ -118,6 +119,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
val isAddFundsBannerShown = isAddFundsBannerVisible(accountStatusList.totalFiatBalance)
|
||||
|
||||
buildList {
|
||||
addBackupErrorNotification(userWallet, clickIntents)
|
||||
|
||||
addUsedOutdatedDataNotification(accountStatusList.totalFiatBalance)
|
||||
|
||||
addAddFundsBanner(
|
||||
|
|
@ -126,7 +129,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
clickIntents = clickIntents,
|
||||
)
|
||||
|
||||
addCriticalNotifications(userWallet, clickIntents)
|
||||
addCriticalNotifications(userWallet)
|
||||
|
||||
addUpgradeHotWalletPromoNotification(
|
||||
userWallet = userWallet,
|
||||
|
|
@ -270,20 +273,22 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addCriticalNotifications(
|
||||
private fun MutableList<WalletNotification>.addBackupErrorNotification(
|
||||
userWallet: UserWallet,
|
||||
clickIntents: WalletClickIntents,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.Critical.BackupError { clickIntents.onBackupErrorClick() },
|
||||
condition = isWalletBackupProblematicUseCase(userWallet),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addCriticalNotifications(userWallet: UserWallet) {
|
||||
if (userWallet !is UserWallet.Cold) {
|
||||
return
|
||||
}
|
||||
|
||||
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
|
||||
addIf(
|
||||
element = WalletNotification.Critical.BackupError { clickIntents.onBackupErrorClick() },
|
||||
condition = !backupValidator.isValidBackupStatus(userWallet.scanResponse.card) || userWallet.hasBackupError,
|
||||
)
|
||||
|
||||
addIf(
|
||||
element = WalletNotification.Critical.DevCard,
|
||||
condition = !cardTypesResolver.isReleaseFirmwareType(),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
|||
import com.tangem.domain.assetsdiscovery.model.AssetsDiscoveryProgress
|
||||
import com.tangem.domain.assetsdiscovery.usecase.ObserveAssetsDiscoveryUseCase
|
||||
import com.tangem.domain.card.CardTypesResolver
|
||||
import com.tangem.domain.card.IsWalletBackupProblematicUseCase
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.hotwallet.GetAccessCodeSkippedUseCase
|
||||
|
|
@ -44,7 +45,7 @@ import javax.inject.Inject
|
|||
internal class GetWalletNotificationsFactory @Inject constructor(
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||
private val backupValidator: BackupValidator,
|
||||
private val isWalletBackupProblematicUseCase: IsWalletBackupProblematicUseCase,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val getAccessCodeSkippedUseCase: GetAccessCodeSkippedUseCase,
|
||||
private val hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase,
|
||||
|
|
@ -167,7 +168,7 @@ internal class GetWalletNotificationsFactory @Inject constructor(
|
|||
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
|
||||
addIf(
|
||||
element = WalletNotificationUM.BackupError { clickIntents.onSupportClick() },
|
||||
condition = !backupValidator.isValidBackupStatus(userWallet.scanResponse.card) || userWallet.hasBackupError,
|
||||
condition = isWalletBackupProblematicUseCase(userWallet),
|
||||
)
|
||||
|
||||
addIf(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.ui.extensions.WrappedList
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.core.ui.message.dialog.Dialogs
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
||||
|
|
@ -55,6 +56,10 @@ internal object WalletAlertUM {
|
|||
)
|
||||
}
|
||||
|
||||
fun addFundsDisabledForBackupError(onContactSupport: () -> Unit): DialogMessage {
|
||||
return Dialogs.backupErrorAddFundsDisabled(onContactSupport = onContactSupport)
|
||||
}
|
||||
|
||||
fun insufficientTokensCountForSwapping(): DialogMessage {
|
||||
return DialogMessage(
|
||||
title = resourceReference(R.string.action_buttons_swap_no_tokens_added_alert_title),
|
||||
|
|
|
|||
|
|
@ -50,9 +50,9 @@ sealed class WalletNotification(val config: NotificationConfig) {
|
|||
)
|
||||
|
||||
data class BackupError(val onSupportClick: () -> Unit) : Critical(
|
||||
title = resourceReference(R.string.warning_backup_errors_title),
|
||||
subtitle = resourceReference(R.string.warning_backup_errors_message),
|
||||
buttonsState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
|
||||
title = resourceReference(R.string.warning_backup_error_attention_title),
|
||||
subtitle = resourceReference(R.string.warning_backup_error_attention_message),
|
||||
buttonsState = ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(id = R.string.common_contact_support),
|
||||
onClick = onSupportClick,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import com.tangem.core.ui.extensions.annotatedReference
|
|||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.ForceDarkTheme
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.test.WalletNotificationTestTags
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
|
|
@ -66,6 +67,16 @@ internal fun LazyListScope.notifications(configs: ImmutableList<WalletNotificati
|
|||
subtitleColor = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
}
|
||||
is WalletNotification.Critical.BackupError -> {
|
||||
Notification(
|
||||
config = item.config,
|
||||
modifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null),
|
||||
containerColor = TangemColorPalette.Amaranth_30,
|
||||
borderColor = TangemColorPalette.Amaranth,
|
||||
iconTint = TangemTheme.colors.icon.warning,
|
||||
subtitleColor = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
is WalletNotification.CreateTangemPayAccount -> {
|
||||
CreatePaymentAccountNotification(
|
||||
modifier = modifier.animateItem(fadeInSpec = null, fadeOutSpec = null),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue