diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt index f35a636601..e1dfdea4c1 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt @@ -13,6 +13,7 @@ sealed class Onboarding( class Started : Onboarding("Onboarding", "Onboarding Started") class Finished : Onboarding("Onboarding", "Onboarding Finished") + data object OfflineAttestationFailed : Onboarding(category = "Onboarding", event = "Offline Attestation Failed") sealed class CreateWallet( event: String, diff --git a/app/src/main/java/com/tangem/tap/di/domain/CardLegacyDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/CardLegacyDomainModule.kt index 38adc6ec9e..7be9d95b9a 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/CardLegacyDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/CardLegacyDomainModule.kt @@ -6,6 +6,7 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.tap.domain.scanCard.CardScanningFeatureToggles import com.tangem.tap.domain.scanCard.DefaultScanCardProcessor +import com.tangem.tap.domain.scanCard.LegacyScanProcessor import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -24,7 +25,9 @@ internal object CardLegacyDomainModule { @Provides @Singleton - fun provideScanCardProcessor(): ScanCardProcessor = DefaultScanCardProcessor() + fun provideScanCardProcessor(legacyScanProcessor: LegacyScanProcessor): ScanCardProcessor { + return DefaultScanCardProcessor(legacyScanProcessor = legacyScanProcessor) + } @Provides @Singleton diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt index a19ec7e918..a7592add78 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/DefaultScanCardProcessor.kt @@ -10,7 +10,9 @@ import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store // TODO: Remove this object after feature toggle was removed and use ScanCardUseCase instead -internal class DefaultScanCardProcessor : ScanCardProcessor { +internal class DefaultScanCardProcessor( + private val legacyScanProcessor: LegacyScanProcessor, +) : ScanCardProcessor { private val isNewCardScanningEnabled: Boolean get() = store.inject(DaggerGraphState::cardScanningFeatureToggles).isNewCardScanningEnabled @@ -21,7 +23,10 @@ internal class DefaultScanCardProcessor : ScanCardProcessor { return if (isNewCardScanningEnabled) { UseCaseScanProcessor.scan(cardId, allowsRequestAccessCodeFromRepository) } else { - LegacyScanProcessor.scan(cardId, allowsRequestAccessCodeFromRepository) + legacyScanProcessor.scan( + cardId = cardId, + allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository, + ) } } @@ -38,24 +43,24 @@ internal class DefaultScanCardProcessor : ScanCardProcessor { ) { if (isNewCardScanningEnabled) { UseCaseScanProcessor.scan( - analyticsSource, - cardId, - onProgressStateChange, - onWalletNotCreated, - disclaimerWillShow, - onFailure, - onSuccess, + analyticsSource = analyticsSource, + cardId = cardId, + onProgressStateChange = onProgressStateChange, + onWalletNotCreated = onWalletNotCreated, + disclaimerWillShow = disclaimerWillShow, + onFailure = onFailure, + onSuccess = onSuccess, ) } else { - LegacyScanProcessor.scan( - analyticsSource, - cardId, - onProgressStateChange, - onWalletNotCreated, - disclaimerWillShow, - onCancel, - onFailure, - onSuccess, + legacyScanProcessor.scan( + analyticsSource = analyticsSource, + cardId = cardId, + onProgressStateChange = onProgressStateChange, + onWalletNotCreated = onWalletNotCreated, + disclaimerWillShow = disclaimerWillShow, + onCancel = onCancel, + onFailure = onFailure, + onSuccess = onSuccess, ) } } diff --git a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt index b1810f1745..36b23b9cf2 100644 --- a/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt +++ b/app/src/main/java/com/tangem/tap/domain/scanCard/LegacyScanProcessor.kt @@ -2,13 +2,22 @@ package com.tangem.tap.domain.scanCard import com.tangem.common.CompletionResult import com.tangem.common.core.TangemError +import com.tangem.common.core.TangemSdkError import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.Analytics +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 +import com.tangem.core.decompose.di.GlobalUiMessageSender +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.ui.R +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.toWrappedList +import com.tangem.core.ui.message.DialogMessage +import com.tangem.core.ui.message.EventMessageAction import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull @@ -18,6 +27,8 @@ import com.tangem.domain.common.util.twinsIsTwinned import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.builder.UserWalletIdBuilder +import com.tangem.sdk.extensions.localizedDescriptionRes +import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppDialog @@ -33,12 +44,15 @@ import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* +import javax.inject.Inject +import javax.inject.Singleton -internal object LegacyScanProcessor { +@Singleton +internal class LegacyScanProcessor @Inject constructor( + @GlobalUiMessageSender private val uiMessageSender: UiMessageSender, + private val analyticsEventHandler: AnalyticsEventHandler, +) { suspend fun scan( cardId: String? = null, @@ -72,7 +86,16 @@ internal object LegacyScanProcessor { result .doOnFailure { error -> - onFailure(error) + onScanFailure( + error = error, + onFailure = onFailure, + onCancel = { + mainScope.launch { + onProgressStateChange.invoke(false) + onCancel() + } + }, + ) } .doOnSuccess { scanResponse -> tangemSdkManager.changeDisplayedCardIdNumbersCount(scanResponse) @@ -82,7 +105,6 @@ internal object LegacyScanProcessor { showDisclaimerIfNeed( scanResponse = scanResponse, disclaimerWillShow = disclaimerWillShow, - onFailure = onFailure, nextHandler = { scanResponse2 -> onScanSuccess( scanResponse = scanResponse2, @@ -114,7 +136,6 @@ internal object LegacyScanProcessor { scanResponse: ScanResponse, crossinline disclaimerWillShow: () -> Unit = {}, crossinline nextHandler: suspend (ScanResponse) -> Unit, - crossinline onFailure: suspend (error: TangemError) -> Unit, ) { val disclaimer = scanResponse.card.createDisclaimer() @@ -135,6 +156,49 @@ internal object LegacyScanProcessor { } } + private suspend inline fun onScanFailure( + error: TangemError, + crossinline onFailure: suspend (TangemError) -> Unit, + crossinline onCancel: () -> Job, + ) { + if (error is TangemSdkError.CardVerificationFailed) { + analyticsEventHandler.send(event = Onboarding.OfflineAttestationFailed) + + val resource = error.localizedDescriptionRes() + val resId = resource.resId ?: R.string.common_unknown_error + val resArgs = resource.args.map { it.value } + + uiMessageSender.send( + message = DialogMessage( + message = resourceReference(id = resId, resArgs.toWrappedList()), + title = resourceReference(id = R.string.security_alert_title), + isDismissable = false, + firstActionBuilder = { + EventMessageAction( + title = resourceReference(id = R.string.alert_button_request_support), + onClick = { + mainScope.launch { + onCancel() + + store.inject(DaggerGraphState::sendFeedbackEmailUseCase).invoke( + type = FeedbackEmailType.CardAttestationFailed, + ) + } + }, + ) + }, + secondActionBuilder = { + cancelAction { + mainScope.launch { onCancel() } + } + }, + ), + ) + } else { + onFailure(error) + } + } + @Suppress("LongMethod", "MagicNumber") private suspend inline fun onScanSuccess( scanResponse: ScanResponse, 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 904ee2d1d8..570f0f9b56 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 @@ -60,6 +60,7 @@ class SendFeedbackEmailUseCase( is FeedbackEmailType.ScanningProblem, is FeedbackEmailType.CurrencyDescriptionError, is FeedbackEmailType.PreActivatedWallet, + is FeedbackEmailType.CardAttestationFailed, -> this is FeedbackEmailType.DirectUserRequest, is FeedbackEmailType.RateCanBeBetter, diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt index 2ec928f7ab..364c363194 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt @@ -48,4 +48,8 @@ sealed interface FeedbackEmailType { } data class PreActivatedWallet(override val cardInfo: CardInfo) : FeedbackEmailType + + data object CardAttestationFailed : FeedbackEmailType { + override val cardInfo: CardInfo? = null + } } \ No newline at end of file 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 9983e4bcc4..e531f71d3f 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 @@ -21,12 +21,14 @@ internal class EmailMessageBodyResolver( when (type) { is FeedbackEmailType.DirectUserRequest -> addUserRequestBody(type.cardInfo) is FeedbackEmailType.RateCanBeBetter -> addCardAndPhoneInfo(type.cardInfo) - is FeedbackEmailType.ScanningProblem -> addScanningProblemBody() is FeedbackEmailType.TransactionSendingProblem -> addTransactionSendingProblemBody(type.cardInfo) is FeedbackEmailType.StakingProblem -> addStakingProblemBody(type) is FeedbackEmailType.SwapProblem -> addSwapProblemBody(type) is FeedbackEmailType.CurrencyDescriptionError -> addTokenInfo(type) is FeedbackEmailType.PreActivatedWallet -> addUserRequestBody(type.cardInfo) + is FeedbackEmailType.ScanningProblem, + is FeedbackEmailType.CardAttestationFailed, + -> addPhoneInfoBody() } return build() @@ -50,7 +52,7 @@ internal class EmailMessageBodyResolver( addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo()) } - private fun FeedbackDataBuilder.addScanningProblemBody() { + private fun FeedbackDataBuilder.addPhoneInfoBody() { addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo()) } 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 4d87eecc86..30058b611e 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 @@ -19,6 +19,7 @@ internal class EmailMessageTitleResolver(private val resources: Resources) { return when (type) { is FeedbackEmailType.DirectUserRequest, is FeedbackEmailType.CurrencyDescriptionError, + is FeedbackEmailType.CardAttestationFailed, -> R.string.feedback_preface_support is FeedbackEmailType.RateCanBeBetter -> R.string.feedback_preface_rate_negative is FeedbackEmailType.ScanningProblem -> R.string.feedback_preface_scan_failed 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 cd511d679b..cc2b6c379c 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 @@ -19,20 +19,24 @@ internal class EmailSubjectResolver(private val resources: Resources) { return when (type) { is FeedbackEmailType.DirectUserRequest -> { if (type.cardInfo.isStart2Coin) { - R.string.feedback_subject_support + resources.getStringSafe(R.string.feedback_subject_support) } else { - R.string.feedback_subject_support_tangem + resources.getStringSafe(R.string.feedback_subject_support_tangem) } } - is FeedbackEmailType.RateCanBeBetter -> R.string.feedback_subject_rate_negative - is FeedbackEmailType.ScanningProblem -> R.string.feedback_subject_scan_failed + is FeedbackEmailType.RateCanBeBetter -> resources.getStringSafe(R.string.feedback_subject_rate_negative) + is FeedbackEmailType.ScanningProblem -> resources.getStringSafe(R.string.feedback_subject_scan_failed) is FeedbackEmailType.TransactionSendingProblem, is FeedbackEmailType.StakingProblem, is FeedbackEmailType.SwapProblem, - -> R.string.feedback_subject_tx_failed - is FeedbackEmailType.PreActivatedWallet -> R.string.feedback_subject_pre_activated_wallet - is FeedbackEmailType.CurrencyDescriptionError -> R.string.feedback_token_description_error + -> resources.getStringSafe(R.string.feedback_subject_tx_failed) + is FeedbackEmailType.PreActivatedWallet -> { + resources.getStringSafe(R.string.feedback_subject_pre_activated_wallet) + } + is FeedbackEmailType.CurrencyDescriptionError -> { + resources.getStringSafe(R.string.feedback_token_description_error) + } + FeedbackEmailType.CardAttestationFailed -> "Card attestation failed" } - .let(resources::getStringSafe) } } \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index bbae985165..ca80ab43c0 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -90,7 +90,7 @@ markdownComposeView = "0.5.4" # region Tangem tangemBlockchainSdk = "develop-900" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-420" +tangemCardSdk = "develop-421" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ tangemVico = "2.0.0-alpha.25-tangem17" #tangemVico = "0.0.1" # Keep it! - used for local builds ^