From e2f9d50499a4c0900fd2d658b398434dc1962846 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 9 Dec 2025 20:50:03 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../com/tangem/screens/DetailsPageObject.kt | 2 +- core/res/src/main/res/values/strings.xml | 3 + .../res/drawable/img_visa_label_26_16.xml | 17 ------ .../res/drawable/img_visa_notification.webp | Bin 0 -> 2500 bytes .../feedback/models/FeedbackEmailType.kt | 2 + .../domain/feedback/FeedbackDataBuilder.kt | 4 ++ .../feedback/SendFeedbackEmailUseCase.kt | 1 + .../utils/EmailMessageBodyResolver.kt | 10 ++++ .../utils/EmailMessageTitleResolver.kt | 3 +- .../feedback/utils/EmailSubjectResolver.kt | 5 +- .../features/details/utils/ItemsBuilder.kt | 2 +- .../entity/TangemPayDetailsStateFactory.kt | 13 +++++ .../tangempay/entity/TangemPayDetailsUM.kt | 2 + .../tangempay/model/TangemPayDetailsModel.kt | 16 +++++- .../tangempay/ui/TangemPayDetailsScreen.kt | 52 +++++++++++++++--- .../tangempay/utils/TangemPayDetailIntents.kt | 1 + .../model/intents/TangemPayClickIntents.kt | 2 +- .../wallet/state/model/WalletNotification.kt | 2 +- 18 files changed, 105 insertions(+), 32 deletions(-) delete mode 100644 core/ui/src/main/res/drawable/img_visa_label_26_16.xml create mode 100644 core/ui/src/main/res/drawable/img_visa_notification.webp diff --git a/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt b/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt index 62b73d6c4b..863e387a71 100644 --- a/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt +++ b/app/src/androidTest/kotlin/com/tangem/screens/DetailsPageObject.kt @@ -44,7 +44,7 @@ class DetailsPageObject(semanticsProvider: SemanticsNodeInteractionsProvider) : } val contactSupportButton: KNode = child { hasTestTag(DetailsScreenTestTags.SCREEN_ITEM) - hasText(getResourceString(R.string.details_row_title_contact_to_support)) + hasText(getResourceString(R.string.common_contact_support)) } val toSButton: KNode = child { hasTestTag(DetailsScreenTestTags.SCREEN_ITEM) diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index deea678229..7f0604d2d7 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -244,6 +244,7 @@ Coming soon Confirm Connecting + Contact support Contact Tangem Support Contact Visa Support Continue @@ -1382,6 +1383,8 @@ You receive Choose token not available + We would be happy to receive your feedback + Tangem Pay is now in beta Card frozen Card payment Deposit diff --git a/core/ui/src/main/res/drawable/img_visa_label_26_16.xml b/core/ui/src/main/res/drawable/img_visa_label_26_16.xml deleted file mode 100644 index c5859a2fda..0000000000 --- a/core/ui/src/main/res/drawable/img_visa_label_26_16.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - diff --git a/core/ui/src/main/res/drawable/img_visa_notification.webp b/core/ui/src/main/res/drawable/img_visa_notification.webp new file mode 100644 index 0000000000000000000000000000000000000000..b0b4afb8b65e4b4c7c19b83757fff925a66d1564 GIT binary patch literal 2500 zcmV;#2|M;uNk&Gz2><|BMM6+kP&il$0000G0001w0055w06|PpNIU}o00E$0Yj4~} z!y&1mO`C_32Qp1mkPg(H-9S=E3W*DusX!IX;tiqze#@_jm;hL{=|UV1W6pFpXT%`} zZ$M)c5*A`Fm^Mxmvq_mM9EXij!R(PL#^8iATcqkS2 zkpx61hdYpvgA*bak~2!+Gl`nr+DsyG3o7;4+Dx zU3Fa{;ZI#Fq>JhC{_{Ny+W)#$+BNO}JnYKJd*Qs2LV zY;PaepT7j1ua}7pU+}~{U4IGuIWducZeY61zeJudO!$-|-j82mfAEldX5hK}68ypw zpUEuKEZID%44+m_he3@wpvjxCV^z)?$(0ob-h07z|-0I)3(ptQva47CLr-x^(LjSyQS z_N~!lYaE(m%&n2zgIZ(3|8M2i$n9}#4gB65eQWfwHDYLu@U1nxX$=FVElvQ~76_2q zA_0W9*Z`QeumF%-Vgvw4Ez#d>TViu#TEg4_^8A!;=0Ui-=ebJv#S`uso_FcaJoX+S z%bRo)3&O)K3teUVB-ZfA6qae0Y@S#$JQ-Q&EZLb@`)4ch6kkrGwgSmMvST-yS6!Evz!hm0+#=B2(GHdTo|y zw6N+ZPcpD_n`Ox!1dM`|2r}AUih~6)ElPkMMD@5f^dPS9*MuHKwtZeli5BDzSRZZ- z$ZiAHgnI)Tnr(lqA*ZbYMeR0W;VYPe0ga6haX`%ZUe3qE5JP8R)mBhAAX)|h05Bi` zodGJ40FVGakwBeFrK7*0rdJ9(@Dd4XZs5SJd;$0Yo&f>@_yK4XXZ-89bB=az%Km)q z_3#Hsd8+hx;dQj{X_kK|Ng)aK;PCs zRiFEw#J{^=yT1T`uOI*a|MmcNMrZx@Wi`OPAWYtv;`q(WlkAH`SdGx=qlw;~lgU4U zOQZU_QIzI{#MtmFix*FmP^cCQ8QmdUm; zT0hetp5o-KBA2iSZVgMwRXqyb%BPT6`M~~;WCh6$=5{xXUI#aW_pn=Xjjr_HNFKEm z?Ct;n{@h4M{>niQ00lsu4?VB=k;@OSq$xycwX8} zToUdk3JbMXFnbP04aX9MOI&Av;}00M)MqObpY7mu(oA`Gt53(HTI3a_tA#j7+RM;5 zKFMNlYr4b--A@Ex{=J}wCv;+652pv$kHnc`_D2ja^gQ8Q30pJ&S30b(%Q1Z9xPnjn zpNZqVK>UIw?f#27yO2ls3b1lf8~_8~l@$BCKtsYOlMY8=I({%h=*T`^mKRL3(8^YwpeY_J7q>y&LgT&?=nj?k6WA7oQp51d;=Ha+=cBWk-~If^*kmh@!z+ z4_LwktU8?cNAU*KGD;cN`|r9{ zUp!DPLXy$_BYLm{Gy8B$mCKmS3oKi_r(e0y3}?j+Bi_99PGlcF$L4bTZfjXhKSD`< zz#mVJt1@D<18FmPcxHERs0$EUk9Ik$tILtfqhmNMImsvyg`!#%@i!xJE~c278p#dV z`l;^ek;}^745j(YiuFc%)N4!`RB;SdZwLTmgcO@E6d=DJzZAge)t>%h!nr%;n3Z;! z8}%52l27f$<2}&pMEthFUBqe^p{QhsR>6!s2=W_!r6zMj>%G1k?`HJ+RP2tNXH1wx zlQwzLt>kAJufC+Q_bT!_2z<*`H{%9kV+iV&7G>t+?1-#8xKMkH-@(1)08p~M zTW$tBQ|0^MjEi4W0rXMXHUXgu3vgow%bi-P90KhgMW?@i(u&O382x`D!Ob#q)6sC>Ui_%?J>k|$K!YGJ_(MvG^ne3< z$501hY(brv!0xo1+;ys}d<|8sKi>D_?3MgmYc;YR< this is FeedbackEmailType.DirectUserRequest, is FeedbackEmailType.RateCanBeBetter, diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt index 12bd80d66a..c8a13a4ef7 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt @@ -38,6 +38,7 @@ internal class EmailMessageBodyResolver( is FeedbackEmailType.Visa.Dispute -> addVisaRequestBody(type.walletMetaInfo, type.visaTxDetails) is FeedbackEmailType.Visa.DisputeV2 -> addTangemPayRequestBody(type.walletMetaInfo, type.item) is FeedbackEmailType.Visa.Withdrawal -> addTangemPayWithdrawalRequestBody(type) + is FeedbackEmailType.Visa.FeatureIsBeta -> addTangemPayBetaRequestBody(type.walletMetaInfo) } return build() @@ -52,6 +53,15 @@ internal class EmailMessageBodyResolver( addTangemPayTxInfo(item) } + private fun FeedbackDataBuilder.addTangemPayBetaRequestBody(walletMetaInfo: WalletMetaInfo) { + addPhoneInfoBody() + addDelimiter() + walletMetaInfo.userWalletId?.let { userWalletId -> + addUserWalletId(userWalletId = userWalletId.stringValue) + addDelimiter() + } + } + private suspend fun FeedbackDataBuilder.addTangemPayWithdrawalRequestBody( type: FeedbackEmailType.Visa.Withdrawal, ) { diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt index 7d42928a99..36940c948e 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt @@ -26,6 +26,8 @@ internal class EmailMessageTitleResolver(private val resources: Resources) { is FeedbackEmailType.Visa.DisputeV2, is FeedbackEmailType.Visa.FailedIssueCard, is FeedbackEmailType.Visa.Withdrawal, + is FeedbackEmailType.Visa.FeatureIsBeta, + is FeedbackEmailType.PreActivatedWallet, -> R.string.feedback_preface_support is FeedbackEmailType.RateCanBeBetter -> R.string.feedback_preface_rate_negative is FeedbackEmailType.ScanningProblem -> R.string.feedback_preface_scan_failed @@ -33,7 +35,6 @@ internal class EmailMessageTitleResolver(private val resources: Resources) { is FeedbackEmailType.StakingProblem, is FeedbackEmailType.SwapProblem, -> R.string.feedback_preface_tx_failed - is FeedbackEmailType.PreActivatedWallet -> R.string.feedback_preface_support } return resources.getStringSafe(resId) diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt index 6e01c9989f..4b1e4f9799 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt @@ -15,6 +15,7 @@ import com.tangem.domain.feedback.models.FeedbackEmailType internal class EmailSubjectResolver(private val resources: Resources) { /** Resolve email message body by [type] */ + @Suppress("CyclomaticComplexMethod") fun resolve(type: FeedbackEmailType): String { return when (type) { is FeedbackEmailType.DirectUserRequest -> { @@ -43,8 +44,8 @@ internal class EmailSubjectResolver(private val resources: Resources) { is FeedbackEmailType.Visa.Dispute, is FeedbackEmailType.Visa.DisputeV2, -> "[Visa] [DISPUTE] {auto-filled subject}" - is FeedbackEmailType.Visa.Withdrawal, - -> "[Visa] [WITHDRAWAL] {auto-filled subject}" + is FeedbackEmailType.Visa.Withdrawal -> "[Visa] [WITHDRAWAL] {auto-filled subject}" + is FeedbackEmailType.Visa.FeatureIsBeta -> "[VISA] [FEEDBACK]" } } } \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt index ebabf476c2..617de52a47 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt @@ -87,7 +87,7 @@ internal class ItemsBuilder @Inject constructor(private val router: Router) { DetailsItemUM.Basic.Item( id = "support_email", block = BlockUM( - text = resourceReference(R.string.details_row_title_contact_to_support), + text = resourceReference(R.string.common_contact_support), iconRes = R.drawable.ic_comment_24, onClick = onSupportEmailClick, ), 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 0036f7c5ba..9853165dad 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 @@ -1,8 +1,10 @@ package com.tangem.features.tangempay.entity +import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem +import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.themedColor import com.tangem.core.ui.res.TangemTheme @@ -21,6 +23,7 @@ internal class TangemPayDetailsStateFactory( private val converter: TangemPayCardFrozenStateConverter, ) { + @Suppress("LongMethod") fun getInitialState(): TangemPayDetailsUM { val cardFrozenStateItem = when (cardFrozenState) { is TangemPayCardFrozenState.Pending -> null @@ -88,6 +91,16 @@ internal class TangemPayDetailsStateFactory( isBalanceHidden = false, addFundsEnabled = true, cardFrozenState = converter.convert(cardFrozenState), + betaNotificationConfig = NotificationConfig( + title = resourceReference(R.string.tangem_pay_beta_notification_title), + subtitle = resourceReference(R.string.tangem_pay_beta_notification_subtitle), + iconResId = R.drawable.img_visa_notification, + buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( + text = resourceReference(R.string.common_contact_support), + onClick = intents::onContactSupportClicked, + ), + iconSize = 36.dp, + ), ) } } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt index 6faa4a89ea..a8b31086f1 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsUM.kt @@ -2,6 +2,7 @@ package com.tangem.features.tangempay.entity import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig +import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.visa.model.TangemPayCardFrozenState import kotlinx.collections.immutable.ImmutableList @@ -14,6 +15,7 @@ internal data class TangemPayDetailsUM( val isBalanceHidden: Boolean, val addFundsEnabled: Boolean, val cardFrozenState: CardFrozenState, + val betaNotificationConfig: NotificationConfig, ) internal data class TangemPayCardDetailsUM( 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 3a08e43adf..78c4c843c6 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 @@ -17,10 +17,13 @@ import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfi import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.message.SnackbarMessage import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase +import com.tangem.domain.feedback.SendFeedbackEmailUseCase +import com.tangem.domain.feedback.models.FeedbackEmailType +import com.tangem.domain.feedback.models.WalletMetaInfo import com.tangem.domain.models.TokenReceiveConfig import com.tangem.domain.pay.TangemPayCryptoCurrencyFactory -import com.tangem.domain.pay.model.TangemPayTopUpData import com.tangem.domain.pay.model.TangemPayCardBalance +import com.tangem.domain.pay.model.TangemPayTopUpData import com.tangem.domain.pay.repository.CustomerOrderRepository import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository import com.tangem.domain.tangempay.TangemPayAnalyticsEvents @@ -73,6 +76,7 @@ internal class TangemPayDetailsModel @Inject constructor( private val tangemPayCryptoCurrencyFactory: TangemPayCryptoCurrencyFactory, private val orderRepository: CustomerOrderRepository, private val getUserWalletUseCase: GetUserWalletUseCase, + private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, ) : Model(), TangemPayTxHistoryUiActions, TangemPayDetailIntents, AddFundsListener { private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require() @@ -306,6 +310,16 @@ internal class TangemPayDetailsModel @Inject constructor( }.saveIn(addToWalletBannerJobHolder) } + override fun onContactSupportClicked() { + modelScope.launch { + sendFeedbackEmailUseCase.invoke( + type = FeedbackEmailType.Visa.FeatureIsBeta( + walletMetaInfo = WalletMetaInfo(userWalletId = params.userWalletId), + ), + ) + } + } + override fun onRefreshSwipe(refreshState: ShowRefreshState) { modelScope.launch { uiState.update(TangemPayDetailsRefreshTransformer(isRefreshing = refreshState.value)) diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt index 6c0a7fdf77..6addbcf3d6 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/ui/TangemPayDetailsScreen.kt @@ -31,11 +31,10 @@ import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfi import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshContainer import com.tangem.core.ui.components.dropdownmenu.TangemDropdownItem import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenu +import com.tangem.core.ui.components.notifications.Notification +import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.components.text.applyBladeBrush -import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.orMaskWithStars -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.test.TokenDetailsTopBarTestTags @@ -81,7 +80,9 @@ internal fun TangemPayDetailsScreen( ) { item(TangemPayCardDetailsUM::class.java) { cardDetailsBlockComponent.CardDetailsBlockContent( - modifier = Modifier.padding(horizontal = 16.dp).padding(top = 8.dp), + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(top = 8.dp), state = cardDetailsState, ) } @@ -116,7 +117,7 @@ internal fun TangemPayDetailsScreen( key = TangemPayDetailsBalanceBlockState::class.java, content = { TangemPayDetailsBalanceBlock( - modifier = modifier + modifier = Modifier .padding(horizontal = TangemTheme.dimens.spacing16) .padding(top = 12.dp) .fillMaxWidth(), @@ -125,15 +126,32 @@ internal fun TangemPayDetailsScreen( ) }, ) + item( + key = "TANGEM_PAY_IS_IN_BETA", + content = { + TangemPayBetaBlock( + modifier = modifier + .padding(horizontal = TangemTheme.dimens.spacing16) + .padding(top = 12.dp) + .fillMaxWidth(), + config = state.betaNotificationConfig, + ) + }, + ) with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryState) } } } } } +@Composable +private fun TangemPayBetaBlock(config: NotificationConfig, modifier: Modifier = Modifier) { + Notification(modifier = modifier, config = config) +} + // region Balance block @Composable -internal fun TangemPayDetailsBalanceBlock( +private fun TangemPayDetailsBalanceBlock( state: TangemPayDetailsBalanceBlockState, isBalanceHidden: Boolean, modifier: Modifier = Modifier, @@ -335,6 +353,16 @@ private class TangemPayDetailsUMProvider : CollectionPreviewParameterProvider