From 7d864a96e45e65c4c2690984eeaad2301005b466 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 19 Nov 2024 17:58:46 +0500 Subject: [PATCH] Updated on 2026-08-14 --- core/res/src/main/res/values-de/strings.xml | 2 +- core/res/src/main/res/values-es/strings.xml | 8 +- core/res/src/main/res/values-fr/strings.xml | 8 +- core/res/src/main/res/values-ja/strings.xml | 14 +- core/res/src/main/res/values-ru/strings.xml | 4 +- core/res/src/main/res/values/strings.xml | 3 + .../component/OnrampSuccessComponent.kt | 2 +- features/onramp/impl/build.gradle.kts | 2 +- .../success/DefaultOnrampSuccessComponent.kt | 8 +- .../entity/OnrampSuccessClickIntents.kt | 5 + .../entity/OnrampSuccessComponentUM.kt | 16 +- .../SetOnrampSuccessContentConverter.kt | 194 +++++++++++++++ .../OnrampSuccessComponentUMPreviewData.kt | 50 ++++ .../model/OnrampSuccessComponentModel.kt | 78 ++++++- .../ui/OnrampSuccessComponentContent.kt | 220 ++++++++++++++++++ 15 files changed, 585 insertions(+), 29 deletions(-) create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/OnrampSuccessClickIntents.kt create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/conterter/SetOnrampSuccessContentConverter.kt create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/previewdata/OnrampSuccessComponentUMPreviewData.kt create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/ui/OnrampSuccessComponentContent.kt diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 65a4990969..8fe310b7d9 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -568,7 +568,7 @@ Keine verfügbaren Anbieter für diese Währung Bezahlen mit Du 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 bitte Dein Wohnsitz wurde identifiziert als 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 mostrados Cancelar Esta 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 complete El 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 XPUB Ocultar Está 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és Annuler Cette 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ées L\'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 XPUB Masquer Vous ê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 3d8d8d15d5..f4e7cfccfe 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -830,7 +830,7 @@ 金額には以下が含まれます: \n • サービス プロバイダーの手数料\n • 取引所からユーザーのアドレスに%1$sを送金するためのネットワーク手数料。 \n\nプロバイダーのスリッページは最大%2$sです この金額には、サービスプロバイダーの手数料が含まれています。 金額にはサービスプロバイダーの手数料が含まれます。 \n\nプロバイダーのスリッページは最大%s です - 手数料 + 情報 すべての分散型取引所は、スマートコントラクトがあなたの許可なくウォレットにアクセスするのを防ぐために承認を必要とします。設計上、スマートコントラクトは承認なしでトークンにアクセスできません。トークンを「ロック解除」することで、あなたは1-inchのスマートコントラクトがトークンを使うことを承認します。ネットワークのマイナーは、このアクションをブロックチェーンに記録するためのガス料金(あなたが支払う)を受け取ります。承認後、トークンを交換することができます。 承認 手数料見積りエラーです。サポートにフィードバックをお送りください。 @@ -846,14 +846,14 @@ 残高表示 元に戻す この操作は現在利用できません。しばらくしてからもう一度お試しください。 - 現在、 %sの買付はご利用いただけません。アップデート情報をご確認ください。 + %sの買付は、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます。 売却できる資金がありません。アカウントに入金して、売却できるようにしてください。 送金する資金がありません。アカウントに入金して、送金できるようにしてください。 - 現在、 %sの交換はご利用いただけません。アップデート情報を確認してください。 + %sのスワップは、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます。 ネットワーク%s内の保留中の取引が完了すると、資金の売却が可能になります。 ネットワーク%s内の保留中の取引が完了すると、送金が可能になります。 - 現在、 %sの売却はご利用いただけません。アップデート情報をご確認ください。 - %s のステーキングは現在ご利用いただけません。最新情報をご確認ください。 + %sの売却は、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます。 + %sのステーキングは、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます。 XPUBを生成する 非表示 このトークンをメイン画面から非表示にします。トークンの管理ページからいつでも再度追加できます。 @@ -946,7 +946,7 @@ %sネットワーク アドレスがクリップボードにコピーされました インターネット接続がありません - 今すぐ10 %オフで購入 + 今すぐ10 %%オフで購入 1.3万種類以上の暗号資産にアクセス。ワンタップで買付、売却、スワップ、ステーキングが可能です。\nバックアップ用に最大3枚のカードを連携できます。 Tangemウォレットを見る ウォレット設定 @@ -1019,7 +1019,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 9a43af9607..76ba8cfddf 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -581,7 +581,7 @@ Для данной валюты нет доступных провайдеров Оплата с Вы сможете завершить транзакцию через сервис стороннего провайдера, %s - Переход к %s ... + Переход к %s... Наши сервисы недоступны в данной стране Настройки Через @@ -966,7 +966,7 @@ Нет соединения с интернетом Получить с 10%% скидкой Получите доступ к более чем 13 000 криптовалют. Покупайте, продавайте, обменяйте и стейкайте в один клик. Свяжите до трех карт для резервного копирования. - Откройте Tangem Wallet + Откройте Tangem Wallet Настройки кошелька Tangem Используйте %s или отсканируйте карту/кольцо, чтобы разблокировать доступ к вашему кошельку diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 6978552ae0..6241677828 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 pair Permission Required Recommended + Bought %s + Buying %s + Buying %s... No tokens found. Please try another request ID: %s Transaction ID copied diff --git a/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampSuccessComponent.kt b/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampSuccessComponent.kt index 70b6e543c4..ceed88f9c5 100644 --- a/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampSuccessComponent.kt +++ b/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampSuccessComponent.kt @@ -5,7 +5,7 @@ import com.tangem.core.ui.decompose.ComposableContentComponent interface OnrampSuccessComponent : ComposableContentComponent { - data class Params(val txId: String?) + data class Params(val txId: String) interface Factory : ComponentFactory } \ No newline at end of file diff --git a/features/onramp/impl/build.gradle.kts b/features/onramp/impl/build.gradle.kts index 314c0f9529..c97691d146 100644 --- a/features/onramp/impl/build.gradle.kts +++ b/features/onramp/impl/build.gradle.kts @@ -22,6 +22,7 @@ dependencies { implementation(projects.core.decompose) implementation(projects.core.ui) implementation(projects.core.featuretoggles) + implementation(projects.core.navigation) /** Project - Common */ implementation(projects.common.routing) @@ -42,7 +43,6 @@ dependencies { /** DI */ implementation(deps.hilt.android) - implementation(project(":common:ui")) kapt(deps.hilt.kapt) /** AndroidX */ diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/DefaultOnrampSuccessComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/DefaultOnrampSuccessComponent.kt index 0f5787d3b3..d1b329d10b 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/DefaultOnrampSuccessComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/DefaultOnrampSuccessComponent.kt @@ -1,5 +1,6 @@ package com.tangem.features.onramp.success +import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -8,6 +9,7 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.features.onramp.component.OnrampSuccessComponent import com.tangem.features.onramp.success.model.OnrampSuccessComponentModel +import com.tangem.features.onramp.success.ui.OnrampSuccessComponentContent import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -23,7 +25,11 @@ internal class DefaultOnrampSuccessComponent @AssistedInject constructor( override fun Content(modifier: Modifier) { val state by model.state.collectAsState() - // todo onramp [REDACTED_JIRA] + BackHandler(onBack = router::pop) + OnrampSuccessComponentContent( + state = state, + onBackClick = router::pop, + ) } @AssistedFactory diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/OnrampSuccessClickIntents.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/OnrampSuccessClickIntents.kt new file mode 100644 index 0000000000..7c7565aef5 --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/OnrampSuccessClickIntents.kt @@ -0,0 +1,5 @@ +package com.tangem.features.onramp.success.entity + +interface OnrampSuccessClickIntents { + fun goToProviderClick(providerLink: String) +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/OnrampSuccessComponentUM.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/OnrampSuccessComponentUM.kt index 3f0bea7160..71f80e2f05 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/OnrampSuccessComponentUM.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/OnrampSuccessComponentUM.kt @@ -1,16 +1,22 @@ package com.tangem.features.onramp.success.entity +import com.tangem.common.ui.expressStatus.state.ExpressStatusUM +import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.core.ui.extensions.TextReference + sealed class OnrampSuccessComponentUM { data object Loading : OnrampSuccessComponentUM() data class Content( val txId: String, - val createdAt: Long, - val providerId: String, + val timestamp: Long, val currencyImageUrl: String, - val fromAmount: String, - val toAmount: String, - val onBackClick: () -> Unit, + val fromAmount: TextReference, + val toAmount: TextReference, + val statusBlock: ExpressStatusUM, + val providerName: TextReference, + val providerImageUrl: String, + val notification: NotificationUM?, ) : OnrampSuccessComponentUM() } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/conterter/SetOnrampSuccessContentConverter.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/conterter/SetOnrampSuccessContentConverter.kt new file mode 100644 index 0000000000..435ced42ba --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/conterter/SetOnrampSuccessContentConverter.kt @@ -0,0 +1,194 @@ +package com.tangem.features.onramp.success.entity.conterter + +import com.tangem.common.ui.expressStatus.state.ExpressLinkUM +import com.tangem.common.ui.expressStatus.state.ExpressStatusItemState +import com.tangem.common.ui.expressStatus.state.ExpressStatusItemUM +import com.tangem.common.ui.expressStatus.state.ExpressStatusUM +import com.tangem.common.ui.notifications.ExpressNotificationsUM +import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.fiat +import com.tangem.core.ui.format.bigdecimal.format +import com.tangem.domain.onramp.model.OnrampStatus +import com.tangem.domain.onramp.model.cache.OnrampTransaction +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.features.onramp.impl.R +import com.tangem.features.onramp.success.entity.OnrampSuccessComponentUM +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.persistentListOf +import org.joda.time.DateTime + +internal class SetOnrampSuccessContentConverter( + private val cryptoCurrency: CryptoCurrency, + private val transaction: OnrampTransaction, + private val goToProviderClick: (String) -> Unit, +) : Converter { + override fun convert(value: OnrampStatus): OnrampSuccessComponentUM { + return OnrampSuccessComponentUM.Content( + txId = value.txId, + timestamp = DateTime.parse(value.createdAt).millis, + currencyImageUrl = transaction.fromCurrency.image, + fromAmount = stringReference( + transaction.fromAmount.format { + fiat( + fiatCurrencyCode = transaction.fromCurrency.name, + fiatCurrencySymbol = transaction.fromCurrency.code, + ) + }, + ), + toAmount = stringReference( + transaction.toAmount.format { + crypto(cryptoCurrency) + }, + ), + providerName = stringReference(transaction.providerName), + providerImageUrl = transaction.providerImageUrl, + statusBlock = convertStatuses(value.status, value.externalTxUrl), + notification = getNotification(value.status, value.externalTxUrl), + ) + } + + private fun getNotification(status: OnrampStatus.Status, externalTxUrl: String?): NotificationUM? { + if (externalTxUrl == null) return null + return when (status) { + OnrampStatus.Status.Verifying -> { + ExpressNotificationsUM.NeedVerification { + goToProviderClick(externalTxUrl) + } + } + OnrampStatus.Status.Failed -> { + ExpressNotificationsUM.FailedByProvider { + goToProviderClick(externalTxUrl) + } + } + else -> null + } + } + + private fun convertStatuses(status: OnrampStatus.Status, externalTxUrl: String?): ExpressStatusUM { + val statuses = with(status) { + persistentListOf( + getAwaitingDepositItem(), + getPaymentProcessingItem(), + getBuyingItem(), + getSendingItem(), + ) + } + + return ExpressStatusUM( + title = resourceReference(R.string.common_transaction_status), + link = getStatusLink(status, externalTxUrl), + statuses = statuses, + ) + } + + private fun OnrampStatus.Status.getAwaitingDepositItem() = ExpressStatusItemUM( + text = when { + order < OnrampStatus.Status.WaitingForPayment.order -> { + resourceReference(R.string.express_exchange_status_receiving) + } + this == OnrampStatus.Status.WaitingForPayment -> { + resourceReference(R.string.express_exchange_status_receiving_active) + } + else -> { + resourceReference(R.string.express_exchange_status_received) + } + }, + state = getStatusState(OnrampStatus.Status.WaitingForPayment), + ) + + private fun OnrampStatus.Status.getPaymentProcessingItem() = ExpressStatusItemUM( + text = when { + order < OnrampStatus.Status.PaymentProcessing.order -> { + resourceReference(R.string.express_exchange_status_confirming) + } + this == OnrampStatus.Status.PaymentProcessing -> { + resourceReference(R.string.express_exchange_status_confirming_active) + } + this == OnrampStatus.Status.Verifying -> { + resourceReference(R.string.express_exchange_status_verifying) + } + this == OnrampStatus.Status.Failed -> { + resourceReference(R.string.express_exchange_status_failed) + } + else -> resourceReference(R.string.express_exchange_status_confirmed) + }, + state = when { + order < OnrampStatus.Status.PaymentProcessing.order -> { + ExpressStatusItemState.Default + } + this == OnrampStatus.Status.PaymentProcessing -> { + ExpressStatusItemState.Active + } + this == OnrampStatus.Status.Verifying -> { + ExpressStatusItemState.Warning + } + this == OnrampStatus.Status.Failed -> { + ExpressStatusItemState.Error + } + else -> { + ExpressStatusItemState.Done + } + }, + ) + + private fun OnrampStatus.Status.getBuyingItem() = ExpressStatusItemUM( + text = when { + order < OnrampStatus.Status.Paid.order -> { + resourceReference(R.string.express_status_buying, wrappedList(cryptoCurrency.name)) + } + this == OnrampStatus.Status.Paid -> { + resourceReference(R.string.express_status_buying_active, wrappedList(cryptoCurrency.name)) + } + else -> { + resourceReference(R.string.express_status_bought, wrappedList(cryptoCurrency.name)) + } + }, + state = getStatusState(OnrampStatus.Status.Paid), + ) + + private fun OnrampStatus.Status.getSendingItem() = ExpressStatusItemUM( + text = when { + order < OnrampStatus.Status.Sending.order -> { + resourceReference(R.string.express_exchange_status_sending, wrappedList(cryptoCurrency.name)) + } + this == OnrampStatus.Status.Sending -> { + resourceReference( + R.string.express_exchange_status_sending_active, + wrappedList(cryptoCurrency.name), + ) + } + else -> { + resourceReference(R.string.express_exchange_status_sent, wrappedList(cryptoCurrency.name)) + } + }, + state = getStatusState(OnrampStatus.Status.Sending), + ) + + private fun getStatusLink(status: OnrampStatus.Status, externalTxUrl: String?): ExpressLinkUM { + if (externalTxUrl == null) return ExpressLinkUM.Empty + return when (status) { + OnrampStatus.Status.Verifying, + OnrampStatus.Status.Failed, + -> { + ExpressLinkUM.Content( + icon = R.drawable.ic_arrow_top_right_24, + text = resourceReference(R.string.common_go_to_provider), + onClick = { + goToProviderClick(externalTxUrl) + }, + ) + } + else -> ExpressLinkUM.Empty + } + } + + private fun OnrampStatus.Status.getStatusState(targetState: OnrampStatus.Status) = when { + order < targetState.order -> ExpressStatusItemState.Default + this == targetState -> ExpressStatusItemState.Active + else -> ExpressStatusItemState.Done + } +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/previewdata/OnrampSuccessComponentUMPreviewData.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/previewdata/OnrampSuccessComponentUMPreviewData.kt new file mode 100644 index 0000000000..d7e355ba63 --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/entity/previewdata/OnrampSuccessComponentUMPreviewData.kt @@ -0,0 +1,50 @@ +package com.tangem.features.onramp.success.entity.previewdata + +import com.tangem.common.ui.expressStatus.state.ExpressLinkUM +import com.tangem.common.ui.expressStatus.state.ExpressStatusItemState +import com.tangem.common.ui.expressStatus.state.ExpressStatusItemUM +import com.tangem.common.ui.expressStatus.state.ExpressStatusUM +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.features.onramp.impl.R +import com.tangem.features.onramp.success.entity.OnrampSuccessComponentUM +import kotlinx.collections.immutable.persistentListOf +import org.joda.time.DateTime + +internal data object OnrampSuccessComponentUMPreviewData { + + val loadingState = OnrampSuccessComponentUM.Loading + + val contentState = OnrampSuccessComponentUM.Content( + txId = "b2894851-7b63-4f56-bd75-e408f1dcba31", + timestamp = DateTime.parse("2023-09-20T13:45:10.868Z").millis, + providerName = stringReference("1Inch"), + providerImageUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/1INCH1024.png", + fromAmount = stringReference("100 USDT"), + toAmount = stringReference("99.99 $"), + currencyImageUrl = "", + notification = null, + statusBlock = ExpressStatusUM( + title = resourceReference(R.string.express_exchange_status_title), + link = ExpressLinkUM.Empty, + statuses = persistentListOf( + ExpressStatusItemUM( + text = stringReference("Deposit received"), + state = ExpressStatusItemState.Done, + ), + ExpressStatusItemUM( + text = stringReference("Confirmed"), + state = ExpressStatusItemState.Done, + ), + ExpressStatusItemUM( + text = stringReference("Buying Bitcoin..."), + state = ExpressStatusItemState.Active, + ), + ExpressStatusItemUM( + text = stringReference("Sending to you"), + state = ExpressStatusItemState.Default, + ), + ), + ), + ) +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/model/OnrampSuccessComponentModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/model/OnrampSuccessComponentModel.kt index bdda90effd..6e925f143c 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/model/OnrampSuccessComponentModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/model/OnrampSuccessComponentModel.kt @@ -1,25 +1,97 @@ package com.tangem.features.onramp.success.model +import arrow.core.getOrElse import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.navigation.url.UrlOpener +import com.tangem.domain.onramp.GetOnrampStatusUseCase +import com.tangem.domain.onramp.GetOnrampTransactionUseCase +import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase +import com.tangem.domain.onramp.model.OnrampStatus +import com.tangem.domain.onramp.model.cache.OnrampTransaction +import com.tangem.domain.tokens.GetCryptoCurrencyUseCase import com.tangem.features.onramp.component.OnrampSuccessComponent +import com.tangem.features.onramp.success.entity.OnrampSuccessClickIntents import com.tangem.features.onramp.success.entity.OnrampSuccessComponentUM +import com.tangem.features.onramp.success.entity.conterter.SetOnrampSuccessContentConverter import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import timber.log.Timber import javax.inject.Inject +@Suppress("LongParameterList") internal class OnrampSuccessComponentModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, + private val urlOpener: UrlOpener, + private val getOnrampTransactionUseCase: GetOnrampTransactionUseCase, + private val getOnrampStatusUseCase: GetOnrampStatusUseCase, + private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase, + private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase, paramsContainer: ParamsContainer, -) : Model() { +) : Model(), OnrampSuccessClickIntents { - // todo onramp remove in [REDACTED_JIRA] - @Suppress("UnusedPrivateMember") private val params: OnrampSuccessComponent.Params = paramsContainer.require() private val _state: MutableStateFlow = MutableStateFlow( value = OnrampSuccessComponentUM.Loading, ) + val state: StateFlow get() = _state.asStateFlow() + + init { + loadData() + } + + override fun goToProviderClick(providerLink: String) { + urlOpener.openUrl(providerLink) + } + + private fun loadData() { + modelScope.launch { + getOnrampTransactionUseCase(txId = params.txId) + .fold( + ifLeft = { + Timber.e(it.toString()) + }, + ifRight = { transaction -> + loadTransactionStatus(transaction) + }, + ) + } + } + + private suspend fun loadTransactionStatus(transaction: OnrampTransaction) { + val cryptoCurrencies = getCryptoCurrencyUseCase( + transaction.userWalletId, + transaction.toCurrencyId, + ).getOrElse { error("Crypto currency not found") } + + getOnrampStatusUseCase(txId = params.txId) + .fold( + ifLeft = { + Timber.e(it.toString()) + }, + ifRight = { status -> + _state.update { + SetOnrampSuccessContentConverter( + cryptoCurrency = cryptoCurrencies, + transaction = transaction, + goToProviderClick = ::goToProviderClick, + ).convert(status) + } + removeTransactionIfTerminalStatus(status) + }, + ) + } + + private fun removeTransactionIfTerminalStatus(status: OnrampStatus) { + modelScope.launch { + if (status.status.isTerminal()) { + onrampRemoveTransactionUseCase(status.txId) + } + } + } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/ui/OnrampSuccessComponentContent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/ui/OnrampSuccessComponentContent.kt new file mode 100644 index 0000000000..e021c732e7 --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/success/ui/OnrampSuccessComponentContent.kt @@ -0,0 +1,220 @@ +package com.tangem.features.onramp.success.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp +import coil.compose.SubcomposeAsyncImage +import coil.request.ImageRequest +import com.tangem.common.ui.expressStatus.ExpressStatusBlock +import com.tangem.common.ui.expressStatus.ExpressStatusNotificationBlock +import com.tangem.core.ui.components.* +import com.tangem.core.ui.components.appbar.TangemTopAppBar +import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM +import com.tangem.core.ui.components.containers.FooterContainer +import com.tangem.core.ui.components.transactions.TransactionDoneTitle +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.utils.DateTimeFormatters +import com.tangem.core.ui.utils.WindowInsetsZero +import com.tangem.core.ui.utils.toTimeFormat +import com.tangem.features.onramp.impl.R +import com.tangem.features.onramp.success.entity.OnrampSuccessComponentUM +import com.tangem.features.onramp.success.entity.previewdata.OnrampSuccessComponentUMPreviewData + +@Composable +internal fun OnrampSuccessComponentContent( + state: OnrampSuccessComponentUM, + onBackClick: () -> Unit, + modifier: Modifier = Modifier, +) { + if (state !is OnrampSuccessComponentUM.Content) { + Box(modifier.background(TangemTheme.colors.background.secondary)) + } else { + Content( + state = state, + onBackClick = onBackClick, + modifier = modifier, + ) + } +} + +@Composable +private fun Content(state: OnrampSuccessComponentUM.Content, onBackClick: () -> Unit, modifier: Modifier = Modifier) { + Scaffold( + modifier = modifier + .background(TangemTheme.colors.background.secondary) + .systemBarsPadding(), + topBar = { + TangemTopAppBar( + startButton = TopAppBarButtonUM( + iconRes = R.drawable.ic_close_24, + onIconClicked = onBackClick, + ), + ) + }, + bottomBar = { + PrimaryButton( + text = stringResource(R.string.common_close), + onClick = onBackClick, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) + }, + contentWindowInsets = WindowInsetsZero, + ) { scaffoldPaddings -> + Column( + modifier = Modifier + .background(TangemTheme.colors.background.secondary) + .padding(scaffoldPaddings) + .fillMaxSize() + .verticalScroll(rememberScrollState()) + .padding(16.dp), + ) { + TransactionDoneTitle( + title = resourceReference(R.string.common_in_progress), + subtitle = resourceReference( + R.string.send_date_format, + wrappedList( + state.timestamp.toTimeFormat(DateTimeFormatters.dateFormatter), + state.timestamp.toTimeFormat(), + ), + ), + ) + SpacerH24() + AmountBlock(state) + SpacerH12() + FooterContainer( + footer = resourceReference(R.string.onramp_transaction_status_footer_text), + ) { + ExpressStatusBlock(state.statusBlock) + } + ExpressStatusNotificationBlock( + state.notification, + ) + } + } +} + +@Composable +private fun AmountBlock(state: OnrampSuccessComponentUM.Content) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(TangemTheme.colors.background.action) + .padding(16.dp), + ) { + val iconModifier = Modifier.size(size = 40.dp) + SubcomposeAsyncImage( + modifier = iconModifier, + model = ImageRequest.Builder(context = LocalContext.current) + .data(state.currencyImageUrl) + .crossfade(enable = true) + .allowHardware(false) + .build(), + loading = { CircleShimmer(modifier = iconModifier) }, + error = { }, + contentDescription = null, + ) + ResizableText( + text = state.fromAmount.resolveReference(), + style = TangemTheme.typography.h2, + color = TangemTheme.colors.text.primary1, + textAlign = TextAlign.Center, + maxLines = 1, + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp), + ) + Text( + text = state.toAmount.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + ) + SpacerH12() + AmountProviderBlock( + state.providerName, + state.providerImageUrl, + ) + } +} + +@Composable +private fun AmountProviderBlock(providerName: TextReference, providerImageUrl: String, modifier: Modifier = Modifier) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp), + ) { + Text( + text = stringResource(R.string.common_with), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.tertiary, + ) + SubcomposeAsyncImage( + modifier = Modifier.size(size = 16.dp), + model = ImageRequest.Builder(context = LocalContext.current) + .data(providerImageUrl) + .crossfade(enable = true) + .allowHardware(false) + .build(), + loading = { CircleShimmer() }, + error = { }, + contentDescription = null, + ) + Text( + text = providerName.resolveReference(), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + ) + } +} + +// region Preview +@Preview(showBackground = true, widthDp = 360, heightDp = 720) +@Preview(showBackground = true, widthDp = 360, heightDp = 720, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun OnrampSuccessComponentContent_Preview( + @PreviewParameter(OnrampSuccessComponentContentPreviewProvider::class) + data: OnrampSuccessComponentUM, +) { + TangemThemePreview { + OnrampSuccessComponentContent( + state = data, + onBackClick = {}, + ) + } +} + +private class OnrampSuccessComponentContentPreviewProvider : + PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + OnrampSuccessComponentUMPreviewData.contentState, + ) +} +// endregion \ No newline at end of file