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 0000000000..b0b4afb8b6 Binary files /dev/null and b/core/ui/src/main/res/drawable/img_visa_notification.webp differ diff --git a/domain/feedback/models/src/main/kotlin/com/tangem/domain/feedback/models/FeedbackEmailType.kt b/domain/feedback/models/src/main/kotlin/com/tangem/domain/feedback/models/FeedbackEmailType.kt index 5786ee9dac..78848710c5 100644 --- a/domain/feedback/models/src/main/kotlin/com/tangem/domain/feedback/models/FeedbackEmailType.kt +++ b/domain/feedback/models/src/main/kotlin/com/tangem/domain/feedback/models/FeedbackEmailType.kt @@ -78,5 +78,7 @@ sealed interface FeedbackEmailType { val providerName: String, val txId: String, ) : Visa() + + data class FeatureIsBeta(override val walletMetaInfo: WalletMetaInfo) : Visa() } } \ No newline at end of file diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt index fac64d171a..9a3a7de9b6 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt @@ -49,6 +49,10 @@ internal class FeedbackDataBuilder { builder.appendKeyValue("Total saved wallets", userWalletsInfo.totalUserWallets.toString()) } + fun addUserWalletId(userWalletId: String) { + builder.appendKeyValue("User Wallet ID", userWalletId) + } + fun addUserWalletMetaInfo(walletMetaInfo: WalletMetaInfo) { builder.appendKeyValue("Mobile Wallet is backed up", walletMetaInfo.hotWalletIsBackedUp?.toString()) builder.appendKeyValue("Card ID", walletMetaInfo.cardId) diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt index 04e4d5db8f..adc8353252 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/SendFeedbackEmailUseCase.kt @@ -65,6 +65,7 @@ class SendFeedbackEmailUseCase( is FeedbackEmailType.CardAttestationFailed, is FeedbackEmailType.Visa.Dispute, is FeedbackEmailType.Visa.DisputeV2, + is FeedbackEmailType.Visa.FeatureIsBeta, -> 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