Updated on 2026-08-14

This commit is contained in:
Tangem 2024-11-19 17:58:46 +05:00
parent 2f5d6525e2
commit 7d864a96e4
15 changed files with 585 additions and 29 deletions

View file

@ -568,7 +568,7 @@
<string name="onramp_no_available_providers">Keine verfügbaren Anbieter für diese Währung</string>
<string name="onramp_pay_with">Bezahlen mit</string>
<string name="onramp_redirecting_to_provider_subtitle">Du kannst Deine Transaktion beim Drittanbieter %s abschließen.</string>
<string name="onramp_redirecting_to_provider_title">Umleitung auf %s </string>
<string name="onramp_redirecting_to_provider_title">Umleitung auf %s…</string>
<string name="onramp_residency_bottomsheet_country_not_supported">Unsere Dienstleistungen sind in diesem Land nicht verfügbar</string>
<string name="onramp_residency_bottomsheet_country_subtitle">Änder oder bestätige bitte</string>
<string name="onramp_residency_bottomsheet_title">Dein Wohnsitz wurde identifiziert als</string>

View file

@ -858,14 +858,14 @@
<string name="toast_balances_shown">Saldos mostrados</string>
<string name="toast_undo">Cancelar</string>
<string name="token_button_unavailability_generic_description">Esta operación no está disponible actualmente. Por favor, inténtalo de nuevo más tarde.</string>
<string name="token_button_unavailability_reason_buy_unavailable">Comprar %s no está disponible en este momento. Por favor revise sus actualizaciones.</string>
<string name="token_button_unavailability_reason_buy_unavailable">Comprar %s no está disponible con los proveedores actuales pero estamos trabajando para agregar más opciones.</string>
<string name="token_button_unavailability_reason_empty_balance_sell">No tiene fondos para vender. Recargue su cuenta para poder vender fondos desde ella.</string>
<string name="token_button_unavailability_reason_empty_balance_send">No tienes fondos para enviar. Recarga tu cuenta para poder enviar fondos desde ella.</string>
<string name="token_button_unavailability_reason_not_exchangeable">El servicio de intercambio %s no está disponible en este momento. Por favor consulte nuestras actualizaciones.</string>
<string name="token_button_unavailability_reason_not_exchangeable">El servicio de intercambio %s no está compatible con los proveedores actuales pero estamos trabajando para agregar más opciones.</string>
<string name="token_button_unavailability_reason_pending_transaction_sell">La venta de fondos estará disponible una vez que la(s) transacción(es) pendiente(s) en la red %s se complete</string>
<string name="token_button_unavailability_reason_pending_transaction_send">El envío de fondos estará disponible una vez que se completen las transacciones pendientes en la red %s.</string>
<string name="token_button_unavailability_reason_sell_unavailable">La venta de %s no está disponible en este momento. Por favor consulte nuestras actualizaciones.</string>
<string name="token_button_unavailability_reason_staking_unavailable">El staking %s no está disponible en este momento. Por favor consulte nuestras actualizaciones.</string>
<string name="token_button_unavailability_reason_sell_unavailable">Vender %s no es compatible con los proveedores actuales, pero estamos trabajando para agregar más opciones.</string>
<string name="token_button_unavailability_reason_staking_unavailable">El staking de %s no es compatible con los proveedores actuales, pero estamos trabajando para agregar más opciones.</string>
<string name="token_details_generate_xpub">Generar XPUB</string>
<string name="token_details_hide_alert_hide">Ocultar</string>
<string name="token_details_hide_alert_message">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.</string>

View file

@ -850,14 +850,14 @@
<string name="toast_balances_shown">Soldes affichés</string>
<string name="toast_undo">Annuler</string>
<string name="token_button_unavailability_generic_description">Cette opération est actuellement indisponible. Veuillez réessayer plus tard.</string>
<string name="token_button_unavailability_reason_buy_unavailable">L\'achat de %s n\'est pas disponible pour le moment. Veuillez vérifier vos mises à jour.</string>
<string name="token_button_unavailability_reason_buy_unavailable">L\'achat de %s n\'est pas pris en charge par les fournisseurs actuels mais nous travaillons à ajouter davantage d\'options.</string>
<string name="token_button_unavailability_reason_empty_balance_sell">Vous n\'avez pas de fonds à vendre. Renflouez votre compte pour pouvoir vendre des fonds à partir de celui-ci.</string>
<string name="token_button_unavailability_reason_empty_balance_send">Vous n\'avez pas de fonds à envoyer. Renflouez votre compte pour pouvoir envoyer des fonds à partir de celui-ci.</string>
<string name="token_button_unavailability_reason_not_exchangeable">Le service d\'échange %s n\'est pas disponible pour le moment. Veuillez consulter nos mises à jour.</string>
<string name="token_button_unavailability_reason_not_exchangeable">Le service d\'échange %s n\'est pas pris en charge par les fournisseurs actuels mais nous travaillons à ajouter plus d\'options.</string>
<string name="token_button_unavailability_reason_pending_transaction_sell">La vente de fonds sera disponible une fois que la ou les transactions en attente dans le réseau %s seront terminées</string>
<string name="token_button_unavailability_reason_pending_transaction_send">L\'envoi de fonds sera disponible une fois la ou les transactions en attente dans le réseau %s terminées.</string>
<string name="token_button_unavailability_reason_sell_unavailable">La vente de %s n\'est pas disponible pour le moment. Veuillez consulter nos mises à jour.</string>
<string name="token_button_unavailability_reason_staking_unavailable">Le staking %s nest pas disponible pour le moment. Veuillez consulter nos mises à jour.</string>
<string name="token_button_unavailability_reason_sell_unavailable">L\'achat de %s n\'est pas pris en charge par les fournisseurs actuels mais nous travaillons à ajouter plus d\'options.</string>
<string name="token_button_unavailability_reason_staking_unavailable">Le staking de %s n\'est pas pris en charge par les fournisseurs actuels, mais nous travaillons à ajouter plus d\'options.</string>
<string name="token_details_generate_xpub">Générer XPUB</string>
<string name="token_details_hide_alert_hide">Masquer</string>
<string name="token_details_hide_alert_message">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.</string>

