diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt
index dc9e77a48d..58d13a9b0e 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt
@@ -4,6 +4,7 @@ import android.net.Uri
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.common.CompletionResult
import com.tangem.common.card.Card
+import com.tangem.common.core.TangemError
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.ifNotNull
import com.tangem.common.extensions.toHexString
@@ -226,7 +227,7 @@ private fun navigateToWalletScreen() {
}
}
-private suspend fun readCard(onSuccess: suspend (ScanResponse) -> Unit) {
+private suspend fun readCard(onSuccess: suspend (ScanResponse) -> Unit, onFailure: (TangemError) -> Unit) {
val shouldSaveAccessCodes = store.inject(DaggerGraphState::settingsRepository).shouldSaveAccessCodes()
store.inject(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
@@ -247,6 +248,7 @@ private suspend fun readCard(onSuccess: suspend (ScanResponse) -> Unit) {
Timber.e(it, "Unable to scan card")
delay(HIDE_PROGRESS_DELAY)
store.dispatch(HomeAction.ScanInProgress(scanInProgress = false))
+ onFailure(it)
},
onSuccess = onSuccess,
)
@@ -605,9 +607,14 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
}
if (scanResponse == null) {
scope.launch {
- readCard { newScanResponse ->
- handleFinishBackup(newScanResponse)
- }
+ readCard(
+ onSuccess = { newScanResponse ->
+ handleFinishBackup(newScanResponse)
+ },
+ onFailure = {
+ store.dispatchNavigationAction(AppRouter::pop)
+ },
+ )
}
} else {
handleFinishBackup(scanResponse)
@@ -643,10 +650,15 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
} else {
delay(HIDE_PROGRESS_DELAY)
- readCard { newScanResponse ->
- scanResponse = newScanResponse
- userWallet = createUserWallet(newScanResponse, backupState)
- }
+ readCard(
+ onSuccess = { newScanResponse ->
+ scanResponse = newScanResponse
+ userWallet = createUserWallet(newScanResponse, backupState)
+ },
+ onFailure = {
+ store.dispatchNavigationAction(AppRouter::pop)
+ },
+ )
}
val notActivatedCardIds = gatherCardIds(backupState, card).mapNotNull {
diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml
index 65a4990969..d8c34ccdc5 100644
--- a/core/res/src/main/res/values-de/strings.xml
+++ b/core/res/src/main/res/values-de/strings.xml
@@ -180,6 +180,7 @@
NutzungsbedingungenHeuteTransaktion fehlgeschlagen
+ TransaktionsstatusTransaktionenÜberweisungIch verstehe
@@ -187,6 +188,7 @@
Nicht erreichbarstaking beendenwoche
+ mitJaVertragsadresse kopiert!Verfügbare Netzwerke
@@ -292,6 +294,9 @@
Für dieses Paar nicht verfügbarErlaubnis erforderlichEmpfohlen
+ Gekauft %s
+ Kauf %s
+ Kauf %s...Keine Token gefunden. Bitte versuche eine andere AnfrageID: %sTransaktions-ID kopiert
@@ -568,13 +573,14 @@
Keine verfügbaren Anbieter für diese WährungBezahlen mitDu kannst Deine Transaktion beim Drittanbieter %s abschließen.
- Umleitung auf %s …
+ Umleitung auf %s…Unsere Dienstleistungen sind in diesem Land nicht verfügbarÄnder oder bestätige bitteDein Wohnsitz wurde identifiziert alsResidenzBitte wähle das richtige Land aus, um korrekte Zahlungsoptionen und Dienstleistungen zu gewährleisten.Einstellungen
+ Du kannst den Transaktionsstatus auf der Token-Seite überprüfen.ÜberGruppe erstellenNach Guthaben
@@ -853,14 +859,14 @@
Angezeigte SaldenRückgängig machenDieser Vorgang ist derzeit nicht verfügbar. Bitte versuche es später noch einmal.
- Der Kauf von %s ist im Moment nicht verfügbar. Bitte prüfe später ob es Updates gibt.
+ Der Kauf von %s wird von aktuellen Anbietern nicht unterstützt, aber wir arbeiten daran, weitere Optionen hinzuzufügen.Du hast kein Guthaben um dies zu Verkaufen. Lade dein Konto auf, um Geld von dort zu verkaufen.Du hast kein Guthaben zum Versenden. Lade dein Konto auf, um Geld von dort aus senden zu können.
- Swappen %s ist im Moment nicht verfügbar. Bitte prüfe später ob es Updates gibt.
+ Das Tauschen von %s wird von aktuellen Anbietern nicht unterstützt, aber wir arbeiten daran, weitere Optionen hinzuzufügen.Das Geld für den Verkauf steht zur Verfügung, sobald die ausstehende(n) Transaktion(en) im Netzwerk %s abgeschlossen ist/sind.Die Geldsendung wird verfügbar, sobald die ausstehende(n) Transaktion(en) im Netzwerk %s abgeschlossen ist/sind.
- Der Verkauf von %s ist im Moment nicht verfügbar. Bitte prüfe später ob es Updates gibt.
- Staking %s ist aktuell nicht verfügbar. Prüfe bitte ob es ein neues Update gibt.
+ Der Verkauf von %s wird von aktuellen Anbietern nicht unterstützt, aber wir arbeiten daran, weitere Optionen hinzuzufügen.
+ Staking %s wird von aktuellen Anbietern nicht unterstützt, aber wir arbeiten daran, weitere Optionen hinzuzufügen.Generiere XPUBAusblendenDu bist dabei, dieses Token vom Hauptbildschirm auszublenden. Du kannst es jederzeit über die Seite „Token verwalten“ wieder hinzufügen.
@@ -953,6 +959,9 @@
%s NetzwerkDie Adresse wurde erfolgreich kopiertKeine Internetverbindung
+ Jetzt mit 10 %% Rabatt kaufen
+ Greife auf 13.000+ Kryptowährungen zu. Kaufe, verkaufe, tausche und stake Sie mit einem einzigen Fingertipp.\nVerbinde bis zu drei Karten für ein Backup.
+ Entdecke die Tangem WalletWallet-EinstellungenTangemVerwende %s oder scanne eine Karte oder Ring, um den Zugriff auf deine Wallet freizuschalten.
@@ -998,6 +1007,9 @@
Dieses Token muss mit deinem Hedera-Konto verknüpft sein, bevor du ihn erhalten kannst.Verknüpfe deinen TokenNicht genug %s. Lade dein Hedera-Konto auf, um dieses Token zuzuordnen
+ Bist Du sicher, dass Du die Transaktion stornieren willst? Du kannst die Transaktion nicht erneut starten.
+ Deine Transaktion mit einem Betrag von %1$s %2$s wurde nicht abgeschlossen. Du kannst versuchen, diese erneut abzuschließen.
+ Du hast eine nicht abgeschlossene TransaktionAuf dieser Karte oder Ring sind nur noch %s Unterschriften übrig. Du musst dein gesamtes Guthaben abheben.Geringe Anzahl von UnterschriftenToken in verschiedenen Netzwerken können unterschiedliche Adressen haben. Überprüfe bei der Überweisung noch einmal, ob deine Adresse mit der des Netzwerks übereinstimmt.
diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml
index ee04d97a2f..b77f4a6456 100644
--- a/core/res/src/main/res/values-es/strings.xml
+++ b/core/res/src/main/res/values-es/strings.xml
@@ -858,14 +858,14 @@
Saldos mostradosCancelarEsta operación no está disponible actualmente. Por favor, inténtalo de nuevo más tarde.
- Comprar %s no está disponible en este momento. Por favor revise sus actualizaciones.
+ Comprar %s no está disponible con los proveedores actuales pero estamos trabajando para agregar más opciones.No tiene fondos para vender. Recargue su cuenta para poder vender fondos desde ella.No tienes fondos para enviar. Recarga tu cuenta para poder enviar fondos desde ella.
- El servicio de intercambio %s no está disponible en este momento. Por favor consulte nuestras actualizaciones.
+ El servicio de intercambio %s no está compatible con los proveedores actuales pero estamos trabajando para agregar más opciones.La venta de fondos estará disponible una vez que la(s) transacción(es) pendiente(s) en la red %s se completeEl envío de fondos estará disponible una vez que se completen las transacciones pendientes en la red %s.
- La venta de %s no está disponible en este momento. Por favor consulte nuestras actualizaciones.
- El staking %s no está disponible en este momento. Por favor consulte nuestras actualizaciones.
+ Vender %s no es compatible con los proveedores actuales, pero estamos trabajando para agregar más opciones.
+ El staking de %s no es compatible con los proveedores actuales, pero estamos trabajando para agregar más opciones.Generar XPUBOcultarEstá a punto de ocultar este token de la pantalla principal. Puede volver a agregarlo en cualquier momento a través de la página de gestión de tokens.
diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml
index c9983efdb7..346d3081e5 100644
--- a/core/res/src/main/res/values-fr/strings.xml
+++ b/core/res/src/main/res/values-fr/strings.xml
@@ -850,14 +850,14 @@
Soldes affichésAnnulerCette opération est actuellement indisponible. Veuillez réessayer plus tard.
- L\'achat de %s n\'est pas disponible pour le moment. Veuillez vérifier vos mises à jour.
+ L\'achat de %s n\'est pas pris en charge par les fournisseurs actuels mais nous travaillons à ajouter davantage d\'options.Vous n\'avez pas de fonds à vendre. Renflouez votre compte pour pouvoir vendre des fonds à partir de celui-ci.Vous n\'avez pas de fonds à envoyer. Renflouez votre compte pour pouvoir envoyer des fonds à partir de celui-ci.
- Le service d\'échange %s n\'est pas disponible pour le moment. Veuillez consulter nos mises à jour.
+ Le service d\'échange %s n\'est pas pris en charge par les fournisseurs actuels mais nous travaillons à ajouter plus d\'options.La vente de fonds sera disponible une fois que la ou les transactions en attente dans le réseau %s seront terminéesL\'envoi de fonds sera disponible une fois la ou les transactions en attente dans le réseau %s terminées.
- La vente de %s n\'est pas disponible pour le moment. Veuillez consulter nos mises à jour.
- Le staking %s n’est pas disponible pour le moment. Veuillez consulter nos mises à jour.
+ L\'achat de %s n\'est pas pris en charge par les fournisseurs actuels mais nous travaillons à ajouter plus d\'options.
+ Le staking de %s n\'est pas pris en charge par les fournisseurs actuels, mais nous travaillons à ajouter plus d\'options.Générer XPUBMasquerVous êtes sur le point de masquer ce jeton de l\'écran principal. Vous pouvez le rajouter à tout moment via la page de gestion des jetons.
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index e32e1a6fcb..0c5037ec1c 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -291,6 +291,9 @@
このペアは利用できません許可が必要です推奨
+ %sを買い付けました
+ %sを買い付けています
+ %sを買い付けています...トークンが見つかりません。別のリクエストをお試しください。ID: %s取引IDをコピーしました
@@ -830,7 +833,7 @@
金額には以下が含まれます: \n • サービス プロバイダーの手数料\n • 取引所からユーザーのアドレスに%1$sを送金するためのネットワーク手数料。 \n\nプロバイダーのスリッページは最大%2$sですこの金額には、サービスプロバイダーの手数料が含まれています。金額にはサービスプロバイダーの手数料が含まれます。 \n\nプロバイダーのスリッページは最大%s です
- 手数料
+ 情報すべての分散型取引所は、スマートコントラクトがあなたの許可なくウォレットにアクセスするのを防ぐために承認を必要とします。設計上、スマートコントラクトは承認なしでトークンにアクセスできません。トークンを「ロック解除」することで、あなたは1-inchのスマートコントラクトがトークンを使うことを承認します。ネットワークのマイナーは、このアクションをブロックチェーンに記録するためのガス料金(あなたが支払う)を受け取ります。承認後、トークンを交換することができます。承認手数料見積りエラーです。サポートにフィードバックをお送りください。
@@ -846,14 +849,14 @@
残高表示元に戻すこの操作は現在利用できません。しばらくしてからもう一度お試しください。
- 現在、 %sの買付はご利用いただけません。アップデート情報をご確認ください。
+ %sの買付は、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます。売却できる資金がありません。アカウントに入金して、売却できるようにしてください。送金する資金がありません。アカウントに入金して、送金できるようにしてください。
- 現在、 %sの交換はご利用いただけません。アップデート情報を確認してください。
+ %sのスワップは、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます。ネットワーク%s内の保留中の取引が完了すると、資金の売却が可能になります。ネットワーク%s内の保留中の取引が完了すると、送金が可能になります。
- 現在、 %sの売却はご利用いただけません。アップデート情報をご確認ください。
- %s のステーキングは現在ご利用いただけません。最新情報をご確認ください。
+ %sの売却は、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます。
+ %sのステーキングは、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます。XPUBを生成する非表示このトークンをメイン画面から非表示にします。トークンの管理ページからいつでも再度追加できます。
@@ -946,6 +949,7 @@
%sネットワークアドレスがクリップボードにコピーされましたインターネット接続がありません
+ 今すぐ10 %%オフで購入1.3万種類以上の暗号資産にアクセス。ワンタップで買付、売却、スワップ、ステーキングが可能です。\nバックアップ用に最大3枚のカードを連携できます。Tangemウォレットを見るウォレット設定
@@ -993,6 +997,9 @@
このトークンを受け取るには、Hederaアカウントに関連付ける必要があります。トークンを関連付ける%sが不足しています。このトークンを関連付けるには、Hederaアカウントに資金を追加してください。
+ 取引をキャンセルしてもよろしいですか? 取引を再試行することはできません
+ %1$s %2$sの取引が完了しませんでした。もう一度試して完了してください。
+ 未完了の取引がありますこのカードには%sの署名のみが残っています。資金をすべて引き出す必要があります。署名数が少ないです異なるネットワーク上のトークンは、異なるアドレスを持つ場合があります。資金を送金する際には、アドレスがネットワークと一致していることを再確認してください。
@@ -1018,7 +1025,7 @@
Solanaネットワークが混雑しています。2分以内に取引が完了しない場合は、再度取引を繰り返してください。SolanaネットワークアラートSolana ネットワークは 2 日ごとに%1$sのレンタル料を請求します。レンタル料を支払えないアカウントはネットワークから削除されます。アカウントに%2$s以上入金すると、無料で使用できます。
- 現在、一部のネットワークにアクセスできません。しばらくしてからもう一度お試しください。
+ 下にスワイプして更新するか、後でもう一度お試しください。一部のネットワークにアクセスできませんこれはテストネットカードです。取引処理はできませんので、テストおよび開発目的でのみご利用ください。テスト目的のみ
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 9f00f71c2c..280a951d4b 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -183,6 +183,7 @@
Условиями использованияСегодняОшибка транзакции
+ Статус транзакцииТранзакцииПереводЯ понял
@@ -580,7 +581,7 @@
Для данной валюты нет доступных провайдеровОплата сВы сможете завершить транзакцию через сервис стороннего провайдера, %s
- Переход к %s ...
+ Переход к %s...Наши сервисы недоступны в данной странеНастройкиЧерез
@@ -965,7 +966,7 @@
Нет соединения с интернетомПолучить с 10%% скидкойПолучите доступ к более чем 13 000 криптовалют. Покупайте, продавайте, обменяйте и стейкайте в один клик. Свяжите до трех карт для резервного копирования.
- Откройте Tangem Wallet
+ Откройте Tangem WalletНастройки кошелькаTangemИспользуйте %s или отсканируйте карту/кольцо, чтобы разблокировать доступ к вашему кошельку
@@ -1011,6 +1012,9 @@
Этот токен должен быть ассоциирован с вашей учетной записью Hedera, прежде чем вы сможете его принятьАссоциируете свой токенНедостаточно %s. Пополните ваш аккаунт Hedera для ассоциации этого токена
+ Вы уверены что хотите отменить эту транзакцию? Вы не сможете отправить ее еще раз
+ Ваша транзакция %1$s %2$s не была завершена. Вы можете попробовать отправить ее еще раз.
+ У вас есть незавершенная транзакцияНа этой карте осталось всего %s подписей. Вам следует вывести все ваши средства.Малое количество подписейТокены на разных сетях могут иметь разные адреса. Пожалуйста, убедитесь при переводе средств, что ваш адрес соответствует сети.
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 8f07d981bf..c446190dcc 100644
--- a/core/res/src/main/res/values-uk-rUA/strings.xml
+++ b/core/res/src/main/res/values-uk-rUA/strings.xml
@@ -300,6 +300,9 @@
Недоступно для цієї париПотрібен дозвілРекомендовано
+ Купив %s
+ Купівля %s...
+ Купівля %s...Токенів не знайдено. Будь ласка, спробуйте інший запитID: %sID транзакції скопійовано
@@ -975,6 +978,9 @@
Мережа %sАдреса скопійована в буфер обмінуНемає підключення до інтернету
+ Отримати з 10%% знижкою
+ Доступ до 13,000+ криптовалют. Купуйте, продавайте, обмінюйте та стейкайте одним дотиком.\nЗ’єднайте до трьох карток для бекапу.
+ Відкрийте Tangem WalletНалаштування гаманцяTangemВикористовуйте %s або відскануйте картку/кільце, щоб розблокувати доступ до вашого гаманця
@@ -1020,6 +1026,9 @@
Цей токен повинен бути асоційований з вашим обліковим записом Hedera, перш ніж ви зможете його прийнятиАсоціюйте свій токенНедостатньо %s. Поповніть ваш обліковий запис Hedera для асоціації цього токена
+ Ви впевнені, що хочете скасувати цю транзакцію? Ви не зможете відправити її ще раз
+ Ваша транзакція %1$s %2$s не була завершена. Ви можете спробувати відправити її ще раз.
+ У вас є незавершена транзакціяНа цій картці залишається лише %s підписів. Ви повинні вивести всі свої кошти.Низька кількість підписівТокени в різних мережах можуть мати різні адреси. Переконайтеся, що ваша адреса відповідає мережі, коли переказуєте кошти.
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 6978552ae0..08a7924ae5 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -294,6 +294,9 @@
Unavailable for this pairPermission RequiredRecommended
+ Bought %s
+ Buying %s
+ Buying %s...No tokens found. Please try another requestID: %sTransaction ID copied
@@ -1004,6 +1007,9 @@
This token must be associated with your Hedera account before you can receive itAssociate your tokenNot enough %s. Top up your Hedera account to associate this token
+ Are you sure you want to cancel the transaction? You will not be able to retry the transaction again
+ Your transaction with an amount of %1$s %2$s was not completed. You can try again to complete it.
+ You have unfinished transactionOnly %s signatures are left on this card. You must withdraw all of your funds.Low signature countTokens on different networks can have different addresses. Double-check that your address matches the network when you transfer funds.
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NoteMigrationNotification.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NoteMigrationNotification.kt
new file mode 100644
index 0000000000..6b5f396f85
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NoteMigrationNotification.kt
@@ -0,0 +1,111 @@
+package com.tangem.core.ui.components.notifications
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.Text
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.tangem.core.ui.R
+import com.tangem.core.ui.components.buttons.common.TangemButton
+import com.tangem.core.ui.components.buttons.common.TangemButtonColors
+import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
+import com.tangem.core.ui.extensions.resolveReference
+import com.tangem.core.ui.extensions.stringReference
+import com.tangem.core.ui.res.TangemColorPalette
+import com.tangem.core.ui.res.TangemColorPalette.Dark6
+import com.tangem.core.ui.res.TangemColorPalette.Light2
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
+
+@Composable
+fun NoteMigrationNotification(config: NotificationConfig, modifier: Modifier = Modifier) {
+ val button = config.buttonsState as? NotificationConfig.ButtonsState.SecondaryButtonConfig
+ button ?: return
+
+ Column(
+ modifier = modifier
+ .fillMaxWidth()
+ .clip(shape = TangemTheme.shapes.roundedCornersXMedium)
+ .background(TangemColorPalette.Dark6),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Image(
+ modifier = Modifier.heightIn(max = 146.dp),
+ painter = painterResource(id = R.drawable.banner_note_migration),
+ contentDescription = null,
+ contentScale = ContentScale.Fit,
+ )
+ Column(
+ modifier = Modifier.padding(
+ start = 12.dp,
+ end = 12.dp,
+ bottom = 12.dp,
+ ),
+ verticalArrangement = Arrangement.spacedBy(10.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ config.title?.let {
+ Text(
+ text = it.resolveReference(),
+ color = TangemColorPalette.White,
+ style = TangemTheme.typography.h3,
+ )
+ }
+
+ Text(
+ text = config.subtitle.resolveReference(),
+ textAlign = TextAlign.Center,
+ color = TangemColorPalette.Light5,
+ style = TangemTheme.typography.body2,
+ )
+ TangemButton(
+ modifier = Modifier.fillMaxWidth(),
+ text = button.text.resolveReference(),
+ icon = TangemButtonIconPosition.None,
+ onClick = button.onClick,
+ colors = TangemButtonColors(
+ backgroundColor = Light2,
+ contentColor = Dark6,
+ disabledBackgroundColor = TangemTheme.colors.button.disabled,
+ disabledContentColor = TangemTheme.colors.text.disabled,
+ ),
+ enabled = true,
+ textStyle = TangemTheme.typography.subtitle1,
+ showProgress = false,
+ )
+ }
+ }
+}
+
+// region Preview
+@Preview(showBackground = true, widthDp = 328)
+@Composable
+private fun NoteMigrationNotification_Preview() {
+ TangemThemePreview {
+ NoteMigrationNotification(
+ NotificationConfig(
+ title = stringReference("Discover Tangem Wallet"),
+ subtitle = stringReference(
+ "Access 13,000+ cryptocurrencies. Buy, sell, swap, and stake with a single tap. Link up to " +
+ "three cards for a backup.",
+ ),
+ iconResId = R.drawable.ic_empty_64,
+ onCloseClick = { },
+ buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
+ text = stringReference("Get now"),
+ onClick = {},
+ ),
+ ),
+ )
+ }
+}
+
+// endregion
\ No newline at end of file
diff --git a/core/ui/src/main/res/drawable/banner_note_migration.webp b/core/ui/src/main/res/drawable/banner_note_migration.webp
new file mode 100644
index 0000000000..6f4f2a0ec6
Binary files /dev/null and b/core/ui/src/main/res/drawable/banner_note_migration.webp differ
diff --git a/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt b/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt
index 2a8c0ed1d7..a329974a0d 100644
--- a/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt
+++ b/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt
@@ -64,14 +64,10 @@ internal class DefaultMarketsTokenRepository(
// we shouldn't infinitely retry on the first batch request
val res = if (isFirstBatchFetching) {
- catchApiErrorAndSendEvent(errorEvent = MarketsDataAnalyticsEvent.List.Error) {
- requestCall()
- }
+ catchListErrorAndSendEvent { requestCall() }
} else {
retryOnError(priority = true) {
- catchApiErrorAndSendEvent(errorEvent = MarketsDataAnalyticsEvent.List.Error) {
- requestCall()
- }
+ catchListErrorAndSendEvent { requestCall() }
}
}
@@ -100,7 +96,7 @@ internal class DefaultMarketsTokenRepository(
marketsApi = marketsApi,
analyticsEventHandler = analyticsEventHandler,
onApiError = {
- analyticsEventHandler.send(MarketsDataAnalyticsEvent.List.Error.toEvent())
+ analyticsEventHandler.send(createListErrorEvent(it).toEvent())
},
)
@@ -128,11 +124,9 @@ internal class DefaultMarketsTokenRepository(
interval = interval.toRequestParam(),
)
- val result = catchApiErrorAndSendEvent(
- errorEvent = MarketsDataAnalyticsEvent.Details.Error(
- request = MarketsDataAnalyticsEvent.Details.Error.Request.Chart,
- tokenSymbol = tokenSymbol,
- ),
+ val result = catchDetailsErrorAndSendEvent(
+ request = MarketsDataAnalyticsEvent.Details.Error.Request.Chart,
+ tokenSymbol = tokenSymbol,
) {
response.getOrThrow()
}
@@ -146,6 +140,7 @@ internal class DefaultMarketsTokenRepository(
analyticsEventHandler.send(
MarketsDataAnalyticsEvent.ChartNullValuesError(
requestPath = "coins/history",
+ errorType = MarketsDataAnalyticsEvent.Type.Custom,
),
)
},
@@ -159,18 +154,16 @@ internal class DefaultMarketsTokenRepository(
tokenSymbol: String,
) = withContext(dispatcherProvider.io) {
val mappedTokenId = getTokenIdIfL2Network(tokenId)
- val response = marketsApi.getCoinsListCharts(
- coinIds = mappedTokenId,
- currency = fiatCurrencyCode,
- interval = interval.toRequestParam(),
- )
- val chart = catchApiErrorAndSendEvent(errorEvent = MarketsDataAnalyticsEvent.List.Error) {
- response.getOrThrow()[mappedTokenId] ?: error(
+ val chart = catchListErrorAndSendEvent {
+ marketsApi.getCoinsListCharts(
+ coinIds = mappedTokenId,
+ currency = fiatCurrencyCode,
+ interval = interval.toRequestParam(),
+ ).getOrThrow()[mappedTokenId] ?: error(
"No chart preview data for the token $mappedTokenId",
)
}
-
return@withContext TokenChartConverter.convert(
interval = interval,
value = chart,
@@ -180,6 +173,7 @@ internal class DefaultMarketsTokenRepository(
analyticsEventHandler.send(
MarketsDataAnalyticsEvent.ChartNullValuesError(
requestPath = "coins/history_preview",
+ errorType = MarketsDataAnalyticsEvent.Type.Custom,
),
)
},
@@ -192,19 +186,15 @@ internal class DefaultMarketsTokenRepository(
tokenSymbol: String,
languageCode: String,
) = withContext(dispatcherProvider.io) {
- val response = marketsApi.getCoinMarketData(
- currency = fiatCurrencyCode,
- coinId = tokenId,
- language = languageCode,
- )
-
- val result = catchApiErrorAndSendEvent(
- errorEvent = MarketsDataAnalyticsEvent.Details.Error(
- request = MarketsDataAnalyticsEvent.Details.Error.Request.Info,
- tokenSymbol = tokenSymbol,
- ),
+ val result = catchDetailsErrorAndSendEvent(
+ request = MarketsDataAnalyticsEvent.Details.Error.Request.Info,
+ tokenSymbol = tokenSymbol,
) {
- response.getOrThrow()
+ marketsApi.getCoinMarketData(
+ currency = fiatCurrencyCode,
+ coinId = tokenId,
+ language = languageCode,
+ ).getOrThrow()
}
val resultResponse = result.applyL2Compatibility(tokenId)
@@ -214,19 +204,16 @@ internal class DefaultMarketsTokenRepository(
override suspend fun getTokenQuotes(fiatCurrencyCode: String, tokenId: String, tokenSymbol: String) =
withContext(dispatcherProvider.io) {
// for second markets iteration we should use extended api method with all required fields
- val response = tangemTechApi.getQuotes(
- currencyId = fiatCurrencyCode,
- coinIds = tokenId,
- fields = marketsQuoteFields.joinToString(separator = ","),
- )
- val result = catchApiErrorAndSendEvent(
- errorEvent = MarketsDataAnalyticsEvent.Details.Error(
- request = MarketsDataAnalyticsEvent.Details.Error.Request.Info,
- tokenSymbol = tokenSymbol,
- ),
+ val result = catchDetailsErrorAndSendEvent(
+ request = MarketsDataAnalyticsEvent.Details.Error.Request.Info,
+ tokenSymbol = tokenSymbol,
) {
- response.getOrThrow()
+ tangemTechApi.getQuotes(
+ currencyId = fiatCurrencyCode,
+ coinIds = tokenId,
+ fields = marketsQuoteFields.joinToString(separator = ","),
+ ).getOrThrow()
}
return@withContext TokenQuotesShortConverter.convert(tokenId, result).toFull()
@@ -272,15 +259,75 @@ internal class DefaultMarketsTokenRepository(
}
}
- private inline fun catchApiErrorAndSendEvent(errorEvent: MarketsDataAnalyticsEvent, block: () -> T): T {
+ inline fun catchListErrorAndSendEvent(block: () -> T): T {
+ return catchErrorAndSendEvent(block, ::createListErrorEvent)
+ }
+
+ private inline fun catchDetailsErrorAndSendEvent(
+ request: MarketsDataAnalyticsEvent.Details.Error.Request,
+ tokenSymbol: String,
+ block: () -> T,
+ ): T {
+ return catchErrorAndSendEvent(block) { error ->
+ createDetailsErrorEvent(error, request, tokenSymbol)
+ }
+ }
+
+ private inline fun catchErrorAndSendEvent(
+ block: () -> T,
+ createErrorEvent: (ApiResponseError) -> MarketsDataAnalyticsEvent,
+ ): T {
return try {
block()
- } catch (e: ApiResponseError.HttpException) {
- analyticsEventHandler.send(errorEvent.toEvent())
- throw e
- } catch (e: ApiResponseError.TimeoutException) {
+ } catch (e: ApiResponseError) {
+ val errorEvent = createErrorEvent(e)
+
analyticsEventHandler.send(errorEvent.toEvent())
throw e
}
}
+
+ private fun createListErrorEvent(error: ApiResponseError): MarketsDataAnalyticsEvent.List.Error {
+ return createErrorEvent(error) { errorType, errorCode ->
+ MarketsDataAnalyticsEvent.List.Error(
+ errorType = errorType,
+ errorCode = errorCode,
+ )
+ }
+ }
+
+ private fun createDetailsErrorEvent(
+ error: ApiResponseError,
+ request: MarketsDataAnalyticsEvent.Details.Error.Request,
+ tokenSymbol: String,
+ ): MarketsDataAnalyticsEvent.Details.Error {
+ return createErrorEvent(error) { errorType, errorCode ->
+ MarketsDataAnalyticsEvent.Details.Error(
+ errorType = errorType,
+ errorCode = errorCode,
+ request = request,
+ tokenSymbol = tokenSymbol,
+ )
+ }
+ }
+
+ private inline fun createErrorEvent(
+ error: ApiResponseError,
+ createEvent: (MarketsDataAnalyticsEvent.Type, Int?) -> T,
+ ): T {
+ return when (error) {
+ is ApiResponseError.HttpException -> {
+ createEvent(MarketsDataAnalyticsEvent.Type.Http, error.code.code)
+ }
+ is ApiResponseError.TimeoutException -> {
+ createEvent(MarketsDataAnalyticsEvent.Type.Timeout, null)
+ }
+ is ApiResponseError.NetworkException -> {
+ createEvent(MarketsDataAnalyticsEvent.Type.Network, null)
+ }
+ is ApiResponseError.UnknownException -> {
+ createEvent(MarketsDataAnalyticsEvent.Type.Unknown, null)
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/data/markets/src/main/java/com/tangem/data/markets/MarketsBatchUpdateFetcher.kt b/data/markets/src/main/java/com/tangem/data/markets/MarketsBatchUpdateFetcher.kt
index f1a92028fb..6a9952d71f 100644
--- a/data/markets/src/main/java/com/tangem/data/markets/MarketsBatchUpdateFetcher.kt
+++ b/data/markets/src/main/java/com/tangem/data/markets/MarketsBatchUpdateFetcher.kt
@@ -25,7 +25,7 @@ internal class MarketsBatchUpdateFetcher(
private val marketsApi: TangemTechMarketsApi,
private val tangemTechApi: TangemTechApi,
private val analyticsEventHandler: AnalyticsEventHandler,
- private val onApiError: () -> Unit,
+ private val onApiError: (ApiResponseError) -> Unit,
) : BatchUpdateFetcher, TokenMarketUpdateRequest> {
override suspend fun BatchUpdateFetcher.UpdateContext>.fetchUpdateAsync(
@@ -124,6 +124,7 @@ internal class MarketsBatchUpdateFetcher(
analyticsEventHandler.send(
MarketsDataAnalyticsEvent.ChartNullValuesError(
requestPath = "coins/history_preview",
+ errorType = MarketsDataAnalyticsEvent.Type.Custom,
),
)
@@ -133,11 +134,11 @@ internal class MarketsBatchUpdateFetcher(
}
}
- private inline fun catchApiError(onError: () -> Unit, block: () -> T): T {
+ private inline fun catchApiError(onError: (ApiResponseError) -> Unit, block: () -> T): T {
return try {
block()
} catch (e: ApiResponseError) {
- onError()
+ onError(e)
throw e
}
}
diff --git a/data/markets/src/main/java/com/tangem/data/markets/analytics/MarketsDataAnalyticsEvent.kt b/data/markets/src/main/java/com/tangem/data/markets/analytics/MarketsDataAnalyticsEvent.kt
index 1c131a3cd4..e0d27728a0 100644
--- a/data/markets/src/main/java/com/tangem/data/markets/analytics/MarketsDataAnalyticsEvent.kt
+++ b/data/markets/src/main/java/com/tangem/data/markets/analytics/MarketsDataAnalyticsEvent.kt
@@ -9,7 +9,16 @@ sealed interface MarketsDataAnalyticsEvent {
params: Map = mapOf(),
) : AnalyticsEvent(category = "Markets", event = event, params = params), MarketsDataAnalyticsEvent {
- data object Error : List(event = "Data Error")
+ data class Error(
+ val errorType: Type,
+ val errorCode: Int? = null,
+ ) : List(
+ event = "Data Error",
+ params = buildMap {
+ put("Error Type", errorType.value)
+ errorCode?.let { put("Error Code", it.toString()) }
+ },
+ )
}
sealed class Details(
@@ -20,12 +29,16 @@ sealed interface MarketsDataAnalyticsEvent {
data class Error(
val request: Request,
val tokenSymbol: String,
+ val errorType: Type,
+ val errorCode: Int? = null,
) : Details(
event = "Data Error",
- params = mapOf(
- "Source" to request.source,
- "Token" to tokenSymbol,
- ),
+ params = buildMap {
+ put("Source", request.source)
+ put("Token", tokenSymbol)
+ errorCode?.let { put("Error Code", it.toString()) }
+ put("Error Type", errorType.value)
+ },
) {
enum class Request(val source: String) {
@@ -43,13 +56,25 @@ sealed interface MarketsDataAnalyticsEvent {
data class ChartNullValuesError(
val requestPath: String,
+ val errorType: Type,
+ val errorCode: Int? = null,
) : AnalyticsEvent(
category = "Markets / Chart",
event = "Data Error",
- params = mapOf("Request path" to requestPath),
- error = IllegalStateException(
- "Chart data contains null values from the API",
- ),
+ params = buildMap {
+ put("Request path", requestPath)
+ errorCode?.let { put("Error Code", it.toString()) }
+ put("Error Type", errorType.value)
+ put("Error Description", "Chart data contains null values from the API")
+ },
),
MarketsDataAnalyticsEvent
+
+ enum class Type(val value: String) {
+ Http("Http"),
+ Timeout("Timeout"),
+ Network("Network"),
+ Custom("Custom"),
+ Unknown("Unknown"),
+ }
}
\ No newline at end of file
diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt
index 13e1c3e368..a680263464 100644
--- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt
+++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt
@@ -45,7 +45,7 @@ internal class DefaultQuotesRepository(
.filterNotNull()
.flatMapLatest { appCurrency ->
fetchExpiredQuotes(currenciesIds, appCurrency.id, refresh = refresh)
- quotesStore.get(currenciesIds).map(quotesConverter::convertSet)
+ quotesStore.get(currenciesIds).map { quotesConverter.convert(currenciesIds to it) }
}
.cancellable()
.flowOn(dispatchers.io)
@@ -77,14 +77,15 @@ internal class DefaultQuotesRepository(
val quotes = quotesStore.getSync(currenciesIds)
- quotesConverter.convertSet(quotes)
+ quotesConverter.convert(currenciesIds to quotes)
}
}
override suspend fun getQuoteSync(currencyId: CryptoCurrency.ID): Quote? {
return withContext(dispatchers.io) {
- val quote = quotesStore.getSync(setOf(currencyId)).firstOrNull()
- quote?.let { quotesConverter.convert(it) }
+ val setOfCurrencyId = setOf(currencyId)
+ val quote = quotesStore.getSync(setOfCurrencyId).firstOrNull()
+ quote?.let { quotesConverter.convert(setOfCurrencyId to setOf(it)).firstOrNull() }
}
}
diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt
index 85c903ceec..79af4417b7 100644
--- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt
+++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt
@@ -1,16 +1,28 @@
package com.tangem.data.tokens.utils
import com.tangem.datasource.local.quote.model.StoredQuote
+import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Quote
import com.tangem.utils.converter.Converter
import java.math.BigDecimal
-internal class QuotesConverter : Converter {
+typealias QuotesConverterValue = Pair, Set>
- override fun convert(value: StoredQuote): Quote {
- val (rawCurrencyId, responseQuote) = value
+internal class QuotesConverter : Converter> {
- return Quote(
+ override fun convert(value: QuotesConverterValue): Set {
+ val (setOfCurrencyId, setOfStoredQuote) = value
+ return setOfCurrencyId.mapTo(hashSetOf()) { id ->
+ setOfStoredQuote.find { id.rawCurrencyId == it.rawCurrencyId }
+ ?.let(::convertExistStoredQuote)
+ ?: Quote.Empty(id.rawCurrencyId)
+ }
+ }
+
+ private fun convertExistStoredQuote(storedQuote: StoredQuote): Quote {
+ val (rawCurrencyId, responseQuote) = storedQuote
+
+ return Quote.Value(
rawCurrencyId = rawCurrencyId,
fiatRate = responseQuote.price ?: BigDecimal.ZERO,
priceChange = (responseQuote.priceChange24h ?: BigDecimal.ZERO).movePointLeft(2),
diff --git a/domain/markets/src/main/java/com/tangem/domain/markets/GetCurrencyQuotesUseCase.kt b/domain/markets/src/main/java/com/tangem/domain/markets/GetCurrencyQuotesUseCase.kt
index 1ec184fbdc..0a8d0d931d 100644
--- a/domain/markets/src/main/java/com/tangem/domain/markets/GetCurrencyQuotesUseCase.kt
+++ b/domain/markets/src/main/java/com/tangem/domain/markets/GetCurrencyQuotesUseCase.kt
@@ -15,10 +15,10 @@ class GetCurrencyQuotesUseCase(
currencyID: CryptoCurrency.ID,
interval: PriceChangeInterval,
refresh: Boolean,
- ): Flow