Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-25 17:27:32 +05:00
parent 2f7bcb9cbb
commit d050fa2654
18 changed files with 12 additions and 568 deletions

View file

@ -2,7 +2,6 @@ package com.tangem.feature.wallet.child.wallet.model.intents
import arrow.core.getOrElse
import com.tangem.utils.logging.TangemLogger
import com.tangem.common.TangemBlogUrlBuilder
import com.tangem.common.routing.AppRoute.*
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.notifications.NotificationId
@ -45,7 +44,6 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnaly
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertUM
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnalyticEvents
@ -85,14 +83,6 @@ internal interface WalletWarningsClickIntents {
fun onNoteMigrationButtonClick(url: String)
fun onSeedPhraseNotificationConfirm()
fun onSeedPhraseNotificationDecline()
fun onSeedPhraseSecondNotificationAccept()
fun onSeedPhraseSecondNotificationReject()
fun onAllowPermissions()
fun onDenyPermissions()
@ -122,7 +112,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val urlOpener: UrlOpener,
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
private val multiQuoteStatusFetcher: MultiQuoteStatusFetcher,
@ -360,66 +349,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
}
override fun onSeedPhraseNotificationConfirm() {
val userWallet = getSelectedUserWallet() ?: return
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonYes())
uiMessageSender.send(
WalletAlertUM.seedPhraseConfirm {
modelScope.launch {
seedPhraseNotificationUseCase.confirm(userWalletId = userWallet.walletId)
urlOpener.openUrl(
url = TangemBlogUrlBuilder.build(post = TangemBlogUrlBuilder.Post.SeedNotify),
)
}
},
)
}
override fun onSeedPhraseNotificationDecline() {
val userWallet = getSelectedUserWallet() ?: return
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonNo())
uiMessageSender.send(
WalletAlertUM.seedPhraseDismiss {
modelScope.launch {
seedPhraseNotificationUseCase.decline(userWalletId = userWallet.walletId)
}
},
)
}
override fun onSeedPhraseSecondNotificationAccept() {
val userWallet = getSelectedUserWallet() ?: return
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonUsed())
uiMessageSender.send(
WalletAlertUM.seedPhraseConfirm {
modelScope.launch {
seedPhraseNotificationUseCase.acceptSecond(userWalletId = userWallet.walletId)
urlOpener.openUrl(
url = TangemBlogUrlBuilder.build(post = TangemBlogUrlBuilder.Post.SeedNotifySecond),
)
}
},
)
}
override fun onSeedPhraseSecondNotificationReject() {
val userWallet = getSelectedUserWallet() ?: return
analyticsEventHandler.send(MainScreen.NoticeSeedPhraseSupportButtonDeclined())
modelScope.launch {
seedPhraseNotificationUseCase.rejectSecond(userWalletId = userWallet.walletId)
}
}
override fun onFinishWalletActivationClick(isBackupExists: Boolean) {
analyticsEventHandler.send(MainScreen.ButtonFinalizeActivation())
val userWalletId = stateHolder.getSelectedWalletId()

View file

@ -155,18 +155,6 @@ sealed class WalletScreenAnalyticsEvent {
class DeleteWalletTapped : MainScreen(event = "Button - Delete Wallet Tapped")
class NoticeSeedPhraseSupport : MainScreen(event = "Notice - Seed Phrase Support")
class NoticeSeedPhraseSupportSecond : MainScreen(event = "Notice - Seed Phrase Support2")
class NoticeSeedPhraseSupportButtonNo : MainScreen(event = "Button - Support No")
class NoticeSeedPhraseSupportButtonYes : MainScreen(event = "Button - Support Yes")
class NoticeSeedPhraseSupportButtonUsed : MainScreen(event = "Button - Support Used")
class NoticeSeedPhraseSupportButtonDeclined : MainScreen(event = "Button - Support Declined")
class NoticeUnrecognizedQr : MainScreen(
event = "Notice - Unrecognized QR",
)

View file

@ -116,8 +116,6 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
balanceState = balanceState,
)
}
is WalletNotification.Critical.SeedPhraseNotification -> NoticeSeedPhraseSupport()
is WalletNotification.Critical.SeedPhraseSecondNotification -> NoticeSeedPhraseSupportSecond()
is WalletNotification.PushNotifications -> PushBanner()
is WalletNotification.Warning.TangemPayRefreshNeeded -> null
is WalletNotification.Warning.TangemPayUnreachable -> null
@ -163,8 +161,6 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
balanceState = balanceState,
)
}
is WalletNotificationUM.SeedPhraseNotification -> NoticeSeedPhraseSupport()
is WalletNotificationUM.SeedPhraseSecondNotification -> NoticeSeedPhraseSupportSecond()
is WalletNotificationUM.PushNotifications -> PushBanner()
is WalletNotificationUM.UnlockWallets,
is WalletNotificationUM.NoAccount,