View file

@ -830,7 +830,7 @@
<string name="swapping_alert_cex_description_with_slippage">金額には以下が含まれます: \n • サービス プロバイダーの手数料\n • 取引所からユーザーのアドレスに%1$sを送金するためのネットワーク手数料。 \n\nプロバイダーのスリッページは最大%2$sです</string>
<string name="swapping_alert_dex_description">この金額には、サービスプロバイダーの手数料が含まれています。</string>
<string name="swapping_alert_dex_description_with_slippage">金額にはサービスプロバイダーの手数料が含まれます。 \n\nプロバイダーのスリッページは最大%s です</string>
<string name="swapping_alert_title">手数料</string>
<string name="swapping_alert_title">情報</string>
<string name="swapping_approve_information_text">すべての分散型取引所は、スマートコントラクトがあなたの許可なくウォレットにアクセスするのを防ぐために承認を必要とします。設計上、スマートコントラクトは承認なしでトークンにアクセスできません。トークンを「ロック解除」することで、あなたは1-inchのスマートコントラクトがトークンを使うことを承認します。ネットワークのマイナーは、このアクションをブロックチェーンに記録するためのガス料金あなたが支払うを受け取ります。承認後、トークンを交換することができます。</string>
<string name="swapping_approve_information_title">承認</string>
<string name="swapping_fee_estimation_error_text">手数料見積りエラーです。サポートにフィードバックをお送りください。</string>
@ -846,14 +846,14 @@
<string name="toast_balances_shown">残高表示</string>
<string name="toast_undo">元に戻す</string>
<string name="token_button_unavailability_generic_description">この操作は現在利用できません。しばらくしてからもう一度お試しください。</string>
<string name="token_button_unavailability_reason_buy_unavailable">現在、 %sの買付はご利用いただけません。アップデート情報をご確認ください</string>
<string name="token_button_unavailability_reason_buy_unavailable">%sの買付は、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます</string>
<string name="token_button_unavailability_reason_empty_balance_sell">売却できる資金がありません。アカウントに入金して、売却できるようにしてください。</string>
<string name="token_button_unavailability_reason_empty_balance_send">送金する資金がありません。アカウントに入金して、送金できるようにしてください。</string>
<string name="token_button_unavailability_reason_not_exchangeable">現在、 %sの交換はご利用いただけません。アップデート情報を確認してください</string>
<string name="token_button_unavailability_reason_not_exchangeable">%sのスワップは、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます</string>
<string name="token_button_unavailability_reason_pending_transaction_sell">ネットワーク%s内の保留中の取引が完了すると、資金の売却が可能になります。</string>
<string name="token_button_unavailability_reason_pending_transaction_send">ネットワーク%s内の保留中の取引が完了すると、送金が可能になります。</string>
<string name="token_button_unavailability_reason_sell_unavailable">現在、 %sの売却はご利用いただけません。アップデート情報をご確認ください</string>
<string name="token_button_unavailability_reason_staking_unavailable">%s のステーキングは現在ご利用いただけません。最新情報をご確認ください</string>
<string name="token_button_unavailability_reason_sell_unavailable">%sの売却は、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます</string>
<string name="token_button_unavailability_reason_staking_unavailable">%sのステーキングは、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます</string>
<string name="token_details_generate_xpub">XPUBを生成する</string>
<string name="token_details_hide_alert_hide">非表示</string>
<string name="token_details_hide_alert_message">このトークンをメイン画面から非表示にします。トークンの管理ページからいつでも再度追加できます。</string>
@ -946,7 +946,7 @@
<string name="wallet_network_group_title">%sネットワーク</string>
<string name="wallet_notification_address_copied">アドレスがクリップボードにコピーされました</string>
<string name="wallet_notification_no_internet">インターネット接続がありません</string>
<string name="wallet_promo_banner_button_title">今すぐ10 %オフで購入</string>
<string name="wallet_promo_banner_button_title">今すぐ10 %%オフで購入</string>
<string name="wallet_promo_banner_description">1.3万種類以上の暗号資産にアクセス。ワンタップで買付、売却、スワップ、ステーキングが可能です。\nバックアップ用に最大3枚のカードを連携できます。</string>
<string name="wallet_promo_banner_title">Tangemウォレットを見る</string>
<string name="wallet_settings_title">ウォレット設定</string>
@ -1019,7 +1019,7 @@
<string name="warning_solana_fee_message">Solanaネットワークが混雑しています。2分以内に取引が完了しない場合は、再度取引を繰り返してください。</string>
<string name="warning_solana_fee_title">Solanaネットワークアラート</string>
<string name="warning_solana_rent_fee_message">Solana ネットワークは 2 日ごとに%1$sのレンタル料を請求します。レンタル料を支払えないアカウントはネットワークから削除されます。アカウントに%2$s以上入金すると、無料で使用できます。</string>
<string name="warning_some_networks_unreachable_message">現在、一部のネットワークにアクセスできません。しばらくしてからもう一度お試しください。</string>
<string name="warning_some_networks_unreachable_message">下にスワイプして更新するか、後でもう一度お試しください。</string>
<string name="warning_some_networks_unreachable_title">一部のネットワークにアクセスできません</string>
<string name="warning_testnet_card_message">これはテストネットカードです。取引処理はできませんので、テストおよび開発目的でのみご利用ください。</string>
<string name="warning_testnet_card_title">テスト目的のみ</string>

