From 210f392373cee5710cc3ae06bfa49b1548a5e62f Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 15 Jun 2026 05:54:18 -0700 Subject: [PATCH] Updated on 2026-08-14 --- .../configs/feature_toggles_config.json | 4 ++ core/res/src/main/res/values/strings.xml | 3 + .../tangempay/TangemPayFeatureToggles.kt | 1 + .../DefaultTangemPayFeatureToggles.kt | 3 + .../entity/TangemPayDetailsStateFactory.kt | 64 ++++++++++++++++++- .../entity/TangemPayDropDownItemUM.kt | 2 + .../tangempay/model/TangemPayDetailsModel.kt | 35 ++++++++++ .../tangempay/ui/TangemPayDetailsScreenV2.kt | 42 ++++++++---- .../ui/components/PayContextMenuBlock.kt | 8 +-- .../tangempay/utils/TangemPayDetailIntents.kt | 1 + 10 files changed, 146 insertions(+), 17 deletions(-) diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json index f6bd556683..741569571b 100644 --- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json @@ -135,6 +135,10 @@ "name": "AND_15364_VISA_PAY_CARD_CLOSE", "version": "undefined" }, + { + "name": "AND_15741_VISA_PAY_REMOVE_ACCOUNT", + "version": "undefined" + }, { "name": "TWI_83_ADDRESS_BOOK_ENABLED", "version": "undefined" diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 3aef2ddfa3..4bf9538ce4 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1930,6 +1930,9 @@ Deposit USDC to payment account to cover the issuing fee Unable to cover fee Replace your card? + Remove account + Tangem Pay will be removed from the main screen and won\'t appear again, even after reinstalling the app. + Remove account? We’re fixing a technical issue. Please try again later. Service temporarily unavailable Service unreachable. However, card payments are still working. diff --git a/features/tangempay/details/api/src/main/kotlin/com/tangem/features/tangempay/TangemPayFeatureToggles.kt b/features/tangempay/details/api/src/main/kotlin/com/tangem/features/tangempay/TangemPayFeatureToggles.kt index aa6c289349..3aeb39bc0b 100644 --- a/features/tangempay/details/api/src/main/kotlin/com/tangem/features/tangempay/TangemPayFeatureToggles.kt +++ b/features/tangempay/details/api/src/main/kotlin/com/tangem/features/tangempay/TangemPayFeatureToggles.kt @@ -3,4 +3,5 @@ package com.tangem.features.tangempay interface TangemPayFeatureToggles { val isRedesignEnabled: Boolean val isCloseCardEnabled: Boolean + val isRemoveAccountEnabled: Boolean } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/DefaultTangemPayFeatureToggles.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/DefaultTangemPayFeatureToggles.kt index aa2166183f..8cf9c58dc1 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/DefaultTangemPayFeatureToggles.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/DefaultTangemPayFeatureToggles.kt @@ -12,4 +12,7 @@ internal class DefaultTangemPayFeatureToggles( override val isCloseCardEnabled: Boolean get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.AND_15364_VISA_PAY_CARD_CLOSE) + + override val isRemoveAccountEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled(FeatureToggles.AND_15741_VISA_PAY_REMOVE_ACCOUNT) } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt index e0591414d9..ea4b2a37a6 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt @@ -18,6 +18,8 @@ import com.tangem.features.tangempay.details.impl.R import com.tangem.features.tangempay.utils.TangemPayDetailIntents import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList +import com.tangem.core.ui.R as CoreUiR @Suppress("LongParameterList") internal class TangemPayDetailsStateFactory( @@ -25,6 +27,7 @@ internal class TangemPayDetailsStateFactory( private val onOpenMenu: () -> Unit, private val intents: TangemPayDetailIntents, private val isRedesignEnabled: Boolean, + private val isRemoveAccountEnabled: Boolean, ) { fun getLoadingState(): TangemPayDetailsUM { return TangemPayDetailsUM( @@ -102,8 +105,8 @@ internal class TangemPayDetailsStateFactory( topBarConfig = TangemPayDetailsTopBarConfig( onBackClick = onBack, onOpenMenu = onOpenMenu, - items = persistentListOf(), - itemsV2 = persistentListOf(), + items = getDeactivatedMenuItems(), + itemsV2 = getDeactivatedMenuItemsV2(), ), pullToRefreshConfig = PullToRefreshConfig( isRefreshing = false, @@ -131,6 +134,14 @@ internal class TangemPayDetailsStateFactory( title = resourceReference(R.string.tangempay_account_deactivated_message_title), subtitle = resourceReference(R.string.tangempay_account_deactivated_message_subtitle), iconResId = R.drawable.img_attention_20, + buttonsState = if (isRemoveAccountEnabled) { + NotificationConfig.ButtonsState.SecondaryButtonConfig( + text = resourceReference(R.string.tangempay_remove_account), + onClick = intents::onRemoveAccount, + ) + } else { + null + }, ) private fun createRenewSessionNotificationConfig() = NotificationConfig( @@ -159,6 +170,55 @@ internal class TangemPayDetailsStateFactory( ) } + private fun getDeactivatedMenuItems(): ImmutableList { + return buildList { + add( + TangemDropdownMenuItem( + title = resourceReference(R.string.tangempay_pay_support), + textColor = themedColor { TangemTheme.colors.text.primary1 }, + onClick = intents::onContactSupportClicked, + ), + ) + if (isRemoveAccountEnabled) { + add( + TangemDropdownMenuItem( + title = resourceReference(R.string.tangempay_remove_account), + textColor = themedColor { TangemTheme.colors.text.warning }, + onClick = intents::onRemoveAccount, + ), + ) + } + }.toImmutableList() + } + + private fun getDeactivatedMenuItemsV2(): ImmutableList { + return buildList { + add( + TangemPayDropDownItemUM( + title = resourceReference(R.string.tangempay_pay_support), + onClick = intents::onContactSupportClicked, + icon = TangemIconUM.Icon( + iconRes = R.drawable.ic_mail_20, + tintReference = { TangemTheme.colors3.icon.primary }, + ), + ), + ) + if (isRemoveAccountEnabled) { + add( + TangemPayDropDownItemUM( + title = resourceReference(R.string.tangempay_remove_account), + onClick = intents::onRemoveAccount, + icon = TangemIconUM.Icon( + iconRes = CoreUiR.drawable.ic_trash_24, + tintReference = { TangemTheme.colors3.icon.status.error }, + ), + titleColor = { TangemTheme.colors3.text.status.error }, + ), + ) + } + }.toImmutableList() + } + private fun getTopBarMenuItemsV2(): ImmutableList { return persistentListOf( TangemPayDropDownItemUM( diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDropDownItemUM.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDropDownItemUM.kt index 904b272672..562ba310be 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDropDownItemUM.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDropDownItemUM.kt @@ -1,6 +1,7 @@ package com.tangem.features.tangempay.entity import com.tangem.core.ui.ds.image.TangemIconUM +import com.tangem.core.ui.extensions.ColorReference2 import com.tangem.core.ui.extensions.TextReference internal data class TangemPayDropDownItemUM( @@ -9,4 +10,5 @@ internal data class TangemPayDropDownItemUM( val icon: TangemIconUM, val subtitle: TextReference? = null, val isEnabled: Boolean = true, + val titleColor: ColorReference2? = null, ) \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt index 2712ea3c99..60462a0036 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt @@ -16,6 +16,8 @@ import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.navigation.url.UrlOpener import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig.ShowRefreshState 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.SnackbarMessage import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase @@ -27,6 +29,7 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier import com.tangem.domain.pay.model.TangemPayTopUpData +import com.tangem.domain.pay.repository.OnboardingRepository import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository import com.tangem.domain.pay.repository.TangemPayWithdrawRepository import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase @@ -75,6 +78,7 @@ internal class TangemPayDetailsModel @Inject constructor( private val tangemPayFeatureToggles: TangemPayFeatureToggles, private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher, private val produceTangemPayInitialDataUseCase: ProduceTangemPayInitialDataUseCase, + private val onboardingRepository: OnboardingRepository, ) : Model(), TangemPayTxHistoryUiActions, TangemPayDetailIntents, AddFundsListener { private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require() @@ -92,6 +96,7 @@ internal class TangemPayDetailsModel @Inject constructor( onOpenMenu = ::onOpenMenu, intents = this, isRedesignEnabled = isRedesignEnabled(), + isRemoveAccountEnabled = tangemPayFeatureToggles.isRemoveAccountEnabled, ) val uiState: StateFlow @@ -374,6 +379,36 @@ internal class TangemPayDetailsModel @Inject constructor( } } + override fun onRemoveAccount() { + uiMessageSender.send( + DialogMessage( + title = resourceReference(R.string.tangempay_remove_account_alert_title), + message = resourceReference(R.string.tangempay_remove_account_alert_description), + firstActionBuilder = { + EventMessageAction( + isWarning = true, + title = resourceReference(R.string.tangempay_remove_account), + onClick = ::removeAccount, + ) + }, + secondActionBuilder = { cancelAction() }, + ), + ) + } + + private fun removeAccount() { + modelScope.launch { + onboardingRepository.disableTangemPay(userWalletId) + .onRight { + paymentAccountStatusFetcher.invoke(userWalletId) + router.pop() + } + .onLeft { + uiMessageSender.send(SnackbarMessage(resourceReference(R.string.common_error))) + } + } + } + private fun showBottomSheetError(type: TangemPayDetailsErrorType) { uiMessageSender.send(message = TangemPayMessagesFactory.createErrorMessage(errorType = type)) } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreenV2.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreenV2.kt index ab96ff04ec..96483e2399 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreenV2.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreenV2.kt @@ -34,8 +34,12 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.ui.components.SpacerW import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshSlidingContainer +import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.components.text.applyBladeBrush import com.tangem.core.ui.components.topFade +import com.tangem.core.ui.ds.button.PrimaryInverseTangemButton +import com.tangem.core.ui.ds.button.TangemButtonShape +import com.tangem.core.ui.ds.button.TangemButtonSize import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.message.TangemMessage import com.tangem.core.ui.ds.message.TangemMessageEffect @@ -43,7 +47,10 @@ import com.tangem.core.ui.ds.topbar.TangemTopBar import com.tangem.core.ui.ds2.button.TangemButton import com.tangem.core.ui.ds2.shimmers.TextShimmer import com.tangem.core.ui.ds2.shimmers.TextShimmerStyle -import com.tangem.core.ui.extensions.* +import com.tangem.core.ui.extensions.orMaskWithStars +import com.tangem.core.ui.extensions.resolveAnnotatedReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.test.TangemPayTestTags @@ -202,22 +209,35 @@ private fun LazyListScope.payDetailsBody(state: TangemPayDetailsUM) { } if (state.accountDeactivatedNotificationConfig != null) { item("deactivationBannerBlock") { - TangemMessage( - modifier = Modifier - .padding(horizontal = TangemTheme.dimens2.x4) - .clickableSingle( - onClick = { state.accountDeactivatedNotificationConfig.onClick?.invoke() }, - ), - title = state.accountDeactivatedNotificationConfig.title, - subtitle = state.accountDeactivatedNotificationConfig.subtitle, - messageEffect = TangemMessageEffect.Warning, - ) + DeactivationBannerBlock(notificationConfig = state.accountDeactivatedNotificationConfig) } } } } } +@Composable +private fun DeactivationBannerBlock(notificationConfig: NotificationConfig) { + val removeAccountButton = notificationConfig.buttonsState as? NotificationConfig.ButtonsState.SecondaryButtonConfig + TangemMessage( + modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x4), + title = notificationConfig.title, + subtitle = notificationConfig.subtitle, + messageEffect = TangemMessageEffect.Warning, + buttons = removeAccountButton?.let { button -> + { + PrimaryInverseTangemButton( + text = button.text, + onClick = button.onClick, + size = TangemButtonSize.X9, + shape = TangemButtonShape.Rounded, + modifier = Modifier.weight(1f), + ) + } + }, + ) +} + @Composable private fun PayDetailsTopBar( config: TangemPayDetailsTopBarConfig, diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/PayContextMenuBlock.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/PayContextMenuBlock.kt index 7e914f42d5..79e46958f5 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/PayContextMenuBlock.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/components/PayContextMenuBlock.kt @@ -71,10 +71,10 @@ private fun PayContextMenuItem(item: TangemPayDropDownItemUM, onMenuDismiss: () Text( text = item.title.resolveReference(), style = TangemTheme.typography3.body.medium, - color = if (item.isEnabled) { - TangemTheme.colors3.text.primary - } else { - TangemTheme.colors3.text.tertiary + color = when { + !item.isEnabled -> TangemTheme.colors3.text.tertiary + item.titleColor != null -> item.titleColor.invoke() + else -> TangemTheme.colors3.text.primary }, maxLines = 1, ) diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/utils/TangemPayDetailIntents.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/utils/TangemPayDetailIntents.kt index c4bd523fcf..7d68426983 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/utils/TangemPayDetailIntents.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/utils/TangemPayDetailIntents.kt @@ -11,4 +11,5 @@ internal interface TangemPayDetailIntents { fun onClickTermsAndLimits() fun onCardClick() fun onAddCardClick() + fun onRemoveAccount() } \ No newline at end of file