diff --git a/app/src/main/java/com/tangem/tap/di/TangemSdkManagerModule.kt b/app/src/main/java/com/tangem/tap/di/TangemSdkManagerModule.kt index 0487d0fad7..38e66db21b 100644 --- a/app/src/main/java/com/tangem/tap/di/TangemSdkManagerModule.kt +++ b/app/src/main/java/com/tangem/tap/di/TangemSdkManagerModule.kt @@ -1,8 +1,13 @@ package com.tangem.tap.di import android.content.Context +import com.tangem.core.analytics.api.AnalyticsExceptionHandler +import com.tangem.core.decompose.di.GlobalUiMessageSender +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.navigation.finisher.AppFinisher import com.tangem.domain.card.BuildConfig import com.tangem.domain.card.repository.CardSdkConfigRepository +import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles import com.tangem.sdk.api.TangemSdkManager import com.tangem.tap.domain.sdk.impl.DefaultTangemSdkManager @@ -10,6 +15,7 @@ import com.tangem.tap.domain.sdk.impl.MockTangemSdkManager import com.tangem.tap.domain.tasks.visa.TangemPayGenerateAddressAndSignChallengeTask import com.tangem.tap.domain.tasks.visa.VisaCardActivationTask import com.tangem.tap.domain.visa.VisaCardScanHandler +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -30,6 +36,11 @@ internal class TangemSdkManagerModule { visaCardActivationTaskFactory: VisaCardActivationTask.Factory, tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory, onboardingV2FeatureToggles: OnboardingV2FeatureToggles, + @GlobalUiMessageSender uiMessageSender: UiMessageSender, + appFinisher: AppFinisher, + sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, + analyticsExceptionHandler: AnalyticsExceptionHandler, + dispatchers: CoroutineDispatcherProvider, ): TangemSdkManager { return if (BuildConfig.MOCK_DATA_SOURCE) { MockTangemSdkManager(resources = context.resources) @@ -41,6 +52,11 @@ internal class TangemSdkManagerModule { visaCardActivationTaskFactory = visaCardActivationTaskFactory, tangemPayChallengeTaskFactory = tangemPayChallengeTaskFactory, onboardingV2FeatureToggles = onboardingV2FeatureToggles, + uiMessageSender = uiMessageSender, + appFinisher = appFinisher, + sendFeedbackEmailUseCase = sendFeedbackEmailUseCase, + analyticsExceptionHandler = analyticsExceptionHandler, + dispatchers = dispatchers, ) } } diff --git a/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt index ef82b7c01f..20046a427d 100644 --- a/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/sdk/impl/DefaultTangemSdkManager.kt @@ -18,12 +18,21 @@ import com.tangem.common.extensions.hexToBytes import com.tangem.common.services.secure.SecureStorage import com.tangem.common.usersCode.UserCodeRepository import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.api.AnalyticsExceptionHandler +import com.tangem.core.analytics.models.ExceptionAnalyticsEvent +import com.tangem.core.decompose.ui.UiMessageSender +import com.tangem.core.navigation.finisher.AppFinisher import com.tangem.core.res.getStringSafe +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.message.DialogMessage +import com.tangem.core.ui.message.EventMessageAction import com.tangem.crypto.bip39.DefaultMnemonic import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.card.repository.CardSdkConfigRepository +import com.tangem.domain.feedback.SendFeedbackEmailUseCase +import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWalletId @@ -57,13 +66,14 @@ import com.tangem.tap.domain.twins.CreateFirstTwinWalletTask import com.tangem.tap.domain.twins.CreateSecondTwinWalletTask import com.tangem.tap.domain.twins.FinalizeTwinTask import com.tangem.tap.domain.visa.VisaCardScanHandler +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.wallet.R import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlin.coroutines.resume -@Suppress("TooManyFunctions", "LargeClass") +@Suppress("TooManyFunctions", "LargeClass", "LongParameterList") internal class DefaultTangemSdkManager( private val cardSdkConfigRepository: CardSdkConfigRepository, private val resources: Resources, @@ -71,6 +81,11 @@ internal class DefaultTangemSdkManager( private val visaCardActivationTaskFactory: VisaCardActivationTask.Factory, private val tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory, private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles, + private val uiMessageSender: UiMessageSender, + private val appFinisher: AppFinisher, + private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase, + private val analyticsExceptionHandler: AnalyticsExceptionHandler, + dispatchers: CoroutineDispatcherProvider, ) : TangemSdkManager { private val awaitInitializationMutex = Mutex() @@ -99,6 +114,8 @@ internal class DefaultTangemSdkManager( override val userCodeRequestPolicy: UserCodeRequestPolicy get() = tangemSdk.config.userCodeRequestPolicy + private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.io) + override suspend fun checkNeedEnrollBiometrics(awaitInitialization: Boolean): Boolean { return try { needEnrollBiometrics @@ -413,9 +430,16 @@ internal class DefaultTangemSdkManager( break } else { if (attemps++ >= MAX_INITIALIZE_ATTEMPTS) { - error("Can't initialize authentication manager after $MAX_INITIALIZE_ATTEMPTS attempts") + analyticsExceptionHandler.sendException( + ExceptionAnalyticsEvent( + exception = IllegalStateException( + "Can't initialize authentication manager after $MAX_INITIALIZE_ATTEMPTS attempts", + ), + ), + ) + showAlert() } else { - delay(timeMillis = 200) + delay(timeMillis = 400) } } } while (true) @@ -424,6 +448,28 @@ internal class DefaultTangemSdkManager( } } + private fun showAlert() { + uiMessageSender.send( + message = DialogMessage( + message = resourceReference(id = R.string.alert_authentication_error_message), + title = resourceReference(id = R.string.alert_authentication_error_title), + isDismissable = false, + dismissOnFirstAction = false, + firstActionBuilder = { + EventMessageAction( + title = resourceReference(R.string.alert_button_request_support), + onClick = { + coroutineScope.launch { + sendFeedbackEmailUseCase(FeedbackEmailType.BiometricsAuthenticationFailed) + } + }, + ) + }, + secondActionBuilder = { cancelAction(onClick = appFinisher::finish) }, + ), + ) + } + // region Twin-specific override suspend fun createFirstTwinWallet( diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 4175f06e1c..ab28c8f601 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -86,6 +86,8 @@ Sende nur %1$s ( %2$s ) vom %3$s -Netzwerk an diese Adresse. Die Verwendung anderer Token und Netzwerke kann zum Verlust von Geldern führen. Standard Altbestand + Bei der biometrischen Authentifizierung ist ein Fehler aufgetreten. Bitte setzen Sie die Biometrie auf Ihrem Gerät zurück oder kontaktieren Sie den Support. + Authentifizierungsfehler So scannt man Hilfe anfordern Erneut versuchen @@ -299,6 +301,7 @@ Zum Token Verstanden Ausblenden + Halten bis %s Stunde Importieren In Arbeit @@ -374,6 +377,7 @@ Tauschen Tangem Tangem Wallet + Tippen und halten Allgemeine Geschäftsbedingungen Nutzungsbedingungen An @@ -463,6 +467,11 @@ Das Senden von Vermögenswerten in anderen Netzwerken führt zu dauerhaftem Verlust. %s Netzwerk Sende Geld nur mit + Beste Gelegenheiten + Alle Netzwerke + Alle Arten + Meist verwendet + Verdienen Hallo Support-Team, ich habe einen Fehler mit dem Code %s festgestellt. WalletConnect-Fehler Du hast eine Karte oder Ring aus einer anderen Wallet verwendet. Tippe auf die Karte oder Ring, die dieser Wallet zugeordnet ist. @@ -613,6 +622,7 @@ Zuerst die Sicherung abschließen Unvollständig Andere Methoden + Bewahre Deine Wiederherstellungsphrase an einem sicheren Ort auf und halte diese geheim, um Dein Guthaben zu schützen. Richte außerdem einen 8 stelligen Zugangscode für zusätzliche Sicherheit ein. Speicher Deinen Wiederherstellungssatz an einem sicheren Ort und halte diesen stets geheim, um Dein Geld zu schützen. Wiederherstellungs-Phrase Um Deine Wallet mit einem Zugangscode zu sichern, schließe den Sicherungsvorgang ab. @@ -1463,6 +1473,9 @@ Einfrieren Ihre Karte ist eingefroren. Hilfe erhalten + Grund: %s + %s · %s + MCC %s Andere Nicht nutzbar auf gerooteten Geräten Abgeschlossen @@ -1834,7 +1847,18 @@ OK, habe ich verstanden! Echt toll! Aktualisieren + Migration starten + Kopie + Um weiterhin Zugriff auf Deine Gelder zu haben, beginne die Migration gemäß den offiziellen Clore-Richtlinien. + Die Nachrichtensignatur wird für dieses Netzwerk nicht unterstützt. + Nachricht konnte nicht signiert werden. Bitte versuche es erneut. Laut der offiziellen Dokumentation von Clore werden alle Münzen, die vor dem 21. Dezember erhalten wurden, in Clore (ERC-20 Token) migriert; Münzen, die nach diesem Datum erhalten wurden, nicht. Eine Lösung für den Transfer ist in Arbeit — bleibt dran. + Nachricht + Claim-Portal öffnen + Um Deine Clore-Token weiterhin nutzen zu können, musst Du die Token-Migration gemäß den Informationen im Claim Portal durchführen. + Clore-Netzwerkmigration + signieren + Unterschrift Migration des Clore-Netzwerks Du befindest sich derzeit im Demo-Modus Demo-Modus aktiv diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml index c3b8bb8a6a..4e0238e409 100644 --- a/core/res/src/main/res/values-es/strings.xml +++ b/core/res/src/main/res/values-es/strings.xml @@ -86,6 +86,8 @@ Envíe solo %1$s (%2$s) desde redes como %3$s a esta dirección. Usar otros tokens y redes puede resultar en la pérdida de fondos. Por defecto Legacy + Algo salió mal con la biometría. Por favor, intente restablecer la biometría en su dispositivo o contacte con soporte. + Error de inicio de sesión Cómo escanear Solicitar soporte Inténtelo de nuevo @@ -836,7 +838,7 @@ Hace %d minutos Resumen rápido - Noticias relacionadas + Noticias Tokens relacionados Fuentes Manténgase informado diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index d88cdd4e57..7eda943c3f 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -86,6 +86,8 @@ Envoyez uniquement %1$s (%2$s) depuis les réseaux %3$s à cette adresse. L\'utilisation d\'autres jetons et réseaux peut entraîner une perte de fonds. Défaut Héritage + Quelque chose s’est mal passé avec la biométrie. Veuillez réinitialiser la biométrie sur votre appareil ou contacter le support. + Erreur d’authentification Comment scanner Demander de l\'aide Réessayez @@ -1845,7 +1847,17 @@ Ok, compris! Vraiment cool ! Rafraîchir + Lancer la migration + Copier + La signature des messages n\'est pas prise en charge pour ce réseau. + Impossible de signer le message. Veuillez réessayer. Selon la documentation officielle de Clore, toutes les pièces reçues avant le 21 décembre seront migrées vers Clore (token ERC-20) ; les pièces reçues après cette date ne le seront pas. Une solution de transfert arrive — restez à l\'écoute. + Message + Ouvrir le portail des réclamations + Pour continuer à utiliser vos jetons Clore, vous devez effectuer la migration des jetons conformément aux informations fournies sur le portail de réclamation. + Migration du réseau Clore + Signer + Signature Migration du réseau Clore Vous êtes actuellement en mode démo Mode démo actif diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 770eb98276..c773a24141 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -86,6 +86,8 @@ このアドレスには%3$s ネットワークから%1$s (%2$s) のみを送信してください。他のトークンやネットワークを使用すると、資金を失う可能性があります。 デフォルト レガシー + デバイスの生体認証をリセットするか、サポートにお問い合わせください + 認証エラー スキャン方法 サポートをリクエストする もう一度やり直してください @@ -523,7 +525,7 @@ プロバイダー ベストレート FCA警告リスト - 最適な選択 + お得なレート FCA警告リストに掲載されたプロバイダー 最大 %s まで使用可能 %s 以上で利用可能 @@ -724,7 +726,7 @@ データなし マーケット動向 クイックアクション - マーケットから探す + トークンを探す 結果 時価総額10万ドル以下のトークンを見る トークンを表示 @@ -741,8 +743,8 @@ 経験豊富な買い手 時価総額 並べ替え - 上昇率上位 - 下落率上位 + 値上がり + 値下がり トレンド 利息モード ステーキングは暗号資産で報酬を受け取る最も簡単な方法です。 %s @@ -770,7 +772,7 @@ 取引所 経験豊富な買い手 少なくとも100の発信取引を持つネット・バイヤー - 経験豊富な買い手 + アクティブ投資家数 完全希薄化後評価額 現在流通していないものも含め、存在する可能性のあるすべてのコインが流通している場合の暗号資産の理論上の合計価値 完全希薄化後評価額 @@ -895,7 +897,7 @@ 事前申し込み受付中。他にない特別なカードを、いち早く体験しよう。 Tangem Visaカード 利用規約 - 100ドル以上を入金し、30日間保持すると、10ドルを受け取れます。 + $100以上を入金して30日間保有すると、$10を受け取れます Yield Mode キャンペーンに参加しよう! すべてのデバイスを保護するには、単一のアクセスコードを設定してください。 保護する @@ -1011,7 +1013,7 @@ 買付金額は%s以下にしてください 買付金額は少なくとも%sである必要があります この通貨で利用可能なプロバイダーはありません - 最速 + 最短処理 支払う 支払方法 最大 %s まで使用可能 @@ -1598,7 +1600,7 @@ このトークンは、2月%2$s-%3$s の間、%1$s のサービス手数料で別のトークンと交換できます。 Changellyでスワップ、手数料%s 今すぐスワップ - ホットな暗号資産🔥 + 市場トレンド🔥 買付できません 売却できません %sからのスワップは利用できません @@ -1825,7 +1827,18 @@ はい、わかりました! すごくクールだ! リフレッシュ + 移行を開始 + コピー + 資金へのアクセスを維持するため、Cloreの公式ガイドラインに従って移行を開始してください。 + このネットワークではメッセージ署名はサポートされていません + メッセージに署名できません。もう一度お試しください。 Cloreの公式ドキュメントによると、12月21日以前に受け取ったすべてのコインは Clore(ERC-20トークン)へ移行されますが、同日以降に受け取ったコインは移行されません。送金(移行)ソリューションは現在準備中です。続報をお待ちください。 + メッセージ + Claim Portalを開く + Cloreトークンを引き続き使用するには、Claim Portalの案内に従ってトークン移行を完了する必要があります。 + Cloreネットワーク移行 + 署名する + 署名 Cloreネットワークの移行 現在デモモードです デモモードが有効になっています diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index b0e42bdf47..cc50a2188a 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -86,6 +86,8 @@ Отправляйте только %1$s (%2$s) в сети %3$s на этот адрес. Использование другой сети может привести к утрате средств. По умолчанию Устаревший + Произошла ошибка при работе с биометрией. Пожалуйста, попробуйте обновить биометрию на вашем устройстве или обратитесь в службу поддержки. + Ошибка аутентификации Как сканировать Обратиться в поддержку Попробовать снова @@ -476,6 +478,9 @@ Отправка средств в другой сети может повлечь потерю средств. %s сеть Отправляйте средства, используя только + Все сети + Все типы + Часто используемые Привет, команда поддержки, у меня возникла ошибка с кодом: %s Ошибка WalletConnect Вы использовали карту или кольцо от другого кошелька. Приложите карту или кольцо, связанную с этим кошельком. @@ -850,7 +855,7 @@ %dмин назад Резюме - Связанные новости + Новости Связанные токены Связанные новости Будьте в курсе diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index 67360c1ce3..4bb916c300 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -86,6 +86,8 @@ Надсилайте тільки %1$s (%2$s) в мережі %3$s на цю адресу. Використання іншої мережі може призвести до втрати коштів. За замовчуванням Застарілий + Сталася помилка під час роботи з біометрією. Будь ласка, спробуйте оновити біометрію на вашому пристрої або зверніться до служби підтримки. + Помилка автентифікації Як сканувати Звернутися в підтримку Спробуйте ще раз diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index ed8c132108..8916a5a857 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -86,7 +86,7 @@ Send only %1$s (%2$s) from %3$s network to this address. Using other tokens and networks may result in loss of funds. Default Legacy - Try to reset biometrics on your device or contact support + Something went wrong with biometrics. Please try resetting biometrics on your device or contact support. Authentication error How to scan Request support @@ -468,9 +468,13 @@ %s network Send funds using only Best opportunities + Clear filter All networks All types + Filter by + Networks Mostly used + No results Earn Hi support team, I\'ve encountered an error with code: %s WalletConnect error @@ -839,7 +843,7 @@ %d minutes ago Quick recap - Related News + News Related tokens Related news Stay in the loop 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 520f49e395..e52c876c09 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 @@ -56,6 +56,10 @@ sealed interface FeedbackEmailType { override val walletMetaInfo: WalletMetaInfo? = null } + data object BiometricsAuthenticationFailed : FeedbackEmailType { + override val walletMetaInfo: WalletMetaInfo? = null + } + sealed class Visa : FeedbackEmailType { data class DirectUserRequest(override val walletMetaInfo: WalletMetaInfo) : Visa() 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 07e0eb137e..3289cba524 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 @@ -92,6 +92,7 @@ class SendFeedbackEmailUseCase( is FeedbackEmailType.CurrencyDescriptionError, is FeedbackEmailType.PreActivatedWallet, is FeedbackEmailType.CardAttestationFailed, + is FeedbackEmailType.BiometricsAuthenticationFailed, is FeedbackEmailType.Visa.Dispute, is FeedbackEmailType.Visa.DisputeV2, is FeedbackEmailType.Visa.FeatureIsBeta, 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 1e4ca44bd2..4533c06c96 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 @@ -30,6 +30,7 @@ internal class EmailMessageBodyResolver( is FeedbackEmailType.PreActivatedWallet -> addUserRequestBody(type.walletMetaInfo) is FeedbackEmailType.ScanningProblem, is FeedbackEmailType.CardAttestationFailed, + is FeedbackEmailType.BiometricsAuthenticationFailed, -> addPhoneInfoBody() is FeedbackEmailType.Visa.Activation -> addUserRequestBody(type.walletMetaInfo) is FeedbackEmailType.Visa.DirectUserRequest -> addUserRequestBody(type.walletMetaInfo) 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 36940c948e..852e933644 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 @@ -34,6 +34,7 @@ internal class EmailMessageTitleResolver(private val resources: Resources) { is FeedbackEmailType.TransactionSendingProblem, is FeedbackEmailType.StakingProblem, is FeedbackEmailType.SwapProblem, + is FeedbackEmailType.BiometricsAuthenticationFailed, -> R.string.feedback_preface_tx_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 4b1e4f9799..39f92cf852 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 @@ -38,6 +38,7 @@ internal class EmailSubjectResolver(private val resources: Resources) { resources.getStringSafe(R.string.feedback_token_description_error) } FeedbackEmailType.CardAttestationFailed -> "Card attestation failed" + FeedbackEmailType.BiometricsAuthenticationFailed -> "Biometrics authentication failed" is FeedbackEmailType.Visa.Activation -> "[Visa] [Activation] {auto-filled subject}" is FeedbackEmailType.Visa.DirectUserRequest -> "[Visa] {auto-filled subject}" is FeedbackEmailType.Visa.FailedIssueCard -> "[Visa] {auto-filled subject}"