View file

@ -11,7 +11,6 @@ import com.tangem.core.ui.message.bottomSheetMessage
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
@ -24,7 +23,6 @@ import javax.inject.Inject
@ModelScoped
internal class WalletWarningsSingleEventSender @Inject constructor(
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val screenLifecycleProvider: ScreenLifecycleProvider,
private val uiMessageSender: UiMessageSender,
private val getUserWalletUseCase: GetUserWalletUseCase,
@ -32,11 +30,7 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
) {
private val isActivationBottomSheetShown: ConcurrentHashMap<UserWalletId, Boolean> = ConcurrentHashMap()
suspend fun send(
userWalletId: UserWalletId,
displayedUiState: WalletState?,
newWarnings: List<WalletNotification>,
) {
fun send(userWalletId: UserWalletId, displayedUiState: WalletState?, newWarnings: List<WalletNotification>) {
if (screenLifecycleProvider.isBackgroundState.value) return
if (newWarnings.isEmpty()) return
if (displayedUiState == null || displayedUiState.pullToRefreshConfig.isRefreshing) return
@ -51,9 +45,6 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
events.forEach { event ->
when (event) {
is WalletNotification.Critical.SeedPhraseNotification -> {
seedPhraseNotificationUseCase.notified(userWalletId = userWalletId)
}
is WalletNotification.FinishWalletActivation -> {
// We check that map contains the first seen wallet (will return null instead false/true otherwise)
// and for this wallet we haven't shown the activation bs yet (check that returns false, not true)
@ -69,11 +60,7 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
}
}
suspend fun send(
userWalletId: UserWalletId,
displayedWalletUM: WalletUM?,
newNotifications: List<WalletNotificationUM>,
) {
fun send(userWalletId: UserWalletId, displayedWalletUM: WalletUM?, newNotifications: List<WalletNotificationUM>) {
if (screenLifecycleProvider.isBackgroundState.value) return
if (newNotifications.isEmpty()) return
if (displayedWalletUM == null || displayedWalletUM.pullToRefreshConfig.isRefreshing) return
@ -89,9 +76,6 @@ internal class WalletWarningsSingleEventSender @Inject constructor(
events.forEach { event ->
when (event) {
is WalletNotificationUM.SeedPhraseNotification -> {
seedPhraseNotificationUseCase.notified(userWalletId = userWalletId)
}
is WalletNotificationUM.FinishWalletActivation -> {
// We check that map contains the first seen wallet (will return null instead false/true otherwise)
// and for this wallet we haven't shown the activation bs yet (check that returns false, not true)

View file

@ -27,9 +27,7 @@ import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.promo.ShouldShowPromoWalletUseCase
import com.tangem.domain.promo.models.PromoId
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
import com.tangem.domain.wallets.models.SeedPhraseNotificationsStatus
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
import com.tangem.feature.wallet.child.wallet.model.WalletActivationBannerType
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.impl.R
@ -56,7 +54,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
private val backupValidator: BackupValidator,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val shouldShowPromoWalletUseCase: ShouldShowPromoWalletUseCase,
private val notificationsRepository: NotificationsRepository,
private val accountDependencies: AccountDependencies,
@ -76,7 +73,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
accountStatusListFlow,
isReadyToShowRateAppUseCase().distinctUntilChanged(),
isNeedToBackupUseCase(userWallet.walletId).distinctUntilChanged(),
seedPhraseNotificationUseCase(userWalletId = userWallet.walletId).distinctUntilChanged(),
shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.OnePlusOne)
.distinctUntilChanged(),
notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key)
@ -93,13 +89,12 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val accountStatusList = array[0] as AccountStatusList
val isReadyToShowRating = array[1] as Boolean
val isNeedToBackup = array[2] as Boolean
val seedPhraseIssueStatus = array[3] as SeedPhraseNotificationsStatus
val shouldShowOnePlusOnePromo = array[4] as Boolean
val shouldShowEnablePushesReminderNotification = array[5] as Boolean
val shouldAccessCodeSkipped = array[6] as Boolean
val shouldShowYieldPromo = array[7] as Boolean
val shouldShowUpgradeBanner = array[8] as Boolean
val closureTimestamp = array[9] as? Long
val shouldShowOnePlusOnePromo = array[3] as Boolean
val shouldShowEnablePushesReminderNotification = array[4] as Boolean
val shouldAccessCodeSkipped = array[5] as Boolean
val shouldShowYieldPromo = array[6] as Boolean
val shouldShowUpgradeBanner = array[7] as Boolean
val closureTimestamp = array[8] as? Long
val flattenCurrencies = accountStatusList.flattenCurrencies()
val paymentAccountStatus = accountStatusList.accountStatuses
@ -109,7 +104,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
buildList {
addUsedOutdatedDataNotification(accountStatusList.totalFiatBalance)
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
addCriticalNotifications(userWallet, clickIntents)
addUpgradeHotWalletPromoNotification(
userWallet = userWallet,
@ -213,15 +208,12 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
private fun MutableList<WalletNotification>.addCriticalNotifications(
userWallet: UserWallet,
seedPhraseIssueStatus: SeedPhraseNotificationsStatus,
clickIntents: WalletClickIntents,
) {
if (userWallet !is UserWallet.Cold) {
return
}
addSeedNotificationIfNeeded(userWallet, seedPhraseIssueStatus, clickIntents)
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
addIf(
element = WalletNotification.Critical.BackupError { clickIntents.onBackupErrorClick() },
@ -246,39 +238,6 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
}
}
private fun MutableList<WalletNotification>.addSeedNotificationIfNeeded(
userWallet: UserWallet.Cold,
seedPhraseIssueStatus: SeedPhraseNotificationsStatus,
clickIntents: WalletClickIntents,
) {
val isNotificationAvailable = with(userWallet) {
val isDemo = isDemoCardUseCase(cardId = userWallet.cardId)
val isWalletWithSeedPhrase = scanResponse.cardTypesResolver.isWallet2() && userWallet.isImported
!isDemo && isWalletWithSeedPhrase
}
when (seedPhraseIssueStatus) {
SeedPhraseNotificationsStatus.SHOW_FIRST -> addIf(
element = WalletNotification.Critical.SeedPhraseNotification(
onDeclineClick = clickIntents::onSeedPhraseNotificationDecline,
onConfirmClick = clickIntents::onSeedPhraseNotificationConfirm,
),
condition = isNotificationAvailable,
)
SeedPhraseNotificationsStatus.SHOW_SECOND -> addIf(
element = WalletNotification.Critical.SeedPhraseSecondNotification(
onDeclineClick = clickIntents::onSeedPhraseSecondNotificationReject,
onConfirmClick = clickIntents::onSeedPhraseSecondNotificationAccept,
),
condition = isNotificationAvailable,
)
SeedPhraseNotificationsStatus.NOT_NEEDED -> {
// do nothing
}
}
}
private fun MutableList<WalletNotification>.addInformationalNotifications(
userWallet: UserWallet,
cardTypesResolver: CardTypesResolver?,

View file

@ -14,9 +14,7 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.wallets.models.SeedPhraseNotificationsStatus
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.domain.wallets.usecase.SeedPhraseNotificationUseCase
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotificationUM
@ -40,7 +38,6 @@ internal class GetWalletNotificationsFactory @Inject constructor(
private val isDemoCardUseCase: IsDemoCardUseCase,
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
private val backupValidator: BackupValidator,
private val seedPhraseNotificationUseCase: SeedPhraseNotificationUseCase,
private val accountDependencies: AccountDependencies,
private val getAccessCodeSkippedUseCase: GetAccessCodeSkippedUseCase,
private val hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase,
@ -54,16 +51,15 @@ internal class GetWalletNotificationsFactory @Inject constructor(
return combine(
flow = accountStatusListFlow,
flow2 = isNeedToBackupUseCase(userWallet.walletId).distinctUntilChanged(),
flow3 = seedPhraseNotificationUseCase(userWalletId = userWallet.walletId).distinctUntilChanged(),
flow4 = getAccessCodeSkippedUseCase(userWallet.walletId).distinctUntilChanged(),
) { accountList, isNeedToBackup, seedPhraseIssueStatus, shouldAccessCodeSkipped ->
flow3 = getAccessCodeSkippedUseCase(userWallet.walletId).distinctUntilChanged(),
) { accountList, isNeedToBackup, shouldAccessCodeSkipped ->
val totalFiatBalance = accountList.totalFiatBalance
val flattenCurrencies = accountList.flattenCurrencies()
buildList {
addUsedOutdatedDataNotification(totalFiatBalance)
addCriticalNotifications(userWallet, seedPhraseIssueStatus, clickIntents)
addCriticalNotifications(userWallet, clickIntents)
addFinishWalletActivationNotification(
userWallet = userWallet,
@ -99,15 +95,12 @@ internal class GetWalletNotificationsFactory @Inject constructor(
private fun MutableList<WalletNotificationUM>.addCriticalNotifications(
userWallet: UserWallet,
seedPhraseIssueStatus: SeedPhraseNotificationsStatus,
clickIntents: WalletClickIntents,
) {
if (userWallet !is UserWallet.Cold) {
return
}
addSeedNotificationIfNeeded(userWallet, seedPhraseIssueStatus, clickIntents)
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
addIf(
element = WalletNotificationUM.BackupError { clickIntents.onSupportClick() },
@ -279,37 +272,6 @@ internal class GetWalletNotificationsFactory @Inject constructor(
)
}
private fun MutableList<WalletNotificationUM>.addSeedNotificationIfNeeded(
userWallet: UserWallet.Cold,
seedPhraseIssueStatus: SeedPhraseNotificationsStatus,
clickIntents: WalletClickIntents,
) {
val isNotificationAvailable = with(userWallet) {
val isDemo = isDemoCardUseCase(cardId = userWallet.cardId)
val isWalletWithSeedPhrase = scanResponse.cardTypesResolver.isWallet2() && userWallet.isImported
!isDemo && isWalletWithSeedPhrase
}
when (seedPhraseIssueStatus) {
SeedPhraseNotificationsStatus.SHOW_FIRST -> addIf(
element = WalletNotificationUM.SeedPhraseNotification(
onDeclineClick = clickIntents::onSeedPhraseNotificationDecline,
onConfirmClick = clickIntents::onSeedPhraseNotificationConfirm,
),
condition = isNotificationAvailable,
)
SeedPhraseNotificationsStatus.SHOW_SECOND -> addIf(
element = WalletNotificationUM.SeedPhraseSecondNotification(
onDeclineClick = clickIntents::onSeedPhraseSecondNotificationReject,
onConfirmClick = clickIntents::onSeedPhraseSecondNotificationAccept,
),
condition = isNotificationAvailable,
)
SeedPhraseNotificationsStatus.NOT_NEEDED -> Unit
}
}
private suspend fun hasSignedHashes(
selectedWallet: UserWallet,
cryptoCurrencyStatus: CryptoCurrencyStatus?,

View file

@ -9,24 +9,6 @@ import com.tangem.feature.wallet.impl.R
internal object WalletAlertUM {
fun seedPhraseConfirm(onClick: () -> Unit): DialogMessage {
return DialogMessage(
message = resourceReference(R.string.warning_seedphrase_issue_answer_yes),
firstActionBuilder = {
okAction(onClick = onClick)
},
)
}
fun seedPhraseDismiss(onClick: () -> Unit): DialogMessage {
return DialogMessage(
message = resourceReference(R.string.warning_seedphrase_issue_answer_no),
firstActionBuilder = {
okAction(onClick = onClick)
},
)
}
fun unableHideToken(cryptoCurrency: CryptoCurrency): DialogMessage {
return DialogMessage(
title = resourceReference(

View file

@ -55,34 +55,6 @@ sealed class WalletNotification(val config: NotificationConfig) {
onClick = onSupportClick,
),
)
data class SeedPhraseNotification(
val onDeclineClick: () -> Unit,
val onConfirmClick: () -> Unit,
) : Critical(
title = resourceReference(R.string.warning_seedphrase_issue_title),
subtitle = resourceReference(R.string.warning_seedphrase_issue_message),
buttonsState = NotificationConfig.ButtonsState.SecondaryPairButtonsConfig(
leftText = resourceReference(R.string.common_no),
onLeftClick = onDeclineClick,
rightText = resourceReference(R.string.common_yes),
onRightClick = onConfirmClick,
),
)
data class SeedPhraseSecondNotification(
val onDeclineClick: () -> Unit,
val onConfirmClick: () -> Unit,
) : Critical(
title = resourceReference(R.string.warning_seedphrase_action_required_title),
subtitle = resourceReference(R.string.warning_seedphrase_contacted_support),
buttonsState = NotificationConfig.ButtonsState.SecondaryPairButtonsConfig(
leftText = resourceReference(R.string.seed_warning_no),
onLeftClick = onDeclineClick,
rightText = resourceReference(R.string.seed_warning_yes),
onRightClick = onConfirmClick,
),
)
}
sealed class Warning(

View file

@ -143,64 +143,6 @@ internal sealed class WalletNotificationUM(val messageUM: TangemMessageUM, val t
type = WalletNotificationType.Critical,
)
data class SeedPhraseNotification(
val onDeclineClick: () -> Unit,
val onConfirmClick: () -> Unit,
) : WalletNotificationUM(
messageUM = TangemMessageUM(
id = "SeedPhraseIssueNotification",
title = resourceReference(id = R.string.warning_seedphrase_issue_title),
subtitle = resourceReference(id = R.string.warning_seedphrase_issue_message),
messageEffect = TangemMessageEffect.Warning,
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(id = R.string.common_no),
type = TangemButtonType.PrimaryInverse,
onClick = onDeclineClick,
),
TangemMessageButtonUM(
text = resourceReference(id = R.string.common_yes),
type = TangemButtonType.PrimaryInverse,
onClick = onConfirmClick,
),
),
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
),
type = WalletNotificationType.Critical,
)
data class SeedPhraseSecondNotification(
val onDeclineClick: () -> Unit,
val onConfirmClick: () -> Unit,
) : WalletNotificationUM(
messageUM = TangemMessageUM(
id = "SeedPhraseSecondIssueNotification",
title = resourceReference(id = R.string.warning_seedphrase_action_required_title),
subtitle = resourceReference(id = R.string.warning_seedphrase_contacted_support),
messageEffect = TangemMessageEffect.Warning,
iconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_attention_default_24,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
buttonsUM = persistentListOf(
TangemMessageButtonUM(
text = resourceReference(id = R.string.seed_warning_no),
type = TangemButtonType.PrimaryInverse,
onClick = onDeclineClick,
),
TangemMessageButtonUM(
text = resourceReference(id = R.string.seed_warning_yes),
type = TangemButtonType.PrimaryInverse,
onClick = onConfirmClick,
),
),
),
type = WalletNotificationType.Critical,
)
data class MissingBackup(val onClick: () -> Unit) : WalletNotificationUM(
messageUM = TangemMessageUM(
id = "MissingBackupNotification",