View file

@ -581,7 +581,7 @@
<string name="onramp_no_available_providers">Для данной валюты нет доступных провайдеров</string>
<string name="onramp_pay_with">Оплата с</string>
<string name="onramp_redirecting_to_provider_subtitle">Вы сможете завершить транзакцию через сервис стороннего провайдера, %s</string>
<string name="onramp_redirecting_to_provider_title">Переход к %s ...</string>
<string name="onramp_redirecting_to_provider_title">Переход к %s...</string>
<string name="onramp_residency_bottomsheet_country_not_supported">Наши сервисы недоступны в данной стране</string>
<string name="onramp_settings_title">Настройки</string>
<string name="onramp_via">Через</string>
@ -966,7 +966,7 @@
<string name="wallet_notification_no_internet">Нет соединения с интернетом</string>
<string name="wallet_promo_banner_button_title">Получить с 10%% скидкой</string>
<string name="wallet_promo_banner_description">Получите доступ к более чем 13 000 криптовалют. Покупайте, продавайте, обменяйте и стейкайте в один клик. Свяжите до трех карт для резервного копирования. </string>
<string name="wallet_promo_banner_title">Откройте Tangem Wallet </string>
<string name="wallet_promo_banner_title">Откройте Tangem Wallet</string>
<string name="wallet_settings_title">Настройки кошелька</string>
<string name="wallet_title">Tangem</string>
<string name="warning_access_denied_message">Используйте %s или отсканируйте карту/кольцо, чтобы разблокировать доступ к вашему кошельку</string>

View file

@ -294,6 +294,9 @@
<string name="express_provider_not_available">Unavailable for this pair</string>
<string name="express_provider_permission_needed">Permission Required</string>
<string name="express_provider_recommended">Recommended</string>
<string name="express_status_bought">Bought %s</string>
<string name="express_status_buying">Buying %s</string>
<string name="express_status_buying_active">Buying %s...</string>
<string name="express_token_list_empty_search">No tokens found. Please try another request</string>
<string name="express_transaction_id">ID: %s</string>
<string name="express_transaction_id_copied">Transaction ID copied</string>

View file

@ -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<Params, OnrampSuccessComponent>
}

View file

@ -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 */

View file

@ -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

View file

@ -0,0 +1,5 @@
package com.tangem.features.onramp.success.entity
interface OnrampSuccessClickIntents {
fun goToProviderClick(providerLink: String)
}

View file

@ -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()
}

View file

@ -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<OnrampStatus, OnrampSuccessComponentUM> {
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
}
}

View file

@ -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,
),
),
),
)
}

View file

@ -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<OnrampSuccessComponentUM> = MutableStateFlow(
value = OnrampSuccessComponentUM.Loading,
)
val state: StateFlow<OnrampSuccessComponentUM> 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)
}
}
}
}

View file

@ -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<OnrampSuccessComponentUM> {
override val values: Sequence<OnrampSuccessComponentUM>
get() = sequenceOf(
OnrampSuccessComponentUMPreviewData.contentState,
)
}
// endregion