Updated on 2026-08-14
This commit is contained in:
commit
7dbefe9430
54 changed files with 1046 additions and 475 deletions
|
|
@ -49,8 +49,6 @@ object TradeCryptoMiddleware {
|
|||
is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId)
|
||||
is TradeCryptoAction.Buy -> proceedBuyAction(state, action)
|
||||
is TradeCryptoAction.Sell -> proceedSellAction(action)
|
||||
is TradeCryptoAction.SendToken -> handleNewSendToken(action = action)
|
||||
is TradeCryptoAction.SendCoin -> handleNewSendCoin(action = action)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -166,37 +164,4 @@ object TradeCryptoMiddleware {
|
|||
transactionId = transactionId,
|
||||
)?.let { store.dispatchOpenUrl(it) }
|
||||
}
|
||||
|
||||
private fun handleNewSendToken(action: TradeCryptoAction.SendToken) {
|
||||
handleNewSend(
|
||||
userWalletId = action.userWallet.walletId,
|
||||
txInfo = action.transactionInfo,
|
||||
currency = action.tokenCurrency,
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleNewSendCoin(action: TradeCryptoAction.SendCoin) {
|
||||
handleNewSend(
|
||||
userWalletId = action.userWallet.walletId,
|
||||
txInfo = action.transactionInfo,
|
||||
currency = action.coinStatus.currency,
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleNewSend(
|
||||
userWalletId: UserWalletId,
|
||||
txInfo: TradeCryptoAction.TransactionInfo?,
|
||||
currency: CryptoCurrency,
|
||||
) {
|
||||
val route = AppRoute.Send(
|
||||
currency = currency,
|
||||
userWalletId = userWalletId,
|
||||
transactionId = txInfo?.transactionId,
|
||||
destinationAddress = txInfo?.destinationAddress,
|
||||
amount = txInfo?.amount,
|
||||
tag = txInfo?.tag,
|
||||
)
|
||||
|
||||
store.dispatchNavigationAction { push(route) }
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ dependencies {
|
|||
implementation(deps.compose.navigation)
|
||||
implementation(deps.compose.navigation.hilt)
|
||||
implementation(deps.compose.coil)
|
||||
implementation(deps.compose.constraintLayout)
|
||||
|
||||
/** Deps */
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
|
|
@ -34,6 +35,7 @@ dependencies {
|
|||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.transaction.models)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.onramp.models)
|
||||
|
||||
implementation(deps.tangem.card.core)
|
||||
implementation(deps.tangem.blockchain) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express
|
||||
package com.tangem.common.ui.expressStatus
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -8,16 +8,16 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.inputrow.InputRowApprox
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
internal fun ExpressEstimate(
|
||||
fun ExpressEstimate(
|
||||
timestamp: TextReference,
|
||||
fromTokenIconState: CurrencyIconState,
|
||||
toTokenIconState: CurrencyIconState,
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express
|
||||
package com.tangem.common.ui.expressStatus
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.widget.Toast
|
||||
|
|
@ -29,7 +29,7 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
@Composable
|
||||
internal fun ExpressProvider(
|
||||
fun ExpressProvider(
|
||||
providerName: TextReference,
|
||||
providerType: TextReference,
|
||||
providerTxId: String?,
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.common.ui.expressStatus
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
data class ExpressStatusBottomSheetConfig(
|
||||
val value: ExpressTransactionStateUM,
|
||||
) : TangemBottomSheetConfigContent
|
||||
|
||||
@Composable
|
||||
fun ExpressStatusBottomSheet(config: TangemBottomSheetConfig) {
|
||||
TangemBottomSheet(
|
||||
config = config,
|
||||
containerColor = TangemTheme.colors.background.tertiary,
|
||||
) { content: ExpressStatusBottomSheetConfig ->
|
||||
when (val state = content.value) {
|
||||
is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express
|
||||
package com.tangem.common.ui.expressStatus
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.DrawableRes
|
||||
|
|
@ -18,6 +18,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.constraintlayout.compose.*
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.core.ui.components.atoms.text.EllipsisText
|
||||
import com.tangem.core.ui.components.atoms.text.TextEllipsis
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
||||
|
|
@ -27,7 +28,6 @@ import com.tangem.core.ui.extensions.resolveReference
|
|||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
|
||||
@Suppress("DestructuringDeclarationWithTooManyEntries", "LongMethod", "LongParameterList")
|
||||
@Composable
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express
|
||||
package com.tangem.common.ui.expressStatus
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.common.ui.R
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateIconUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
|
||||
internal fun LazyListScope.expressTransactionsItems(
|
||||
fun LazyListScope.expressTransactionsItems(
|
||||
expressTxs: PersistentList<ExpressTransactionStateUM>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.onramp
|
||||
package com.tangem.common.ui.expressStatus
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -8,8 +8,7 @@ import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBlock
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusNotificationBlock
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH10
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
|
|
@ -17,12 +16,9 @@ import com.tangem.core.ui.components.SpacerH16
|
|||
import com.tangem.core.ui.components.SpacerH24
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressEstimate
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressProvider
|
||||
|
||||
@Composable
|
||||
internal fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM) {
|
||||
fun OnrampStatusBottomSheetContent(state: ExpressTransactionStateUM.OnrampUM) {
|
||||
Column(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16)) {
|
||||
SpacerH10()
|
||||
Text(
|
||||
|
|
@ -1,31 +1,13 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.express
|
||||
package com.tangem.common.ui.expressStatus.state
|
||||
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressStatusUM
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.onramp.model.OnrampStatus
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapProvider
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal sealed class ExpressTransactionStateUM {
|
||||
interface ExpressTransactionStateUM {
|
||||
|
||||
abstract val info: ExpressTransactionStateInfoUM
|
||||
|
||||
data class ExchangeUM(
|
||||
override val info: ExpressTransactionStateInfoUM,
|
||||
val provider: SwapProvider,
|
||||
val activeStatus: ExchangeStatus?,
|
||||
val statuses: ImmutableList<ExchangeStatusState>,
|
||||
val notification: ExchangeStatusNotifications? = null,
|
||||
val showProviderLink: Boolean,
|
||||
val isRefundTerminalStatus: Boolean,
|
||||
val fromCryptoCurrency: CryptoCurrency,
|
||||
val toCryptoCurrency: CryptoCurrency,
|
||||
) : ExpressTransactionStateUM()
|
||||
val info: ExpressTransactionStateInfoUM
|
||||
|
||||
data class OnrampUM(
|
||||
override val info: ExpressTransactionStateInfoUM,
|
||||
|
|
@ -34,10 +16,10 @@ internal sealed class ExpressTransactionStateUM {
|
|||
val providerType: String, // todo onramp fix after SwapProvider moved to own module
|
||||
val activeStatus: OnrampStatus.Status,
|
||||
val fromCurrencyCode: String,
|
||||
) : ExpressTransactionStateUM()
|
||||
) : ExpressTransactionStateUM
|
||||
}
|
||||
|
||||
internal data class ExpressTransactionStateInfoUM(
|
||||
data class ExpressTransactionStateInfoUM(
|
||||
val title: TextReference,
|
||||
val status: ExpressStatusUM,
|
||||
val notification: NotificationUM?,
|
||||
|
|
@ -49,7 +31,6 @@ internal data class ExpressTransactionStateInfoUM(
|
|||
val onGoToProviderClick: (String) -> Unit,
|
||||
val onClick: () -> Unit,
|
||||
val iconState: ExpressTransactionStateIconUM,
|
||||
|
||||
val toAmount: TextReference,
|
||||
val toFiatAmount: TextReference?,
|
||||
val toAmountSymbol: String,
|
||||
|
|
@ -61,7 +42,7 @@ internal data class ExpressTransactionStateInfoUM(
|
|||
val fromCurrencyIcon: CurrencyIconState,
|
||||
)
|
||||
|
||||
internal enum class ExpressTransactionStateIconUM {
|
||||
enum class ExpressTransactionStateIconUM {
|
||||
Warning,
|
||||
Error,
|
||||
None,
|
||||
|
|
@ -583,6 +583,8 @@
|
|||
<string name="onramp_choose_provider_title_hint">Der Kauf von Kryptowährungen bei Tangem wird von Drittanbietern zu deren Bedingungen durchgeführt.</string>
|
||||
<string name="onramp_country_search">Suche nach Land</string>
|
||||
<string name="onramp_country_unavailable">Nicht verfügbar</string>
|
||||
<string name="onramp_currency_other">Andere Währungen</string>
|
||||
<string name="onramp_currency_popular">Beliebte Fiats</string>
|
||||
<string name="onramp_currency_search">Suche nach Währung</string>
|
||||
<string name="onramp_legal">Durch die Nutzung der Onramp-Funktionalität stimmst Du den %1$s und %2$s des Anbieters zu.</string>
|
||||
<string name="onramp_max_amount_restriction">Der Kaufbetrag sollte nicht höher sein als %s</string>
|
||||
|
|
|
|||
|
|
@ -6,10 +6,15 @@
|
|||
<string name="action_buttons_service_loading_alert_message">Esto puede tardar unos segundos. Inténtelo de nuevo más tarde.</string>
|
||||
<string name="action_buttons_service_loading_alert_title">Los datos aún no se han cargado.</string>
|
||||
<string name="action_buttons_something_wrong_alert_message">La acción no está disponible actualmente. Inténtalo de nuevo más tarde o actualiza los datos deslizando el dedo hacia abajo en la pantalla.</string>
|
||||
<string name="action_buttons_something_wrong_alert_title">No disponible</string>
|
||||
<string name="action_buttons_swap_choose_token">Elige el token</string>
|
||||
<string name="action_buttons_swap_empty_search_message">¿No encuentras el token en tu billetera? Consulte los mercados para encontrarlo y agregarlo al intercambio</string>
|
||||
<string name="action_buttons_swap_empty_search_message">¿No encuentra el token en tu billetera? Consulte los mercados para encontrarlo y agregarlo al intercambio</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_message">No hay tokens disponibles para intercambiar con el token seleccionado. Por favor elige otro.</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_title">No hay par disponible</string>
|
||||
<string name="action_buttons_swap_no_tokens_added_alert_message">Para utilizar la función de intercambio, su billetera debe contener al menos 2 tokens</string>
|
||||
<string name="action_buttons_swap_no_tokens_added_alert_title">Añada tokens</string>
|
||||
<string name="action_buttons_swap_not_enough_tokens_alert_message">Sólo tienes 1 token añadido a tu portafolio. Para utilizar la función de intercambio, necesita añadir al menos 2 tokens</string>
|
||||
<string name="action_buttons_swap_not_enough_tokens_alert_title">Añada tokens</string>
|
||||
<string name="action_buttons_you_want_to_receive">Seleccione el token que desea recibir</string>
|
||||
<string name="action_buttons_you_want_to_swap">Seleccione el token que desea intercambiar</string>
|
||||
<string name="add_custom_token_choose_network">Elige red</string>
|
||||
|
|
@ -25,7 +30,7 @@
|
|||
<string name="alert_manage_tokens_unsupported_blockchain_by_card_message">El seleccionado no admite la red %1$s</string>
|
||||
<string name="alert_manage_tokens_unsupported_curve_message">Para activar el %1$s cifrado criptográfico de la blockchain, necesitarás reiniciar la billetera a los ajustes de fábrica. Por favor, retire sus fondos antes de hacerlo para asegurarte de no perderlos, y luego complete el proceso de reinicio. El acceso a la billetera actual no será posible después del reinicio.</string>
|
||||
<string name="alert_manage_tokens_unsupported_message">Esta tarjeta no admite %1$s tokens de red debido a una limitación del firmware.</string>
|
||||
<string name="alert_troubleshooting_scan_card_title">¿Tiene dificultades para escanear su tarjeta?</string>
|
||||
<string name="alert_troubleshooting_scan_card_title">¿Tiene dificultades para escanear su tarjeta/anillo?</string>
|
||||
<string name="alert_unsupported_card">Esta tarjeta no está diseñada para funcionar con Tangem</string>
|
||||
<string name="app_settings_default_fee">Tarifa por defecto</string>
|
||||
<string name="app_settings_default_fee_footer">Habilite las Tarifas predeterminadas para establecer automáticamente las tarifas de transacción y omitir la página de Tarifas al enviar fondos. Siempre puede volver a esta página si es necesario.</string>
|
||||
|
|
@ -65,10 +70,10 @@
|
|||
<string name="card_settings_change_access_code_footer">El código de acceso se cambiará solo en esta tarjeta o este anillo</string>
|
||||
<string name="card_settings_completed_reset_alert_message">Todas las tarjetas de la billetera seleccionada se han restablecido a la configuración de fábrica. Ahora puedes crear una nueva billetera.</string>
|
||||
<string name="card_settings_completed_reset_alert_title">Reinicio completado</string>
|
||||
<string name="card_settings_continue_reset_alert_message">¿Quiere restablecer la siguiente tarjeta en esta billetera?</string>
|
||||
<string name="card_settings_continue_reset_alert_message">¿Quiere restablecer el siguiente dispositivo en esta billetera?</string>
|
||||
<string name="card_settings_continue_reset_alert_title">Reinicio de tarjeta</string>
|
||||
<string name="card_settings_interrupted_reset_alert_message">Recomendamos completar el proceso de reinicio para todas las tarjetas en esta billetera.</string>
|
||||
<string name="card_settings_interrupted_reset_alert_title">No ha reseteado todas sus tarjetas</string>
|
||||
<string name="card_settings_interrupted_reset_alert_title">No ha reseteado todas sus dispositivos Tangem</string>
|
||||
<string name="card_settings_reset_card_to_factory">Restablecer a ajustes de fábrica</string>
|
||||
<string name="card_settings_security_mode">Modo de seguridad</string>
|
||||
<string name="card_settings_title">Ajustes del dispositivo</string>
|
||||
|
|
@ -266,6 +271,9 @@
|
|||
<string name="express_exchange_notification_refund_title">El monto fue reembolsado en %1$s (red %2$s)</string>
|
||||
<string name="express_exchange_notification_verification_text">Visite el sitio web del proveedor para la verificación</string>
|
||||
<string name="express_exchange_notification_verification_title">Verificación KYC requerida por el proveedor</string>
|
||||
<string name="express_exchange_status_bought">Comprado</string>
|
||||
<string name="express_exchange_status_buying">Comprando</string>
|
||||
<string name="express_exchange_status_buying_active">Comprando...</string>
|
||||
<string name="express_exchange_status_canceled">Cancelado</string>
|
||||
<string name="express_exchange_status_confirmed">Confirmado</string>
|
||||
<string name="express_exchange_status_confirming">Esperando confirmación</string>
|
||||
|
|
@ -305,15 +313,15 @@
|
|||
<string name="express_token_list_empty_search">No se encontraron fichas. Por favor intenta con otra solicitud</string>
|
||||
<string name="express_transaction_id">ID : %s</string>
|
||||
<string name="express_transaction_id_copied">ID de transacción copiado</string>
|
||||
<string name="exсhange_token_description">Convierta una de sus monedas a otra</string>
|
||||
<string name="exсhange_token_description">Convierta una de sus activos de su portafolio por este token</string>
|
||||
<string name="feedback_data_collection_message">La siguiente información es opcional. Puede borrarla si no quiere compartirla.</string>
|
||||
<string name="feedback_preface_rate_negative">Cuéntenos qué funciones echa de menos y trataremos de ayudarle.</string>
|
||||
<string name="feedback_preface_scan_failed">Por favor, díganos qué tarjeta tiene</string>
|
||||
<string name="feedback_preface_scan_failed">Por favor, díganos qué tarjeta o anillo tiene</string>
|
||||
<string name="feedback_preface_support">Hola equipo de soporte,</string>
|
||||
<string name="feedback_preface_tx_failed">Por favor, cuéntenos más sobre tu problema. Cada pequeño detalle puede ayudar.</string>
|
||||
<string name="feedback_subject_pre_activated_wallet">Billetera previamente activada</string>
|
||||
<string name="feedback_subject_rate_negative">Mis recomendaciones</string>
|
||||
<string name="feedback_subject_scan_failed">No se puede escanear una tarjeta</string>
|
||||
<string name="feedback_subject_scan_failed">No se puede escanear una tarjeta/anillo</string>
|
||||
<string name="feedback_subject_support">Comentarios</string>
|
||||
<string name="feedback_subject_support_tangem">Comentarios en Tangem</string>
|
||||
<string name="feedback_subject_tx_failed">No se puede completar una transacción</string>
|
||||
|
|
@ -329,14 +337,14 @@
|
|||
<string name="home_button_order">Ordenar </string>
|
||||
<string name="home_button_scan">Escanee</string>
|
||||
<string name="information_generated_with_ai">Esta información fue generada con IA.\nPulse aquí si encuentra algún error.</string>
|
||||
<string name="initial_message_change_access_code_body">Toque para cambiar la contraseña</string>
|
||||
<string name="initial_message_change_access_code_body">Para cambiar el código de acceso coloque la tarjeta o el anillo como se muestra arriba y no lo retire hasta el fin de la operación</string>
|
||||
<string name="initial_message_change_passcode_body">Toque para cambiar la contraseña</string>
|
||||
<string name="initial_message_create_wallet_body">Para crear la billetera, toque la tarjeta como se muestra arriba y no la retire hasta el final de la operación</string>
|
||||
<string name="initial_message_create_wallet_body">Para crear la billetera, coloque la tarjeta o el anillo como se muestra arriba y no la retire hasta el fin de la operación</string>
|
||||
<string name="initial_message_create_wallet_body_ring">Para crear la billetera, ponga el anillo como se muestra arriba y manténgala hasta que termine la operación.</string>
|
||||
<string name="initial_message_reset_backup_card_header">Toque la tarjeta no. %s de la billetera</string>
|
||||
<string name="initial_message_scan_header">Coloque para escanear</string>
|
||||
<string name="initial_message_sign_header">Toque para firmar</string>
|
||||
<string name="initial_message_tap_header">Coloque la tarjeta</string>
|
||||
<string name="initial_message_tap_header">Coloque la tarjeta o el anillo</string>
|
||||
<string name="key_invalidated_warning_description">Ha actualizado la biometría, escanee su tarjeta o anillo para entrar</string>
|
||||
<string name="koinos_insufficient_balance_to_send_koin_description">Su saldo debe ser mayor que el valor de la tarifa para hacer una transferencia</string>
|
||||
<string name="koinos_insufficient_balance_to_send_koin_title">Saldo insuficiente</string>
|
||||
|
|
@ -379,7 +387,7 @@
|
|||
<string name="markets_add_to_my_portfolio_unavailable_description">Este activo no está disponible en la billetera</string>
|
||||
<string name="markets_add_to_my_portfolio_unavailable_for_wallet_description">Este activo no está disponible para esta billetera</string>
|
||||
<string name="markets_add_to_portfolio_button">Añadir al portafolio</string>
|
||||
<string name="markets_add_token">Agregue un token</string>
|
||||
<string name="markets_add_token">Añadir</string>
|
||||
<string name="markets_available_networks">Redes disponibles</string>
|
||||
<string name="markets_common_my_portfolio">Mi portafolio</string>
|
||||
<string name="markets_common_title">Mercado</string>
|
||||
|
|
@ -391,7 +399,7 @@
|
|||
<string name="markets_quick_actions">Acciones rápidas</string>
|
||||
<string name="markets_search_header_title">Busque en el mercado</string>
|
||||
<string name="markets_search_result_title">Resultado</string>
|
||||
<string name="markets_search_see_tokens_under_100k">Ver tokens con una capitalización de mercado de menos de $100,000</string>
|
||||
<string name="markets_search_see_tokens_under_100k">Ver tokens con una capitalización de mercado inferior a 100,000$</string>
|
||||
<string name="markets_search_show_tokens">Mostrar tokens</string>
|
||||
<string name="markets_search_token_no_result_title">Sin resultado</string>
|
||||
<string name="markets_select_network">Seleccione una red</string>
|
||||
|
|
@ -478,9 +486,9 @@
|
|||
<string name="nfc_error_unavailable">NFC no está disponible en su dispositivo</string>
|
||||
<string name="onboarding_access_code_feature_1_description">Deba configurar un único código de acceso para proteger todss sus dispositivos.</string>
|
||||
<string name="onboarding_access_code_feature_1_title">Proteger</string>
|
||||
<string name="onboarding_access_code_feature_2_description">Puede configurar un código de acceso individual en cada tarjeta más tarde</string>
|
||||
<string name="onboarding_access_code_feature_2_description">Puede configurar un código de acceso individual en cada tarjeta más adelante</string>
|
||||
<string name="onboarding_access_code_feature_2_title">Personalizar</string>
|
||||
<string name="onboarding_access_code_feature_3_description">El código de acceso se puede recuperar con una tarjeta o un anillo vinculada(o). No guarde todas las tarjetas en un mismo lugar.</string>
|
||||
<string name="onboarding_access_code_feature_3_description">El código de acceso se puede recuperar con una tarjeta o un anillo vinculada(o). No guarde todos los dispositivos en el mismo lugar.</string>
|
||||
<string name="onboarding_access_code_feature_3_title">Restaurar</string>
|
||||
<string name="onboarding_access_code_hint">Elige cualquier palabra, frase o número que quieras como su código de acceso</string>
|
||||
<string name="onboarding_access_code_intro_title">Crear un código de acceso</string>
|
||||
|
|
@ -491,12 +499,12 @@
|
|||
<string name="onboarding_activation_error_message">Por favor repita la operación. La tarjeta se restablecerá a la configuración de fábrica.</string>
|
||||
<string name="onboarding_activation_error_title">Error de activación</string>
|
||||
<string name="onboarding_add_tokens">Agregar tokens</string>
|
||||
<string name="onboarding_alert_message_not_max_backup_cards_added">Ha agregado una tarjeta de backup. Cuando el proceso de backup finalice, no podrá añadir más tarjetas de backup. Si tiene otra tarjeta, agréguela al backup. ¿Quiere continuar el proceso de backup?</string>
|
||||
<string name="onboarding_alert_message_not_max_backup_cards_added">Ha agregado una tarjeta/anillo de backup. Cuando el proceso de backup finalice, no podrá añadir más dispositivos de backup. Si tiene otra tarjeta o anillo, agréguela al backup. ¿Quiere continuar el proceso de backup?</string>
|
||||
<string name="onboarding_backup_exit_warning">El proceso de backup está parcialmente completo. No puede salir ahora.</string>
|
||||
<string name="onboarding_bottom_sheet_passphrase_description">La frase de contraseña es una característica de seguridad avanzada utilizada por las billeteras criptográficas. Agrega una palabra o frase adicional de su elección a su frase de recuperación ya existente para desbloquear un conjunto completamente nuevo de direcciones.</string>
|
||||
<string name="onboarding_button_add_backup_card">Agregar una tarjeta o un anillo de backup</string>
|
||||
<string name="onboarding_button_backup_card">Escanee la tarjeta</string>
|
||||
<string name="onboarding_button_backup_card_format">Escanee la tarjeta no. %d</string>
|
||||
<string name="onboarding_button_backup_card_format">Escanee la tarjeta núm. %d</string>
|
||||
<string name="onboarding_button_backup_now">Hacer backup ahora</string>
|
||||
<string name="onboarding_button_backup_origin">Escanee la tarjeta principal</string>
|
||||
<string name="onboarding_button_backup_ring">Escanee el anillo</string>
|
||||
|
|
@ -506,7 +514,7 @@
|
|||
<string name="onboarding_button_scan_origin_card">Escanee la tarjeta principal</string>
|
||||
<string name="onboarding_button_skip_backup">Finalizar más tarde</string>
|
||||
<string name="onboarding_button_what_does_it_mean">¿Cómo funciona?</string>
|
||||
<string name="onboarding_create_wallet_body">Vamos a generar todas las claves en su tarjeta y crear una billetera segura</string>
|
||||
<string name="onboarding_create_wallet_body">Vamos a generar todas las claves en su tarjeta o anillos y crear una billetera segura</string>
|
||||
<string name="onboarding_create_wallet_button_create_wallet">Crear una billetera</string>
|
||||
<string name="onboarding_create_wallet_header">Crear una billetera</string>
|
||||
<string name="onboarding_create_wallet_options_button_options">Otras opciones</string>
|
||||
|
|
@ -539,23 +547,23 @@
|
|||
<string name="onboarding_seed_phrase_intro_legacy">Legado</string>
|
||||
<string name="onboarding_seed_user_validation_message">Para comprobar si has escrito correctamente su seed phrase, por favor introduce las palabras 2ª, 7ª y 11ª</string>
|
||||
<string name="onboarding_seed_user_validation_title">Bien, vamos a comprobar</string>
|
||||
<string name="onboarding_subtitle_no_backup_cards">Para iniciar el proceso de backup, agregue hasta dos tarjetas de backup.</string>
|
||||
<string name="onboarding_subtitle_one_backup_card">Puede agregar una tarjeta más o finalizar el proceso de backup</string>
|
||||
<string name="onboarding_subtitle_no_backup_cards">Para iniciar el proceso de backup, agregue hasta dos tarjetas/anillos de backup.</string>
|
||||
<string name="onboarding_subtitle_one_backup_card">Puede agregar una tarjeta/anillo más o finalizar el proceso de backup</string>
|
||||
<string name="onboarding_subtitle_scan_backup_card_format">Prepare su tarjeta de backup con el no.%s</string>
|
||||
<string name="onboarding_subtitle_scan_primary">Escanee la tarjeta principal o el anillo para iniciar el proceso de backup.</string>
|
||||
<string name="onboarding_subtitle_scan_primary_card_format">Prepare la tarjeta principal con el número %s</string>
|
||||
<string name="onboarding_subtitle_scan_ring">Prepare el anillo y presione el botón de escaneo a continuación</string>
|
||||
<string name="onboarding_subtitle_success_tangem_wallet_onboarding">Su tarjeta billetera está configurada y lista para usar.</string>
|
||||
<string name="onboarding_subtitle_two_backup_cards">Número máximo de tarjetas añadido. Finalice el proceso de backup.</string>
|
||||
<string name="onboarding_title">Activando tarjeta</string>
|
||||
<string name="onboarding_subtitle_success_tangem_wallet_onboarding">Su billetera está configurada y lista para usar.</string>
|
||||
<string name="onboarding_subtitle_two_backup_cards">Número máximo de dispositivos añadido. Finalice el proceso de backup.</string>
|
||||
<string name="onboarding_title">Activando billetera</string>
|
||||
<string name="onboarding_title_backup_card">Tarjeta de backup</string>
|
||||
<string name="onboarding_title_backup_card_format">Tarjeta de backup no.%d</string>
|
||||
<string name="onboarding_title_backup_ring">Anillo de backup</string>
|
||||
<string name="onboarding_title_no_backup_cards">Ningunos dispositivos de backup</string>
|
||||
<string name="onboarding_title_notifications">Notificaciones</string>
|
||||
<string name="onboarding_title_one_backup_card">Una tarjeta de backup agregada</string>
|
||||
<string name="onboarding_title_one_backup_card">Un dispositivo de backup agregado</string>
|
||||
<string name="onboarding_title_scan_origin_card">Prepare su tarjeta</string>
|
||||
<string name="onboarding_title_two_backup_cards">Dos tarjetas de backup agregadas</string>
|
||||
<string name="onboarding_title_two_backup_cards">Dos dispositivos de backup agregados</string>
|
||||
<string name="onboarding_top_up_body">Para empezar, simplemente recargue la billetera con cualquier cantidad</string>
|
||||
<string name="onboarding_top_up_body_no_account_error">Para empezar, simplemente recargue la billetera con más de %1$s %2$s</string>
|
||||
<string name="onboarding_top_up_button_but_crypto">Comprar cripto</string>
|
||||
|
|
@ -563,7 +571,7 @@
|
|||
<string name="onboarding_topup_title">Activar una billetera</string>
|
||||
<string name="onboarding_twin_exit_warning">El proceso de emparejamiento está parcialmente completo. No puede salir ahora.</string>
|
||||
<string name="onboarding_twins_interrupt_warning">Si el proceso de creación del wallet se interrumpe de alguna manera, tendrá que empezar de nuevo</string>
|
||||
<string name="onboarding_wallet_info_subtitle_first">Puede hacer backup de sus claves en hasta dos tarjetas Tangem Wallet en blanco.</string>
|
||||
<string name="onboarding_wallet_info_subtitle_first">Puede hacer backup de sus claves en hasta dos tarjetas o anillos Tangem no activadas.</string>
|
||||
<string name="onboarding_wallet_info_subtitle_fourth">El código de acceso se puede restaurar con una de las tarjetas de backup.</string>
|
||||
<string name="onboarding_wallet_info_subtitle_second">Todas las tarjetas de backup se pueden usar como funcionales con las claves idénticas.</string>
|
||||
<string name="onboarding_wallet_info_subtitle_third">Podrá establecer un código de acceso para proteger sus billeteras.</string>
|
||||
|
|
@ -572,8 +580,11 @@
|
|||
<string name="onboarding_wallet_info_title_second">Tarjetas idénticas</string>
|
||||
<string name="onboarding_wallet_info_title_third">Código de acceso</string>
|
||||
<string name="onramp_avaiable_with_payment_methods">Disponible con %s</string>
|
||||
<string name="onramp_choose_provider_title_hint">La compra de criptomonedas en Tangem se lleva a cabo utilizando proveedores externos usando sus propios términos.</string>
|
||||
<string name="onramp_country_search">Buscar por país</string>
|
||||
<string name="onramp_country_unavailable">Indisponible</string>
|
||||
<string name="onramp_currency_other">Otras monedas</string>
|
||||
<string name="onramp_currency_popular">Monedas Fiats Populares</string>
|
||||
<string name="onramp_currency_search">Buscar por moneda</string>
|
||||
<string name="onramp_legal">Al utilizar la funcionalidad onramp, acepta %1$s y %2$s del proveedor.</string>
|
||||
<string name="onramp_max_amount_restriction">El monto de la compra no debe ser mayor a %s</string>
|
||||
|
|
@ -604,7 +615,8 @@
|
|||
<string name="receive_bottom_sheet_warning_message">%1$s (%2$s) en la red %3$s</string>
|
||||
<string name="receive_bottom_sheet_warning_message_description">El envío de cualquier otra moneda supondrá una pérdida irreversible.</string>
|
||||
<string name="receive_bottom_sheet_warning_message_full">Envíe solo %s a esta dirección. Enviar cualquier otra moneda resultará en su pérdida irreversible.</string>
|
||||
<string name="receive_token_description">Transfiera fondos desde otra billetera o intercambio</string>
|
||||
<string name="receive_bottom_sheet_warning_title">Envíe solo %1$s en la red %2$s</string>
|
||||
<string name="receive_token_description">Transfiera fondos desde otra billetera o exchange</string>
|
||||
<string name="referral_button_participate">Participar</string>
|
||||
<string name="referral_error_failed_to_load_info">Error al cargar la información sobre el programa de referidos. Por favor, inténtelo de nuevo más tarde.</string>
|
||||
<string name="referral_error_failed_to_load_info_with_reason">Error al cargar la información sobre el programa de referidos. Código de error: %s. Por favor, inténtelo de nuevo más tarde.</string>
|
||||
|
|
@ -806,17 +818,17 @@
|
|||
<string name="staking_revote">Revotar</string>
|
||||
<string name="staking_reward_claiming_auto">Auto</string>
|
||||
<string name="staking_reward_claiming_manual">Manual</string>
|
||||
<string name="staking_reward_schedule_block">Bloquear</string>
|
||||
<string name="staking_reward_schedule_day">Día</string>
|
||||
<string name="staking_reward_schedule_block">Por Bloque</string>
|
||||
<string name="staking_reward_schedule_day">Diario</string>
|
||||
<string name="staking_reward_schedule_each">Cada %s</string>
|
||||
<string name="staking_reward_schedule_each_day">Cada día</string>
|
||||
<string name="staking_reward_schedule_each_days">Cada %s</string>
|
||||
<string name="staking_reward_schedule_each_days">Cada %s días</string>
|
||||
<string name="staking_reward_schedule_each_plural">Cada</string>
|
||||
<string name="staking_reward_schedule_epoch">Época</string>
|
||||
<string name="staking_reward_schedule_epoch">Por Época</string>
|
||||
<string name="staking_reward_schedule_era">Era</string>
|
||||
<string name="staking_reward_schedule_hour">Hora</string>
|
||||
<string name="staking_reward_schedule_month">Mes</string>
|
||||
<string name="staking_reward_schedule_week">Semana</string>
|
||||
<string name="staking_reward_schedule_hour">Cada Hora</string>
|
||||
<string name="staking_reward_schedule_month">Mensual</string>
|
||||
<string name="staking_reward_schedule_week">Semanal</string>
|
||||
<string name="staking_rewards">Recompensas</string>
|
||||
<string name="staking_stake_locked">El stake está bloqueado</string>
|
||||
<string name="staking_stake_more">Hacer más staking</string>
|
||||
|
|
@ -843,7 +855,7 @@
|
|||
<string name="staking_vote_locked">Votar</string>
|
||||
<string name="staking_withdraw">Retirar</string>
|
||||
<string name="staking_your_stakes">Sus stakes</string>
|
||||
<string name="story_awe_description">Almacene sus activos crypto de forma segura manteniendo las claves privadas contenidas en su tarjeta</string>
|
||||
<string name="story_awe_description">Almacene sus criptomonedas de forma segura manteniendo las claves privadas almacenadas en su tarjeta</string>
|
||||
<string name="story_awe_title">Billetera de hardware revolucionaria</string>
|
||||
<string name="story_backup_description">Hasta 3 tarjetas físicas o anillos por billetera</string>
|
||||
<string name="story_backup_title">Backup ultra seguro</string>
|
||||
|
|
@ -857,7 +869,7 @@
|
|||
<string name="swap_give_permission_fee_footer">La red cobrará una tarifa de aprobación de token para verificar que está autorizando el uso de su token para el swap.</string>
|
||||
<string name="swap_promo_text">Intercambie más tokens a mejores tasas directamente en su billetera.</string>
|
||||
<string name="swap_promo_title">¡Nuevo proveedor de intercambio disponible!</string>
|
||||
<string name="swapping_alert_cex_description">El monto incluye:\n• Tarifas del proveedor de servicios\n• Tarifas de red por enviar %s desde el intercambio a la dirección del usuario.</string>
|
||||
<string name="swapping_alert_cex_description">La cantidad incluye:\n• Tarifas del proveedor de servicios\n• Tarifas de red por enviar %s desde el intercambio a la dirección del usuario.</string>
|
||||
<string name="swapping_alert_cex_description_with_slippage">El monto incluye:\n• tarifas del proveedor de servicios\n• tarifas de red por enviar %1$s desde el intercambio a la dirección del usuario. \n\nEl slippage del proveedor puede alcanzar el %2$s</string>
|
||||
<string name="swapping_alert_dex_description">El importe incluye los honorarios del proveedor de servicios.</string>
|
||||
<string name="swapping_alert_dex_description_with_slippage">El importe incluye las tarifas del proveedor de servicios. \n\nEl slippage del proveedor puede alcanzar el %s</string>
|
||||
|
|
@ -920,7 +932,7 @@
|
|||
<string name="twin_error_wrong_twin">Ha escaneado la tarjeta gemela incorrecta. Por favor, intente con otra</string>
|
||||
<string name="twins_onboarding_description_format">Esta que tiene en sus manos y la otra con el número %s.\n\nAmbas tarjetas pueden usarse para extraer fondos de esta billetera.</string>
|
||||
<string name="twins_onboarding_subtitle">Una billetera. Dos tarjetas.</string>
|
||||
<string name="twins_recreate_button_format">Escanee la tarjeta no. %s</string>
|
||||
<string name="twins_recreate_button_format">Escanee la tarjeta núm. %s</string>
|
||||
<string name="twins_recreate_title_creating_wallet">Creando la billetera</string>
|
||||
<string name="twins_recreate_title_format">Escanear tarjeta gemela no. %s</string>
|
||||
<string name="twins_recreate_title_preparing">Preparando la tarjeta</string>
|
||||
|
|
|
|||
|
|
@ -582,6 +582,8 @@
|
|||
<string name="onramp_avaiable_with_payment_methods">Disponible avec %s</string>
|
||||
<string name="onramp_country_search">Recherche par pays</string>
|
||||
<string name="onramp_country_unavailable">Indisponible</string>
|
||||
<string name="onramp_currency_other">Autres devises</string>
|
||||
<string name="onramp_currency_popular">Devises Fiats Populaires</string>
|
||||
<string name="onramp_currency_search">Recherche par devise</string>
|
||||
<string name="onramp_legal">En utilisant la fonctionnalité onramp, vous acceptez %1$s et %2$s du fournisseur</string>
|
||||
<string name="onramp_max_amount_restriction">Le montant de l\'achat ne doit pas dépasser %s</string>
|
||||
|
|
|
|||
|
|
@ -575,6 +575,8 @@
|
|||
<string name="onramp_choose_provider_title_hint">Tangemでの暗号通貨の買付は、サードパーティプロバイダーの条件に基づいて行われます。</string>
|
||||
<string name="onramp_country_search">国で検索</string>
|
||||
<string name="onramp_country_unavailable">利用不可</string>
|
||||
<string name="onramp_currency_other">その他の通貨</string>
|
||||
<string name="onramp_currency_popular">人気の法定通貨</string>
|
||||
<string name="onramp_currency_search">通貨で検索</string>
|
||||
<string name="onramp_legal">オンランプ機能を使用することにより、プロバイダの%1$sおよび%2$sに同意するものとします</string>
|
||||
<string name="onramp_max_amount_restriction">買付金額は%s以下にしてください</string>
|
||||
|
|
|
|||
|
|
@ -597,6 +597,8 @@
|
|||
<string name="onramp_choose_provider_title_hint">Tangem предоставляет доступ к покупке через сторонних провайдеров в соответствии с их правилами</string>
|
||||
<string name="onramp_country_search">Поиск по стране</string>
|
||||
<string name="onramp_country_unavailable">Недоступно</string>
|
||||
<string name="onramp_currency_other">Другие валюты</string>
|
||||
<string name="onramp_currency_popular">Популярные фиаты</string>
|
||||
<string name="onramp_currency_search">Поиск по валюте</string>
|
||||
<string name="onramp_legal">Пользуясь сервисом покупки, вы соглашаетесь с %1$s и %2$s</string>
|
||||
<string name="onramp_max_amount_restriction">Сумма покупки не может быть больше, чем %s</string>
|
||||
|
|
|
|||
|
|
@ -599,6 +599,8 @@
|
|||
<string name="onramp_choose_provider_title_hint">Tangem забезпечує доступ к покупці через сторонніх провайдерів згідно з їхніми умовами</string>
|
||||
<string name="onramp_country_search">Пошук за країною</string>
|
||||
<string name="onramp_country_unavailable">Недоступно</string>
|
||||
<string name="onramp_currency_other">Інші валюти</string>
|
||||
<string name="onramp_currency_popular">Популярні фіати</string>
|
||||
<string name="onramp_currency_search">Пошук по валюті</string>
|
||||
<string name="onramp_legal">Використовуючи сервіс покупки, ви погоджуєтесь з %1$s та %2$s</string>
|
||||
<string name="onramp_max_amount_restriction">Сума покупки не може бути більше ніж %s</string>
|
||||
|
|
|
|||
|
|
@ -583,6 +583,8 @@
|
|||
<string name="onramp_choose_provider_title_hint">Buying crypto in Tangem is powered by third-party providers on their terms.</string>
|
||||
<string name="onramp_country_search">Search by country</string>
|
||||
<string name="onramp_country_unavailable">Unavailable</string>
|
||||
<string name="onramp_currency_other">Other currencies</string>
|
||||
<string name="onramp_currency_popular">Popular Fiats</string>
|
||||
<string name="onramp_currency_search">Search by currency</string>
|
||||
<string name="onramp_legal">By using onramp functionality, you agree with provider’s %1$s and %2$s</string>
|
||||
<string name="onramp_max_amount_restriction">The purchase amount should be no more than %s</string>
|
||||
|
|
|
|||
|
|
@ -249,6 +249,7 @@ internal class DefaultOnrampRepository(
|
|||
maxFromAmount = convertToAmount(response.maxFromAmount, cryptoCurrency),
|
||||
paymentMethod = paymentMethod,
|
||||
provider = provider,
|
||||
countryCode = response.countryCode,
|
||||
)
|
||||
},
|
||||
onError = { error ->
|
||||
|
|
@ -257,6 +258,7 @@ internal class DefaultOnrampRepository(
|
|||
paymentMethod = paymentMethod,
|
||||
provider = provider,
|
||||
fromOnrampAmount = fromOnrampAmount,
|
||||
countryCode = country.code,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
@ -454,6 +456,7 @@ internal class DefaultOnrampRepository(
|
|||
paymentMethod: OnrampPaymentMethod,
|
||||
provider: OnrampProvider,
|
||||
fromOnrampAmount: OnrampAmount,
|
||||
countryCode: String,
|
||||
) = if (error is ApiResponseError.HttpException) {
|
||||
val onrampError = onrampErrorConverter.convert(value = error.errorBody.orEmpty())
|
||||
if (onrampError is OnrampError.AmountError) {
|
||||
|
|
@ -462,6 +465,7 @@ internal class DefaultOnrampRepository(
|
|||
provider = provider,
|
||||
fromAmount = fromOnrampAmount,
|
||||
error = onrampError,
|
||||
countryCode = countryCode,
|
||||
)
|
||||
} else {
|
||||
Timber.w(error, "Unable to fetch onramp quotes for ${provider.id}. $error")
|
||||
|
|
@ -470,6 +474,7 @@ internal class DefaultOnrampRepository(
|
|||
provider = provider,
|
||||
fromAmount = fromOnrampAmount,
|
||||
error = onrampError,
|
||||
countryCode = countryCode,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@ sealed class OnrampQuote {
|
|||
abstract val paymentMethod: OnrampPaymentMethod
|
||||
abstract val provider: OnrampProvider
|
||||
abstract val fromAmount: OnrampAmount
|
||||
abstract val countryCode: String
|
||||
|
||||
data class Data(
|
||||
override val paymentMethod: OnrampPaymentMethod,
|
||||
override val provider: OnrampProvider,
|
||||
override val fromAmount: OnrampAmount,
|
||||
override val countryCode: String,
|
||||
val toAmount: OnrampAmount,
|
||||
val minFromAmount: OnrampAmount,
|
||||
val maxFromAmount: OnrampAmount,
|
||||
|
|
@ -21,6 +23,7 @@ sealed class OnrampQuote {
|
|||
override val paymentMethod: OnrampPaymentMethod,
|
||||
override val provider: OnrampProvider,
|
||||
override val fromAmount: OnrampAmount,
|
||||
override val countryCode: String,
|
||||
val error: OnrampError.AmountError,
|
||||
) : OnrampQuote()
|
||||
|
||||
|
|
@ -28,6 +31,7 @@ sealed class OnrampQuote {
|
|||
override val paymentMethod: OnrampPaymentMethod,
|
||||
override val provider: OnrampProvider,
|
||||
override val fromAmount: OnrampAmount,
|
||||
override val countryCode: String,
|
||||
val error: OnrampError,
|
||||
) : OnrampQuote()
|
||||
}
|
||||
|
|
@ -11,9 +11,9 @@ class GetOnrampStatusUseCase(
|
|||
private val errorResolver: OnrampErrorResolver,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(externalTxId: String): Either<OnrampError, OnrampStatus> {
|
||||
suspend operator fun invoke(txId: String): Either<OnrampError, OnrampStatus> {
|
||||
return Either.catch {
|
||||
onrampRepository.getStatus(externalTxId)
|
||||
onrampRepository.getStatus(txId)
|
||||
}.mapLeft(errorResolver::resolve)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
package com.tangem.domain.tokens.legacy
|
||||
|
||||
import com.tangem.domain.onramp.model.OnrampSource
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import org.rekotlin.Action
|
||||
import java.math.BigDecimal
|
||||
|
||||
sealed class TradeCryptoAction : Action {
|
||||
|
||||
|
|
@ -23,29 +21,4 @@ sealed class TradeCryptoAction : Action {
|
|||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val appCurrencyCode: String,
|
||||
) : TradeCryptoAction()
|
||||
|
||||
@Deprecated("Use AppRoute instead")
|
||||
data class SendToken(
|
||||
val userWallet: UserWallet,
|
||||
val tokenCurrency: CryptoCurrency.Token,
|
||||
val tokenFiatRate: BigDecimal?,
|
||||
val coinFiatRate: BigDecimal?,
|
||||
val feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
val transactionInfo: TransactionInfo? = null,
|
||||
) : TradeCryptoAction()
|
||||
|
||||
@Deprecated("Use AppRoute instead")
|
||||
data class SendCoin(
|
||||
val userWallet: UserWallet,
|
||||
val coinStatus: CryptoCurrencyStatus,
|
||||
val feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
val transactionInfo: TransactionInfo? = null,
|
||||
) : TradeCryptoAction()
|
||||
|
||||
data class TransactionInfo(
|
||||
val transactionId: String,
|
||||
val destinationAddress: String,
|
||||
val amount: String,
|
||||
val tag: String? = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -52,8 +52,10 @@ internal class ConfirmResidencyModel @Inject constructor(
|
|||
ConfirmResidencyUM.ActionButtonConfig(
|
||||
onClick = {
|
||||
analyticsEventHandler.send(OnrampAnalyticsEvent.OnResidenceConfirm(country.name))
|
||||
modelScope.launch { saveDefaultCountryUseCase.invoke(country) }
|
||||
params.onDismiss()
|
||||
modelScope.launch {
|
||||
saveDefaultCountryUseCase.invoke(country)
|
||||
params.onDismiss()
|
||||
}
|
||||
},
|
||||
text = resourceReference(R.string.common_confirm),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.features.onramp.main.entity
|
||||
|
||||
import com.tangem.domain.onramp.model.OnrampAmount
|
||||
|
||||
data class OnrampLastUpdate(
|
||||
val lastAmount: OnrampAmount,
|
||||
val lastCountryString: String,
|
||||
)
|
||||
|
|
@ -49,8 +49,11 @@ internal class OnrampStateFactory(
|
|||
fun getOnrampErrorState(onrampError: OnrampError): OnrampMainComponentUM {
|
||||
return when (onrampError) {
|
||||
OnrampError.PairsNotFound -> getNoPairsErrorState()
|
||||
is OnrampError.DataError -> getErrorState(onrampError.code)
|
||||
is OnrampError.DomainError -> getErrorState()
|
||||
is OnrampError.DataError -> getErrorState(
|
||||
errorCode = onrampError.code,
|
||||
onRefresh = onrampIntents::onRefresh,
|
||||
)
|
||||
is OnrampError.DomainError -> getErrorState(onRefresh = onrampIntents::onRefresh)
|
||||
is OnrampError.AmountError.TooBigError,
|
||||
is OnrampError.AmountError.TooSmallError,
|
||||
OnrampError.RedirectError.VerificationFailed,
|
||||
|
|
@ -74,7 +77,7 @@ internal class OnrampStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun getErrorState(errorCode: String? = null): OnrampMainComponentUM {
|
||||
fun getErrorState(errorCode: String? = null, onRefresh: () -> Unit): OnrampMainComponentUM {
|
||||
val state = currentStateProvider()
|
||||
val endButton = state.topBarConfig.endButtonUM.copy(enabled = true)
|
||||
|
||||
|
|
@ -89,13 +92,13 @@ internal class OnrampStateFactory(
|
|||
providerBlockState = OnrampProviderBlockUM.Empty,
|
||||
errorNotification = NotificationUM.Warning.OnrampErrorNotification(
|
||||
errorCode = errorCode,
|
||||
onRefresh = onrampIntents::onRefresh,
|
||||
onRefresh = onRefresh,
|
||||
),
|
||||
)
|
||||
is OnrampMainComponentUM.InitialLoading -> state.copy(
|
||||
errorNotification = NotificationUM.Warning.OnrampErrorNotification(
|
||||
errorCode = errorCode,
|
||||
onRefresh = onrampIntents::onRefresh,
|
||||
onRefresh = onRefresh,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,15 +14,18 @@ import com.tangem.domain.onramp.model.OnrampProviderWithQuote
|
|||
import com.tangem.domain.onramp.model.OnrampQuote
|
||||
import com.tangem.domain.onramp.model.error.OnrampError
|
||||
import com.tangem.domain.tokens.model.AmountType
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.main.entity.*
|
||||
import com.tangem.features.onramp.providers.entity.SelectProviderResult
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.extensions.isSingleItem
|
||||
|
||||
internal class OnrampAmountStateFactory(
|
||||
private val currentStateProvider: Provider<OnrampMainComponentUM>,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val onrampIntents: OnrampIntents,
|
||||
private val cryptoCurrency: CryptoCurrency,
|
||||
) {
|
||||
|
||||
private val onrampAmountFieldChangeConverter = OnrampAmountFieldChangeConverter(
|
||||
|
|
@ -62,11 +65,12 @@ internal class OnrampAmountStateFactory(
|
|||
|
||||
return currentState.copy(
|
||||
amountBlockState = amountState.copy(secondaryFieldModel = OnrampAmountSecondaryFieldUM.Loading),
|
||||
providerBlockState = OnrampProviderBlockUM.Loading,
|
||||
buyButtonConfig = currentState.buyButtonConfig.copy(enabled = false),
|
||||
)
|
||||
}
|
||||
|
||||
fun getAmountSecondaryUpdatedState(quote: OnrampQuote, isBestRate: Boolean): OnrampMainComponentUM {
|
||||
fun getAmountSecondaryUpdatedState(quote: OnrampQuote): OnrampMainComponentUM {
|
||||
val currentState = currentStateProvider()
|
||||
if (currentState !is OnrampMainComponentUM.Content) return currentState
|
||||
|
||||
|
|
@ -75,9 +79,9 @@ internal class OnrampAmountStateFactory(
|
|||
|
||||
return currentState.copy(
|
||||
amountBlockState = amountState.copy(
|
||||
amountFieldModel = amountState.amountFieldModel.copy(isError = false),
|
||||
secondaryFieldModel = quote.toSecondaryFieldUiModel(amountState) ?: amountState.secondaryFieldModel,
|
||||
),
|
||||
providerBlockState = quote.toProviderBlockState(isBestRate),
|
||||
buyButtonConfig = currentState.buyButtonConfig.copy(
|
||||
enabled = quote is OnrampQuote.Data,
|
||||
onClick = {
|
||||
|
|
@ -96,6 +100,32 @@ internal class OnrampAmountStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun getUpdatedProviderState(selectedQuote: OnrampQuote, quotes: List<OnrampQuote>): OnrampMainComponentUM {
|
||||
val currentState = currentStateProvider()
|
||||
if (currentState !is OnrampMainComponentUM.Content) return currentState
|
||||
|
||||
analyticsEventHandler.send(
|
||||
OnrampAnalyticsEvent.ProviderCalculated(
|
||||
providerName = selectedQuote.provider.info.name,
|
||||
tokenSymbol = cryptoCurrency.symbol,
|
||||
paymentMethod = selectedQuote.paymentMethod.name,
|
||||
),
|
||||
)
|
||||
|
||||
val bestProvider = selectedQuote as? OnrampQuote.Data
|
||||
val isMultipleQuotes = !quotes.isSingleItem()
|
||||
val isOtherQuotesHasData = quotes
|
||||
.filter { it.paymentMethod == selectedQuote.paymentMethod }
|
||||
.filterNot { it == bestProvider }
|
||||
.any { it is OnrampQuote.Data }
|
||||
|
||||
val isBestProvider = selectedQuote == bestProvider && isMultipleQuotes && isOtherQuotesHasData
|
||||
|
||||
return currentState.copy(
|
||||
providerBlockState = selectedQuote.toProviderBlockState(isBestProvider),
|
||||
)
|
||||
}
|
||||
|
||||
fun getAmountSecondaryUpdatedState(
|
||||
providerResult: SelectProviderResult,
|
||||
isBestRate: Boolean,
|
||||
|
|
|
|||
|
|
@ -25,10 +25,7 @@ import com.tangem.domain.onramp.model.error.OnrampError
|
|||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.main.OnrampMainComponent
|
||||
import com.tangem.features.onramp.main.entity.OnrampIntents
|
||||
import com.tangem.features.onramp.main.entity.OnrampMainBottomSheetConfig
|
||||
import com.tangem.features.onramp.main.entity.OnrampMainComponentUM
|
||||
import com.tangem.features.onramp.main.entity.OnrampProviderBlockUM
|
||||
import com.tangem.features.onramp.main.entity.*
|
||||
import com.tangem.features.onramp.main.entity.factory.OnrampStateFactory
|
||||
import com.tangem.features.onramp.main.entity.factory.amount.OnrampAmountStateFactory
|
||||
import com.tangem.features.onramp.providers.entity.SelectProviderResult
|
||||
|
|
@ -38,15 +35,13 @@ import com.tangem.utils.Provider
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.PeriodicTask
|
||||
import com.tangem.utils.coroutines.SingleTaskScheduler
|
||||
import com.tangem.utils.extensions.isSingleItem
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
internal class OnrampMainComponentModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
|
|
@ -75,6 +70,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
currentStateProvider = Provider { _state.value },
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
onrampIntents = this,
|
||||
cryptoCurrency = params.cryptoCurrency,
|
||||
)
|
||||
private val selectedUserWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
|
||||
private val _state: MutableStateFlow<OnrampMainComponentUM> = MutableStateFlow(
|
||||
|
|
@ -87,7 +83,7 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
val state: StateFlow<OnrampMainComponentUM> get() = _state.asStateFlow()
|
||||
val bottomSheetNavigation: SlotNavigation<OnrampMainBottomSheetConfig> = SlotNavigation()
|
||||
|
||||
private val lastAmount = mutableStateOf(BigDecimal.ZERO)
|
||||
private val lastUpdateState = mutableStateOf<OnrampLastUpdate?>(null)
|
||||
|
||||
init {
|
||||
sendScreenOpenAnalytics()
|
||||
|
|
@ -161,7 +157,11 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun updatePairsAndQuotes() {
|
||||
_state.update { amountStateFactory.getAmountSecondaryLoadingState() }
|
||||
val state = state.value as? OnrampMainComponentUM.Content
|
||||
|
||||
if (!state?.amountBlockState?.amountFieldModel?.fiatValue.isNullOrEmpty()) {
|
||||
_state.update { amountStateFactory.getAmountSecondaryLoadingState() }
|
||||
}
|
||||
fetchPairsUseCase.invoke(params.cryptoCurrency).fold(
|
||||
ifLeft = ::handleOnrampError,
|
||||
ifRight = { _state.update { amountStateFactory.getAmountSecondaryResetState() } },
|
||||
|
|
@ -200,7 +200,6 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
|
||||
private fun subscribeToQuotesUpdate() {
|
||||
getOnrampQuotesUseCase.invoke()
|
||||
.distinctUntilChanged()
|
||||
.conflate()
|
||||
.onEach { maybeQuotes ->
|
||||
maybeQuotes.fold(
|
||||
|
|
@ -282,42 +281,67 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
private fun handleQuoteResult(quotes: List<OnrampQuote>) {
|
||||
sendOnrampQuotesErrorAnalytic(quotes)
|
||||
|
||||
val quote = quotes.firstOrNull { it !is OnrampQuote.Error }
|
||||
val quote = selectOrUpdateQuote(quotes)
|
||||
|
||||
if (quote == null) {
|
||||
_state.update { stateFactory.getErrorState() }
|
||||
lastAmount.value = BigDecimal.ZERO
|
||||
_state.update { stateFactory.getErrorState(onRefresh = ::onRetryQuotes) }
|
||||
lastUpdateState.value = null
|
||||
return
|
||||
}
|
||||
|
||||
val bestProvider = quote as? OnrampQuote.Data
|
||||
val isMultipleQuotes = !quotes.isSingleItem()
|
||||
val isOtherQuotesHasData = quotes
|
||||
.filter { it.paymentMethod == quote.paymentMethod }
|
||||
.filterNot { it == bestProvider }
|
||||
.any { it is OnrampQuote.Data }
|
||||
val hasBestProvider = isMultipleQuotes && isOtherQuotesHasData
|
||||
if (checkLastInputState(quote)) {
|
||||
lastUpdateState.value = OnrampLastUpdate(
|
||||
quote.fromAmount,
|
||||
quote.countryCode,
|
||||
)
|
||||
|
||||
val isBestProvider = quote == bestProvider && hasBestProvider
|
||||
|
||||
if (lastAmount.value != quote.fromAmount.value) {
|
||||
lastAmount.value = quote.fromAmount.value
|
||||
if (quote is OnrampQuote.Data) {
|
||||
analyticsEventHandler.send(
|
||||
OnrampAnalyticsEvent.ProviderCalculated(
|
||||
providerName = quote.provider.info.name,
|
||||
tokenSymbol = params.cryptoCurrency.symbol,
|
||||
paymentMethod = quote.paymentMethod.name,
|
||||
),
|
||||
)
|
||||
}
|
||||
_state.update {
|
||||
amountStateFactory.getAmountSecondaryUpdatedState(
|
||||
quote = quote,
|
||||
isBestRate = isBestProvider,
|
||||
)
|
||||
amountStateFactory.getUpdatedProviderState(selectedQuote = quote, quotes = quotes)
|
||||
}
|
||||
}
|
||||
_state.update { amountStateFactory.getAmountSecondaryUpdatedState(quote = quote) }
|
||||
}
|
||||
|
||||
/**
|
||||
* !!! Important quote selection logic !!!
|
||||
* Selects or updated quote based on input data (amount, country, currency).
|
||||
* If input data has changed select new best quote, otherwise last selected quote.
|
||||
* If last selected quote on same input data is in an error state, select next best quote
|
||||
* If new best quote or next best quote does not exist (i.e. Error state) select nothing.
|
||||
*/
|
||||
private fun selectOrUpdateQuote(quotes: List<OnrampQuote>): OnrampQuote? {
|
||||
val quoteToCheck = quotes.firstOrNull { it !is OnrampQuote.Error }
|
||||
|
||||
// Check if amount, country or currency has changed
|
||||
return if (checkLastInputState(quoteToCheck)) {
|
||||
quoteToCheck
|
||||
} else {
|
||||
val state = state.value as? OnrampMainComponentUM.Content
|
||||
val providerState = state?.providerBlockState as? OnrampProviderBlockUM.Content
|
||||
|
||||
// Get current selected quote to update
|
||||
val lastSelectedQuote = quotes.firstOrNull {
|
||||
it.provider.id == providerState?.providerId &&
|
||||
it.paymentMethod.id == providerState.paymentMethod.id
|
||||
}
|
||||
|
||||
// Check if selected updated quote is not error
|
||||
if (lastSelectedQuote is OnrampQuote.Error) {
|
||||
quoteToCheck
|
||||
} else {
|
||||
lastSelectedQuote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onRetryQuotes() {
|
||||
_state.update {
|
||||
(it as? OnrampMainComponentUM.Content)?.copy(
|
||||
errorNotification = null,
|
||||
providerBlockState = OnrampProviderBlockUM.Loading,
|
||||
) ?: it
|
||||
}
|
||||
startLoadingQuotes()
|
||||
}
|
||||
|
||||
private fun showDemoWarning() {
|
||||
|
|
@ -380,6 +404,11 @@ internal class OnrampMainComponentModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun checkLastInputState(quote: OnrampQuote?): Boolean {
|
||||
return lastUpdateState.value?.lastAmount != quote?.fromAmount ||
|
||||
lastUpdateState.value?.lastCountryString != quote?.countryCode
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val UPDATE_DELAY = 10_000L
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
package com.tangem.features.onramp.selectcurrency.entity.transformer
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.onramp.model.OnrampCurrencies
|
||||
import com.tangem.domain.onramp.model.OnrampCurrency
|
||||
import com.tangem.domain.onramp.model.error.OnrampError
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.selectcurrency.entity.CurrenciesListUM
|
||||
import com.tangem.features.onramp.selectcurrency.entity.CurrenciesSection
|
||||
import com.tangem.features.onramp.selectcurrency.entity.CurrencyItemState
|
||||
|
|
@ -28,7 +29,7 @@ internal class UpdateCurrencyItemsTransformer(
|
|||
if (populars.isNotEmpty()) {
|
||||
add(
|
||||
CurrenciesSection(
|
||||
title = stringReference("Popular fiats"),
|
||||
title = resourceReference(R.string.onramp_currency_popular),
|
||||
items = populars.map(::convertCurrencyToUiModel).toImmutableList(),
|
||||
),
|
||||
)
|
||||
|
|
@ -36,7 +37,7 @@ internal class UpdateCurrencyItemsTransformer(
|
|||
if (others.isNotEmpty()) {
|
||||
add(
|
||||
CurrenciesSection(
|
||||
title = stringReference("Other currencies"),
|
||||
title = resourceReference(R.string.onramp_currency_other),
|
||||
items = others.map(::convertCurrencyToUiModel).toImmutableList(),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.tangem.core.decompose.model.Model
|
|||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.onramp.FetchOnrampCurrenciesUseCase
|
||||
import com.tangem.domain.onramp.GetOnrampCurrenciesUseCase
|
||||
import com.tangem.domain.onramp.OnrampSaveDefaultCurrencyUseCase
|
||||
|
|
@ -136,8 +135,11 @@ internal class OnrampSelectCurrencyModel @Inject constructor(
|
|||
|
||||
private companion object {
|
||||
val loadingSections = listOf(
|
||||
CurrenciesSection(stringReference("Popular fiats"), items = createLoadingItems("popular")),
|
||||
CurrenciesSection(stringReference("Other currencies"), items = createLoadingItems("other")),
|
||||
CurrenciesSection(
|
||||
resourceReference(R.string.onramp_currency_popular),
|
||||
items = createLoadingItems("popular"),
|
||||
),
|
||||
CurrenciesSection(resourceReference(R.string.onramp_currency_other), items = createLoadingItems("other")),
|
||||
).toImmutableList()
|
||||
|
||||
private fun createLoadingItems(prefix: String, size: Int = 5): ImmutableList<CurrencyItemState.Loading> =
|
||||
|
|
|
|||
|
|
@ -1,19 +1,28 @@
|
|||
package com.tangem.features.onramp.success.model
|
||||
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.components.BasicDialog
|
||||
import com.tangem.core.ui.components.DialogButtonUM
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.message.ContentMessage
|
||||
import com.tangem.domain.onramp.GetOnrampStatusUseCase
|
||||
import com.tangem.domain.onramp.GetOnrampTransactionUseCase
|
||||
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
|
||||
import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
|
||||
import com.tangem.domain.onramp.model.OnrampStatus
|
||||
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
||||
import com.tangem.domain.onramp.model.error.OnrampError
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.features.onramp.component.OnrampSuccessComponent
|
||||
import com.tangem.features.onramp.impl.R
|
||||
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
|
||||
|
|
@ -36,6 +45,8 @@ internal class OnrampSuccessComponentModel @Inject constructor(
|
|||
private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
|
||||
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val messageSender: UiMessageSender,
|
||||
private val router: Router,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model(), OnrampSuccessClickIntents {
|
||||
|
||||
|
|
@ -58,8 +69,9 @@ internal class OnrampSuccessComponentModel @Inject constructor(
|
|||
modelScope.launch {
|
||||
getOnrampTransactionUseCase(externalTxId = params.externalTxId)
|
||||
.fold(
|
||||
ifLeft = {
|
||||
Timber.e(it.toString())
|
||||
ifLeft = { error ->
|
||||
Timber.e(error.toString())
|
||||
showErrorAlert(error)
|
||||
},
|
||||
ifRight = { transaction ->
|
||||
loadTransactionStatus(transaction)
|
||||
|
|
@ -74,7 +86,7 @@ internal class OnrampSuccessComponentModel @Inject constructor(
|
|||
transaction.toCurrencyId,
|
||||
).getOrElse { error("Crypto currency not found") }
|
||||
|
||||
getOnrampStatusUseCase(externalTxId = params.externalTxId)
|
||||
getOnrampStatusUseCase(txId = transaction.txId)
|
||||
.fold(
|
||||
ifLeft = { error ->
|
||||
analyticsEventHandler.sendOnrampErrorEvent(
|
||||
|
|
@ -84,6 +96,7 @@ internal class OnrampSuccessComponentModel @Inject constructor(
|
|||
paymentMethod = transaction.paymentMethod,
|
||||
)
|
||||
Timber.e(error.toString())
|
||||
showErrorAlert(error)
|
||||
},
|
||||
ifRight = { status ->
|
||||
analyticsEventHandler.send(
|
||||
|
|
@ -112,6 +125,32 @@ internal class OnrampSuccessComponentModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun showErrorAlert(error: OnrampError) {
|
||||
val contentMessage = ContentMessage { onDismiss ->
|
||||
val errorCode = (error as? OnrampError.DataError)?.code
|
||||
val message = if (errorCode.isNullOrBlank()) {
|
||||
stringResource(R.string.common_unknown_error)
|
||||
} else {
|
||||
stringResource(R.string.express_error_code, wrappedList(errorCode))
|
||||
}
|
||||
BasicDialog(
|
||||
message = message,
|
||||
confirmButton = DialogButtonUM(
|
||||
title = stringResource(id = R.string.common_ok),
|
||||
onClick = {
|
||||
router.pop()
|
||||
onDismiss()
|
||||
},
|
||||
),
|
||||
onDismissDialog = {
|
||||
router.pop()
|
||||
onDismiss()
|
||||
},
|
||||
)
|
||||
}
|
||||
messageSender.send(contentMessage)
|
||||
}
|
||||
|
||||
private fun removeTransactionIfTerminalStatus(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
providerName: String,
|
||||
|
|
|
|||
|
|
@ -378,10 +378,9 @@ internal class SendViewModel @Inject constructor(
|
|||
when {
|
||||
uiState.value.sendState?.isSuccess == true -> return
|
||||
transactionId != null && amount != null && destinationAddress != null -> {
|
||||
loadFee()
|
||||
uiState.value = stateFactory.getReadyState(amount, destinationAddress, memo)
|
||||
stateRouter.showSend()
|
||||
updateNotifications()
|
||||
loadFee()
|
||||
}
|
||||
else -> {
|
||||
uiState.value = stateFactory.getReadyState()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.pullToRefresh.PullToRefreshConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.express
|
||||
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||
import com.tangem.feature.swap.domain.models.domain.SwapProvider
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class ExchangeUM(
|
||||
override val info: ExpressTransactionStateInfoUM,
|
||||
val provider: SwapProvider,
|
||||
val activeStatus: ExchangeStatus?,
|
||||
val statuses: ImmutableList<ExchangeStatusState>,
|
||||
val notification: ExchangeStatusNotifications? = null,
|
||||
val showProviderLink: Boolean,
|
||||
val isRefundTerminalStatus: Boolean,
|
||||
val fromCryptoCurrency: CryptoCurrency,
|
||||
val toCryptoCurrency: CryptoCurrency,
|
||||
) : ExpressTransactionStateUM
|
||||
|
|
@ -23,9 +23,9 @@ import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
|||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateIconUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateInfoUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.Provider
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListMode
|
|||
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionModel
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeStatusState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateIconUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateInfoUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.Provider
|
||||
|
|
@ -45,20 +45,20 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
private val cryptoCurrency: CryptoCurrency,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
appCurrencyProvider: Provider<AppCurrency>,
|
||||
) : Converter<Unit, PersistentList<ExpressTransactionStateUM.ExchangeUM>> {
|
||||
) : Converter<Unit, PersistentList<ExchangeUM>> {
|
||||
|
||||
private val iconStateConverter = CryptoCurrencyToIconStateConverter()
|
||||
private val appCurrency = appCurrencyProvider()
|
||||
|
||||
override fun convert(value: Unit): PersistentList<ExpressTransactionStateUM.ExchangeUM> {
|
||||
override fun convert(value: Unit): PersistentList<ExchangeUM> {
|
||||
return persistentListOf()
|
||||
}
|
||||
|
||||
fun convert(
|
||||
savedTransactions: List<SavedSwapTransactionListModel>,
|
||||
quotes: Set<Quote>,
|
||||
): PersistentList<ExpressTransactionStateUM.ExchangeUM> {
|
||||
val result = mutableListOf<ExpressTransactionStateUM.ExchangeUM>()
|
||||
): PersistentList<ExchangeUM> {
|
||||
val result = mutableListOf<ExchangeUM>()
|
||||
|
||||
savedTransactions
|
||||
.forEach { swapTransaction ->
|
||||
|
|
@ -84,7 +84,7 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
getNotification(transaction.status?.status, transaction.status?.txExternalUrl, null)
|
||||
val showProviderLink = getShowProviderLink(notifications, transaction.status)
|
||||
result.add(
|
||||
ExpressTransactionStateUM.ExchangeUM(
|
||||
ExchangeUM(
|
||||
provider = transaction.provider,
|
||||
statuses = getStatuses(transaction.status?.status),
|
||||
notification = notifications,
|
||||
|
|
@ -108,11 +108,11 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
}
|
||||
|
||||
fun updateTxStatus(
|
||||
tx: ExpressTransactionStateUM.ExchangeUM,
|
||||
tx: ExchangeUM,
|
||||
statusModel: ExchangeStatusModel?,
|
||||
refundToken: CryptoCurrency?,
|
||||
isRefundTerminalStatus: Boolean,
|
||||
): ExpressTransactionStateUM.ExchangeUM {
|
||||
): ExchangeUM {
|
||||
if (statusModel == null || tx.activeStatus == statusModel.status) {
|
||||
Timber.e("UpdateTxStatus isn't required. Current status isn't changed")
|
||||
return tx
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express
|
||||
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus
|
||||
import com.tangem.datasource.local.swaptx.SwapTransactionStatusStore
|
||||
|
|
@ -15,9 +16,8 @@ import com.tangem.feature.swap.domain.SwapTransactionRepository
|
|||
import com.tangem.feature.swap.domain.api.SwapRepository
|
||||
import com.tangem.feature.swap.domain.models.domain.*
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsSwapTransactionsStateConverter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import dagger.assisted.Assisted
|
||||
|
|
@ -55,7 +55,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
suspend operator fun invoke(): Flow<PersistentList<ExpressTransactionStateUM.ExchangeUM>> {
|
||||
suspend operator fun invoke(): Flow<PersistentList<ExchangeUM>> {
|
||||
val selectedWallet = getSelectedWalletSyncUseCase().fold(
|
||||
ifLeft = { return emptyFlow() },
|
||||
ifRight = { it },
|
||||
|
|
@ -82,7 +82,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
|||
suspend fun removeTransactionOnBottomSheetClosed(isForceTerminal: Boolean = false) {
|
||||
val state = currentStateProvider()
|
||||
val bottomSheetConfig = state.bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig ?: return
|
||||
val selectedTx = bottomSheetConfig.value as? ExpressTransactionStateUM.ExchangeUM ?: return
|
||||
val selectedTx = bottomSheetConfig.value as? ExchangeUM ?: return
|
||||
|
||||
val shouldTerminate = selectedTx.activeStatus.isTerminal(selectedTx.isRefundTerminalStatus) || isForceTerminal
|
||||
if (shouldTerminate) {
|
||||
|
|
@ -95,7 +95,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun updateSwapTxStatus(swapTx: ExpressTransactionStateUM.ExchangeUM): ExpressTransactionStateUM.ExchangeUM {
|
||||
suspend fun updateSwapTxStatus(swapTx: ExchangeUM): ExchangeUM {
|
||||
return if (swapTx.activeStatus.isTerminal(swapTx.isRefundTerminalStatus)) {
|
||||
swapTx
|
||||
} else {
|
||||
|
|
@ -163,7 +163,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
|||
private fun getExchangeStatusState(
|
||||
savedTransactions: List<SavedSwapTransactionListModel>?,
|
||||
quotes: Set<Quote>,
|
||||
): PersistentList<ExpressTransactionStateUM.ExchangeUM> {
|
||||
): PersistentList<ExchangeUM> {
|
||||
if (savedTransactions == null) {
|
||||
return persistentListOf()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express
|
||||
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
|
|
@ -10,8 +12,7 @@ import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
|||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -79,11 +80,13 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
|||
expressTxs.map { tx ->
|
||||
async {
|
||||
when (tx) {
|
||||
is ExpressTransactionStateUM.ExchangeUM -> exchangeStatusFactory.updateSwapTxStatus(tx)
|
||||
is ExchangeUM -> exchangeStatusFactory.updateSwapTxStatus(tx)
|
||||
is ExpressTransactionStateUM.OnrampUM -> onrampStatusFactory.updateOnrmapTxStatus(tx)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
.filterNotNull()
|
||||
.toPersistentList()
|
||||
}
|
||||
|
||||
|
|
@ -95,13 +98,13 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
|||
val config = state.bottomSheetConfig
|
||||
val expressBottomSheet = config?.content as? ExpressStatusBottomSheetConfig
|
||||
val currentTx = expressTxs.firstOrNull { it.info.txId == expressBottomSheet?.value?.info?.txId }
|
||||
if (currentTx is ExpressTransactionStateUM.ExchangeUM && currentTx.activeStatus == ExchangeStatus.Finished) {
|
||||
if (currentTx is ExchangeUM && currentTx.activeStatus == ExchangeStatus.Finished) {
|
||||
updateBalance(currentTx.toCryptoCurrency)
|
||||
}
|
||||
val expressTxsToDisplay = expressTxs.filterNot {
|
||||
when (it) {
|
||||
is ExpressTransactionStateUM.ExchangeUM -> false
|
||||
is ExpressTransactionStateUM.OnrampUM -> it.activeStatus.isHidden
|
||||
else -> false
|
||||
}
|
||||
}.toPersistentList()
|
||||
return state.copy(
|
||||
|
|
@ -115,7 +118,7 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
|||
|
||||
fun getStateWithExpressStatusBottomSheet(expressState: ExpressTransactionStateUM): TokenDetailsState {
|
||||
val analyticEvent = when (expressState) {
|
||||
is ExpressTransactionStateUM.ExchangeUM -> TokenExchangeAnalyticsEvent.CexTxStatusOpened(
|
||||
is ExchangeUM -> TokenExchangeAnalyticsEvent.CexTxStatusOpened(
|
||||
cryptoCurrency.symbol,
|
||||
)
|
||||
is ExpressTransactionStateUM.OnrampUM -> TokenOnrampAnalyticsEvent.OnrampStatusOpened(
|
||||
|
|
@ -123,6 +126,7 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
|||
provider = expressState.providerName,
|
||||
fiatCurrency = expressState.fromCurrencyCode,
|
||||
)
|
||||
else -> return currentStateProvider()
|
||||
}
|
||||
|
||||
analyticsEventsHandler.send(analyticEvent)
|
||||
|
|
@ -156,9 +160,7 @@ internal class ExpressStatusFactory @AssistedInject constructor(
|
|||
isForceTerminal: Boolean = false,
|
||||
) {
|
||||
when (expressState) {
|
||||
is ExpressTransactionStateUM.ExchangeUM -> exchangeStatusFactory.removeTransactionOnBottomSheetClosed(
|
||||
isForceTerminal,
|
||||
)
|
||||
is ExchangeUM -> exchangeStatusFactory.removeTransactionOnBottomSheetClosed(isForceTerminal)
|
||||
is ExpressTransactionStateUM.OnrampUM -> onrampStatusFactory.removeTransactionOnBottomSheetClosed()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express
|
||||
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
|
|
@ -14,9 +16,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
|||
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsOnrampTransactionStateConverter
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import dagger.assisted.Assisted
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.expressTransactionsItems
|
||||
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet
|
||||
|
|
@ -49,8 +51,6 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.T
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsTopAppBar
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenInfoBlock
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheet
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.expressTransactionsItems
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking.TokenStakingBlock
|
||||
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.OnrampStatusBottomSheetContent
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.exchange.ExchangeStatusBottomSheetContent
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.onramp.OnrampStatusBottomSheetContent
|
||||
|
||||
internal data class ExpressStatusBottomSheetConfig(
|
||||
val value: ExpressTransactionStateUM,
|
||||
) : TangemBottomSheetConfigContent
|
||||
|
||||
@Composable
|
||||
internal fun ExpressStatusBottomSheet(config: TangemBottomSheetConfig) {
|
||||
|
|
@ -21,7 +18,7 @@ internal fun ExpressStatusBottomSheet(config: TangemBottomSheetConfig) {
|
|||
) { content: ExpressStatusBottomSheetConfig ->
|
||||
when (val state = content.value) {
|
||||
is ExpressTransactionStateUM.OnrampUM -> OnrampStatusBottomSheetContent(state)
|
||||
is ExpressTransactionStateUM.ExchangeUM -> ExchangeStatusBottomSheetContent(state)
|
||||
is ExchangeUM -> ExchangeStatusBottomSheetContent(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,8 @@ import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.tangem.common.ui.expressStatus.ExpressEstimate
|
||||
import com.tangem.common.ui.expressStatus.ExpressProvider
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH10
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
|
|
@ -19,12 +21,10 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatus
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.ExchangeStatusNotifications
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressEstimate
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressProvider
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExchangeUM
|
||||
|
||||
@Composable
|
||||
internal fun ExchangeStatusBottomSheetContent(state: ExpressTransactionStateUM.ExchangeUM) {
|
||||
internal fun ExchangeStatusBottomSheetContent(state: ExchangeUM) {
|
||||
Column(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16)) {
|
||||
SpacerH10()
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import androidx.lifecycle.*
|
|||
import androidx.paging.cachedIn
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.routing.bundle.unbundle
|
||||
|
|
@ -41,7 +40,6 @@ import com.tangem.domain.staking.model.StakingAvailability
|
|||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction.TransactionInfo
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkAddress
|
||||
|
|
@ -68,10 +66,10 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.Token
|
|||
import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenDetailsNotificationsAnalyticsSender
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceSegmentedButtonConfig
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.express.ExpressTransactionStateUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express.ExpressStatusFactory
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.features.onramp.OnrampFeatureToggles
|
||||
import com.tangem.features.tokendetails.impl.R
|
||||
import com.tangem.utils.Provider
|
||||
|
|
@ -84,7 +82,6 @@ import kotlinx.coroutines.awaitAll
|
|||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList", "LargeClass", "TooManyFunctions")
|
||||
|
|
@ -99,8 +96,6 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val getExploreUrlUseCase: GetExploreUrlUseCase,
|
||||
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
|
||||
private val removeCurrencyUseCase: RemoveCurrencyUseCase,
|
||||
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
|
||||
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
|
||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val getCurrencyWarningsUseCase: GetCurrencyWarningsUseCase,
|
||||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
|
|
@ -518,68 +513,16 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
return
|
||||
}
|
||||
|
||||
sendCurrency(status = cryptoCurrencyStatus ?: return)
|
||||
sendCurrency()
|
||||
}
|
||||
|
||||
private fun sendCurrency(status: CryptoCurrencyStatus, transactionInfo: TransactionInfo? = null) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
val maybeFeeCurrencyStatus =
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, status).getOrNull()
|
||||
private fun sendCurrency() {
|
||||
val route = AppRoute.Send(
|
||||
currency = cryptoCurrency,
|
||||
userWalletId = userWallet.walletId,
|
||||
)
|
||||
|
||||
when (val currency = status.currency) {
|
||||
is CryptoCurrency.Coin -> {
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.SendCoin(
|
||||
userWallet = userWallet,
|
||||
coinStatus = status,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
is CryptoCurrency.Token -> {
|
||||
sendToken(
|
||||
tokenCurrency = currency,
|
||||
tokenFiatRate = status.value.fiatRate,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendToken(
|
||||
tokenCurrency: CryptoCurrency.Token,
|
||||
tokenFiatRate: BigDecimal?,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
transactionInfo: TransactionInfo?,
|
||||
) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
val maybeCoinStatus = getNetworkCoinStatusUseCase(
|
||||
userWalletId = userWalletId,
|
||||
networkId = tokenCurrency.network.id,
|
||||
derivationPath = tokenCurrency.network.derivationPath,
|
||||
isSingleWalletWithTokens = userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(),
|
||||
)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.firstOrNull()
|
||||
|
||||
reduxStateHolder.dispatchWithMain(
|
||||
action = TradeCryptoAction.SendToken(
|
||||
userWallet = userWallet,
|
||||
tokenCurrency = tokenCurrency,
|
||||
tokenFiatRate = tokenFiatRate,
|
||||
coinFiatRate = maybeCoinStatus?.fold(
|
||||
ifLeft = { null },
|
||||
ifRight = { it.value.fiatRate },
|
||||
),
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
appRouter.push(route)
|
||||
}
|
||||
|
||||
override fun onReceiveClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
|
|
|
|||
|
|
@ -52,18 +52,19 @@ dependencies {
|
|||
implementation(projects.core.utils)
|
||||
implementation(projects.core.analytics)
|
||||
implementation(projects.core.analytics.models)
|
||||
implementation(projects.common.routing)
|
||||
implementation(projects.core.deepLinks)
|
||||
implementation(projects.core.deepLinks.global)
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.datasource)
|
||||
|
||||
|
||||
implementation(projects.libs.crypto)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
|
||||
/** Domain modules */
|
||||
implementation(projects.domain.card)
|
||||
implementation(projects.domain.demo)
|
||||
implementation(projects.domain.legacy)
|
||||
implementation(projects.libs.blockchainSdk)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.settings)
|
||||
implementation(projects.domain.tokens)
|
||||
|
|
@ -82,6 +83,7 @@ dependencies {
|
|||
implementation(projects.domain.markets.models)
|
||||
implementation(projects.domain.feedback)
|
||||
implementation(projects.domain.onramp.models)
|
||||
implementation(projects.domain.onramp)
|
||||
|
||||
//TODO: Create api/impl modules for onboarding [REDACTED_JIRA]
|
||||
implementation(projects.features.onboarding)
|
||||
|
|
@ -100,6 +102,7 @@ dependencies {
|
|||
|
||||
/** Common modules */
|
||||
implementation(projects.common.ui)
|
||||
implementation(projects.common.routing)
|
||||
|
||||
/** Test libraries */
|
||||
implementation(deps.test.junit)
|
||||
|
|
|
|||
|
|
@ -2,25 +2,22 @@ package com.tangem.feature.wallet.presentation.deeplink
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.deeplink.global.BuyCurrencyDeepLink
|
||||
import com.tangem.core.deeplink.global.SellCurrencyDeepLink
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction.TransactionInfo
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.features.onramp.OnrampFeatureToggles
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -28,13 +25,9 @@ internal class WalletDeepLinksHandler @Inject constructor(
|
|||
private val deepLinksRegistry: DeepLinksRegistry,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
|
||||
private val getCryptoCurrencyStatusSyncUseCase: GetCryptoCurrencyStatusSyncUseCase,
|
||||
private val getCryptoCurrencyStatusesSyncUseCase: GetCryptoCurrencyStatusesSyncUseCase,
|
||||
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
|
||||
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val onrampFeatureToggles: OnrampFeatureToggles,
|
||||
private val router: AppRouter,
|
||||
) {
|
||||
|
||||
private var deepLinksMap = mutableMapOf<UserWalletId, List<DeepLink>>()
|
||||
|
|
@ -75,35 +68,23 @@ internal class WalletDeepLinksHandler @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun onSellCurrencyDeepLink(userWallet: UserWallet, data: SellCurrencyDeepLink.Data) {
|
||||
val cryptoCurrencyStatus = findCryptoCurrencyStatus(userWallet, data.currencyId) ?: return
|
||||
val feeCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
userWallet.walletId,
|
||||
cryptoCurrencyStatus,
|
||||
).getOrNull()
|
||||
val cryptoCurrency = getCryptoCurrencyUseCase(userWallet.walletId, data.currencyId).getOrNull()
|
||||
|
||||
val transactionInfo = data.let {
|
||||
TransactionInfo(
|
||||
amount = it.baseCurrencyAmount,
|
||||
destinationAddress = it.depositWalletAddress,
|
||||
transactionId = it.transactionId,
|
||||
tag = it.depositWalletAddressTag,
|
||||
)
|
||||
if (cryptoCurrency == null) {
|
||||
Timber.e("onSellCurrencyDeepLink cryptoCurrency is null")
|
||||
return
|
||||
}
|
||||
|
||||
when (cryptoCurrencyStatus.currency) {
|
||||
is CryptoCurrency.Coin -> sendCoin(
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
)
|
||||
is CryptoCurrency.Token -> sendToken(
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
)
|
||||
}
|
||||
val route = AppRoute.Send(
|
||||
currency = cryptoCurrency,
|
||||
userWalletId = userWallet.walletId,
|
||||
transactionId = data.transactionId,
|
||||
destinationAddress = data.depositWalletAddress,
|
||||
amount = data.baseCurrencyAmount,
|
||||
tag = data.depositWalletAddressTag,
|
||||
)
|
||||
|
||||
router.push(route)
|
||||
}
|
||||
|
||||
private suspend fun onBuyCurrencyDeepLink(externalTxId: String, userWallet: UserWallet) {
|
||||
|
|
@ -114,62 +95,4 @@ internal class WalletDeepLinksHandler @Inject constructor(
|
|||
analyticsEventHandler.send(TokenScreenAnalyticsEvent.Bought(cryptoCurrency.symbol))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun findCryptoCurrencyStatus(
|
||||
userWallet: UserWallet,
|
||||
currencyIdValue: String,
|
||||
): CryptoCurrencyStatus? {
|
||||
return if (userWallet.isMultiCurrency) {
|
||||
getCryptoCurrencyStatusesSyncUseCase(userWallet.walletId).getOrNull()?.let { currencies ->
|
||||
currencies.find { currencyIdValue == it.currency.id.value }
|
||||
}
|
||||
} else {
|
||||
getCryptoCurrencyStatusSyncUseCase(userWallet.walletId).getOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendCoin(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
transactionInfo: TransactionInfo,
|
||||
) {
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.SendCoin(
|
||||
userWallet = userWallet,
|
||||
coinStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun sendToken(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
transactionInfo: TransactionInfo,
|
||||
) {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency as? CryptoCurrency.Token ?: return
|
||||
val coinStatus = getNetworkCoinStatusUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
networkId = cryptoCurrency.network.id,
|
||||
derivationPath = cryptoCurrency.network.derivationPath,
|
||||
isSingleWalletWithTokens = false,
|
||||
)
|
||||
.firstOrNull()
|
||||
?.getOrNull()
|
||||
?: return
|
||||
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.SendToken(
|
||||
userWallet = userWallet,
|
||||
tokenCurrency = cryptoCurrency,
|
||||
tokenFiatRate = cryptoCurrencyStatus.value.fiatRate,
|
||||
coinFiatRate = coinStatus.value.fiatRate,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.domain
|
||||
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.datasource.local.swaptx.ExpressAnalyticsStatus
|
||||
import com.tangem.domain.onramp.GetOnrampStatusUseCase
|
||||
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
|
||||
import com.tangem.domain.onramp.OnrampUpdateTransactionStatusUseCase
|
||||
import com.tangem.domain.onramp.model.OnrampStatus
|
||||
import com.tangem.domain.onramp.model.OnrampStatus.Status.*
|
||||
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ViewModelScoped
|
||||
internal class OnrampStatusFactory @Inject constructor(
|
||||
private val stateHolder: WalletStateController,
|
||||
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
|
||||
private val getOnrampStatusUseCase: GetOnrampStatusUseCase,
|
||||
private val onrampUpdateTransactionStatusUseCase: OnrampUpdateTransactionStatusUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
suspend fun removeTransactionOnBottomSheetClosed() {
|
||||
val state = stateHolder.getSelectedWallet()
|
||||
val bottomSheetConfig = state.bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig ?: return
|
||||
val selectedTx = bottomSheetConfig.value as? ExpressTransactionStateUM.OnrampUM ?: return
|
||||
|
||||
if (selectedTx.activeStatus.isTerminal) {
|
||||
onrampRemoveTransactionUseCase(externalTxId = selectedTx.info.txExternalId)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateOnrmapTransactionStatuses() = withContext(dispatchers.io) {
|
||||
val singleWalletState = stateHolder.getSelectedWallet() as? WalletState.SingleCurrency.Content
|
||||
?: return@withContext
|
||||
|
||||
singleWalletState.expressTxs.map { tx ->
|
||||
async {
|
||||
if (tx is ExpressTransactionStateUM.OnrampUM) {
|
||||
updateOnrampTxStatus(tx)
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
}
|
||||
|
||||
private suspend fun updateOnrampTxStatus(onrampTx: ExpressTransactionStateUM.OnrampUM) {
|
||||
if (!onrampTx.activeStatus.isTerminal) {
|
||||
getOnrampStatusUseCase(onrampTx.info.txId).fold(
|
||||
ifLeft = {
|
||||
Timber.e("Couldn't update onramp status. $it")
|
||||
},
|
||||
ifRight = { statusModel ->
|
||||
val externalTxId = statusModel.externalTxId
|
||||
val status = toAnalyticStatus(statusModel.status) ?: return
|
||||
|
||||
if (statusModel.status != onrampTx.activeStatus) {
|
||||
analyticsEventHandler.send(
|
||||
TokenOnrampAnalyticsEvent.OnrampStatusChanged(
|
||||
tokenSymbol = onrampTx.info.toAmountSymbol,
|
||||
status = status.name,
|
||||
provider = onrampTx.providerName,
|
||||
fiatCurrency = onrampTx.fromCurrencyCode,
|
||||
),
|
||||
)
|
||||
onrampUpdateTransactionStatusUseCase(externalTxId = externalTxId, statusModel.status)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toAnalyticStatus(status: OnrampStatus.Status?): ExpressAnalyticsStatus? {
|
||||
return when (status) {
|
||||
Expired,
|
||||
Paused,
|
||||
-> ExpressAnalyticsStatus.Cancelled
|
||||
Created,
|
||||
WaitingForPayment,
|
||||
PaymentProcessing,
|
||||
Paid,
|
||||
Sending,
|
||||
-> ExpressAnalyticsStatus.InProgress
|
||||
Verifying -> ExpressAnalyticsStatus.KYC
|
||||
Failed -> ExpressAnalyticsStatus.Fail
|
||||
Finished -> ExpressAnalyticsStatus.Done
|
||||
null -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
|||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
|
||||
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
|
||||
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
|
||||
|
|
@ -27,6 +29,8 @@ internal class SingleWalletContentLoader(
|
|||
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
|
||||
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
|
@ -55,6 +59,16 @@ internal class SingleWalletContentLoader(
|
|||
getSingleWalletWarningsFactory = getSingleWalletWarningsFactory,
|
||||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
),
|
||||
SingleWalletExpressStatusesSubscriber(
|
||||
userWallet = userWallet,
|
||||
stateHolder = stateHolder,
|
||||
clickIntents = clickIntents,
|
||||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase,
|
||||
getOnrampTransactionsUseCase = getOnrampTransactionsUseCase,
|
||||
onrampRemoveTransactionUseCase = onrampRemoveTransactionUseCase,
|
||||
),
|
||||
TxHistorySubscriber(
|
||||
userWallet = userWallet,
|
||||
isRefresh = isRefresh,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
|
|||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
|
||||
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
|
||||
import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
|
||||
|
|
@ -26,6 +28,8 @@ internal class SingleWalletContentLoaderFactory @Inject constructor(
|
|||
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
|
||||
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
|
||||
) {
|
||||
|
|
@ -45,6 +49,8 @@ internal class SingleWalletContentLoaderFactory @Inject constructor(
|
|||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
|
||||
getOnrampTransactionsUseCase = getOnrampTransactionsUseCase,
|
||||
onrampRemoveTransactionUseCase = onrampRemoveTransactionUseCase,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
|
|
@ -59,6 +60,8 @@ internal sealed interface WalletState : WalletStateHolder {
|
|||
override val buttons: PersistentList<WalletManageButton>,
|
||||
override val marketPriceBlockState: MarketPriceBlockState,
|
||||
override val txHistoryState: TxHistoryState,
|
||||
val expressTxsToDisplay: PersistentList<ExpressTransactionStateUM>,
|
||||
val expressTxs: PersistentList<ExpressTransactionStateUM>,
|
||||
) : SingleCurrency()
|
||||
|
||||
data class Locked(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletOnrampTransactionConverter
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import timber.log.Timber
|
||||
|
||||
internal class SetExpressStatusesTransformer(
|
||||
userWalletId: UserWalletId,
|
||||
private val onrampTxs: List<OnrampTransaction>,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : WalletStateTransformer(userWalletId) {
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return when (prevState) {
|
||||
is WalletState.SingleCurrency.Content -> {
|
||||
val expressTxs = SingleWalletOnrampTransactionConverter(
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
appCurrency = appCurrency,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
).convertList(onrampTxs).toPersistentList()
|
||||
|
||||
val expressTxsToDisplay = expressTxs.filterNot {
|
||||
it.activeStatus.isHidden
|
||||
}.toPersistentList()
|
||||
|
||||
val expressBottomSheet = prevState.bottomSheetConfig?.content as? ExpressStatusBottomSheetConfig
|
||||
val currentTx = expressTxs.firstOrNull { it.info.txId == expressBottomSheet?.value?.info?.txId }
|
||||
|
||||
prevState.copy(
|
||||
expressTxs = expressTxs,
|
||||
expressTxsToDisplay = expressTxsToDisplay,
|
||||
bottomSheetConfig = prevState.bottomSheetConfig?.updateStateWithExpressStatusBottomSheet(currentTx),
|
||||
)
|
||||
}
|
||||
is WalletState.SingleCurrency.Locked -> {
|
||||
Timber.w("Impossible to load express statuses for locked wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.Visa -> {
|
||||
Timber.w("Impossible to load express statuses for visa wallet")
|
||||
prevState
|
||||
}
|
||||
is WalletState.MultiCurrency -> {
|
||||
Timber.w("Impossible to load express statuses for multi-currency wallet")
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TangemBottomSheetConfig.updateStateWithExpressStatusBottomSheet(
|
||||
expressState: ExpressTransactionStateUM?,
|
||||
): TangemBottomSheetConfig {
|
||||
val currentConfig = this.content as? ExpressStatusBottomSheetConfig ?: return this
|
||||
if (expressState == null) return this
|
||||
return copy(
|
||||
content = if (currentConfig.value != expressState) {
|
||||
ExpressStatusBottomSheetConfig(expressState)
|
||||
} else {
|
||||
currentConfig
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,279 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||
|
||||
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.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
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.core.ui.utils.toDateFormatWithTodayYesterday
|
||||
import com.tangem.core.ui.utils.toTimeFormat
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.onramp.model.OnrampStatus
|
||||
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateIconUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateInfoUM
|
||||
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal class SingleWalletOnrampTransactionConverter(
|
||||
private val clickIntents: WalletClickIntents,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Converter<OnrampTransaction, ExpressTransactionStateUM.OnrampUM> {
|
||||
|
||||
private val iconStateConverter = CryptoCurrencyToIconStateConverter()
|
||||
|
||||
private val currency = cryptoCurrencyStatus.currency
|
||||
private val status = cryptoCurrencyStatus.value
|
||||
|
||||
override fun convert(value: OnrampTransaction): ExpressTransactionStateUM.OnrampUM {
|
||||
return ExpressTransactionStateUM.OnrampUM(
|
||||
info = ExpressTransactionStateInfoUM(
|
||||
title = resourceReference(
|
||||
id = R.string.express_status_buying,
|
||||
wrappedList(currency.name),
|
||||
),
|
||||
status = convertStatuses(value.status, value.externalTxUrl),
|
||||
notification = getNotification(value.status, value.externalTxUrl, value.providerName),
|
||||
txId = value.txId,
|
||||
txExternalId = value.externalTxId,
|
||||
txExternalUrl = value.externalTxUrl,
|
||||
timestamp = value.timestamp,
|
||||
timestampFormatted = resourceReference(
|
||||
R.string.send_date_format,
|
||||
wrappedList(
|
||||
value.timestamp.toDateFormatWithTodayYesterday(),
|
||||
value.timestamp.toTimeFormat(),
|
||||
),
|
||||
),
|
||||
toAmount = stringReference(
|
||||
value.toAmount.format { crypto(currency) },
|
||||
),
|
||||
toFiatAmount = stringReference(
|
||||
status.fiatRate?.multiply(value.toAmount).format {
|
||||
fiat(
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
},
|
||||
),
|
||||
toAmountSymbol = currency.symbol,
|
||||
toCurrencyIcon = iconStateConverter.convert(currency),
|
||||
fromAmount = stringReference(
|
||||
value.fromAmount.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = value.fromCurrency.name,
|
||||
fiatCurrencySymbol = value.fromCurrency.code,
|
||||
)
|
||||
},
|
||||
),
|
||||
fromFiatAmount = null,
|
||||
fromAmountSymbol = value.fromCurrency.code,
|
||||
fromCurrencyIcon = CurrencyIconState.FiatIcon(
|
||||
url = value.fromCurrency.image,
|
||||
fallbackResId = R.drawable.ic_currency_24,
|
||||
),
|
||||
iconState = getIconState(value.status),
|
||||
onGoToProviderClick = {
|
||||
analyticsEventHandler.send(TokenOnrampAnalyticsEvent.GoToProvider)
|
||||
clickIntents.onGoToProviderClick(it)
|
||||
},
|
||||
onClick = {
|
||||
val analyticEvent = TokenOnrampAnalyticsEvent.OnrampStatusOpened(
|
||||
tokenSymbol = currency.symbol,
|
||||
provider = value.providerName,
|
||||
fiatCurrency = value.fromCurrency.code,
|
||||
)
|
||||
analyticsEventHandler.send(analyticEvent)
|
||||
clickIntents.onExpressTransactionClick(value.txId)
|
||||
},
|
||||
),
|
||||
providerName = value.providerName,
|
||||
providerImageUrl = value.providerImageUrl,
|
||||
providerType = value.providerType,
|
||||
activeStatus = value.status,
|
||||
fromCurrencyCode = value.fromCurrency.code,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getNotification(
|
||||
status: OnrampStatus.Status,
|
||||
externalTxUrl: String?,
|
||||
providerName: String,
|
||||
): NotificationUM? {
|
||||
if (externalTxUrl == null) return null
|
||||
return when (status) {
|
||||
OnrampStatus.Status.Verifying -> {
|
||||
analyticsEventHandler.send(
|
||||
TokenOnrampAnalyticsEvent.NoticeKYC(currency.symbol, providerName),
|
||||
)
|
||||
ExpressNotificationsUM.NeedVerification {
|
||||
clickIntents.onGoToProviderClick(externalTxUrl)
|
||||
}
|
||||
}
|
||||
OnrampStatus.Status.Failed -> {
|
||||
ExpressNotificationsUM.FailedByProvider {
|
||||
clickIntents.onGoToProviderClick(externalTxUrl)
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIconState(status: OnrampStatus.Status): ExpressTransactionStateIconUM {
|
||||
return when (status) {
|
||||
OnrampStatus.Status.Verifying -> ExpressTransactionStateIconUM.Warning
|
||||
OnrampStatus.Status.Failed -> ExpressTransactionStateIconUM.Error
|
||||
else -> ExpressTransactionStateIconUM.None
|
||||
}
|
||||
}
|
||||
|
||||
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(currency.name))
|
||||
}
|
||||
this == OnrampStatus.Status.Paid -> {
|
||||
resourceReference(
|
||||
R.string.express_status_buying_active,
|
||||
wrappedList(currency.name),
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
resourceReference(R.string.express_status_bought, wrappedList(currency.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(currency.name),
|
||||
)
|
||||
}
|
||||
this == OnrampStatus.Status.Sending -> {
|
||||
resourceReference(
|
||||
R.string.express_exchange_status_sending_active,
|
||||
wrappedList(currency.name),
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
resourceReference(
|
||||
R.string.express_exchange_status_sent,
|
||||
wrappedList(currency.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 = {
|
||||
clickIntents.onGoToProviderClick(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
|
||||
}
|
||||
}
|
||||
|
|
@ -58,6 +58,8 @@ internal class WalletLoadingStateFactory(
|
|||
value = TxHistoryState.getDefaultLoadingTransactions(clickIntents::onExploreClick),
|
||||
),
|
||||
),
|
||||
expressTxsToDisplay = persistentListOf(),
|
||||
expressTxs = persistentListOf(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.subscribers
|
||||
|
||||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.onramp.GetOnrampTransactionsUseCase
|
||||
import com.tangem.domain.onramp.OnrampRemoveTransactionUseCase
|
||||
import com.tangem.domain.onramp.model.cache.OnrampTransaction
|
||||
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetExpressStatusesTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class SingleWalletExpressStatusesSubscriber(
|
||||
private val userWallet: UserWallet,
|
||||
private val stateHolder: WalletStateController,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
|
||||
private val getOnrampTransactionsUseCase: GetOnrampTransactionsUseCase,
|
||||
private val onrampRemoveTransactionUseCase: OnrampRemoveTransactionUseCase,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
override fun create(
|
||||
coroutineScope: CoroutineScope,
|
||||
): Flow<Pair<Either<CurrencyStatusError, CryptoCurrencyStatus>, AppCurrency>> {
|
||||
return combine(
|
||||
flow = getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = userWallet.walletId)
|
||||
.conflate()
|
||||
.distinctUntilChanged(),
|
||||
flow2 = getSelectedAppCurrencyUseCase()
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.map { maybeAppCurrency -> maybeAppCurrency.getOrElse { AppCurrency.Default } },
|
||||
transform = { maybeCurrencyStatus, appCurrency -> maybeCurrencyStatus to appCurrency },
|
||||
).onEach { maybeCurrencyStatusAndAppCurrency ->
|
||||
val status = maybeCurrencyStatusAndAppCurrency.first.getOrElse {
|
||||
Timber.e("Unable to get primary currency status: $it")
|
||||
return@onEach
|
||||
}
|
||||
|
||||
getOnrampTransactionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrencyId = status.currency.id,
|
||||
).onEach { maybeTransaction ->
|
||||
maybeTransaction.fold(
|
||||
ifRight = { onrampTxs ->
|
||||
onrampTxs.clearHiddenTerminal()
|
||||
stateHolder.update(
|
||||
SetExpressStatusesTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
onrampTxs = onrampTxs,
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatus = status,
|
||||
appCurrency = maybeCurrencyStatusAndAppCurrency.second,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
),
|
||||
)
|
||||
},
|
||||
ifLeft = {
|
||||
stateHolder.update(
|
||||
SetExpressStatusesTransformer(
|
||||
userWalletId = userWallet.walletId,
|
||||
onrampTxs = listOf(),
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatus = status,
|
||||
appCurrency = maybeCurrencyStatusAndAppCurrency.second,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun List<OnrampTransaction>.clearHiddenTerminal() {
|
||||
this.filter { it.status.isHidden && it.status.isTerminal }
|
||||
.forEach { onrampRemoveTransactionUseCase(externalTxId = it.externalTxId) }
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +45,9 @@ import androidx.compose.ui.unit.IntOffset
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheet
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.expressStatus.expressTransactionsItems
|
||||
import com.tangem.core.ui.components.atoms.Hand
|
||||
import com.tangem.core.ui.components.atoms.handComposableComponentHeight
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
|
|
@ -216,6 +219,12 @@ private fun WalletContent(
|
|||
walletState.marketPriceBlockState?.let { marketPriceBlockState ->
|
||||
marketPriceBlock(state = marketPriceBlockState, modifier = itemModifier)
|
||||
}
|
||||
if (walletState is WalletState.SingleCurrency.Content) {
|
||||
expressTransactionsItems(
|
||||
expressTxs = walletState.expressTxsToDisplay,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
(selectedWallet as? WalletState.Visa.Content)?.let {
|
||||
|
|
@ -716,6 +725,7 @@ private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
|
|||
is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig)
|
||||
is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig)
|
||||
is PushNotificationsBottomSheetConfig -> PushNotificationsBottomSheet(config = bottomSheetConfig)
|
||||
is ExpressStatusBottomSheetConfig -> ExpressStatusBottomSheet(config = bottomSheetConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
|||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWalletAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader
|
||||
|
|
@ -33,9 +34,7 @@ import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
|||
import com.tangem.features.pushnotifications.api.utils.getPushPermissionOrNull
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import com.tangem.utils.coroutines.*
|
||||
import com.tangem.utils.extensions.indexOfFirstOrNull
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -70,6 +69,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase,
|
||||
private val walletImageResolver: WalletImageResolver,
|
||||
private val tokenListStore: MultiWalletTokenListStore,
|
||||
private val onrampStatusFactory: OnrampStatusFactory,
|
||||
private val walletFeatureToggles: WalletFeatureToggles,
|
||||
analyticsEventsHandler: AnalyticsEventHandler,
|
||||
) : ViewModel() {
|
||||
|
|
@ -81,6 +81,8 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val refreshWalletJobHolder = JobHolder()
|
||||
private var needToRefreshWallet = false
|
||||
|
||||
private var expressTxStatusTaskScheduler = SingleTaskScheduler<Unit>()
|
||||
|
||||
init {
|
||||
analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.ScreenOpened)
|
||||
|
||||
|
|
@ -93,6 +95,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
subscribeOnSelectedWalletFlow()
|
||||
subscribeToScreenBackgroundState()
|
||||
subscribeOnPushNotificationsPermission()
|
||||
subscribeOnExpressTransactionsUpdates()
|
||||
}
|
||||
|
||||
private fun maybeMigrateNames() {
|
||||
|
|
@ -239,6 +242,24 @@ internal class WalletViewModel @Inject constructor(
|
|||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnExpressTransactionsUpdates() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
expressTxStatusTaskScheduler.cancelTask()
|
||||
expressTxStatusTaskScheduler.scheduleTask(
|
||||
viewModelScope,
|
||||
PeriodicTask(
|
||||
isDelayFirst = false,
|
||||
delay = EXPRESS_STATUS_UPDATE_DELAY,
|
||||
task = {
|
||||
runCatching { onrampStatusFactory.updateOnrmapTransactionStatuses() }
|
||||
},
|
||||
onSuccess = { /* no-op */ },
|
||||
onError = { /* no-op */ },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun needToRefreshTimer() {
|
||||
viewModelScope.launch {
|
||||
delay(REFRESH_WALLET_BACKGROUND_TIMER_MILLIS)
|
||||
|
|
@ -430,5 +451,6 @@ internal class WalletViewModel @Inject constructor(
|
|||
|
||||
private companion object {
|
||||
const val REFRESH_WALLET_BACKGROUND_TIMER_MILLIS = 10000L
|
||||
const val EXPRESS_STATUS_UPDATE_DELAY = 10000L
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||
|
|
@ -11,10 +12,14 @@ import com.tangem.domain.tokens.model.TokenActionsState
|
|||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.unwrap
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBottomSheetConfig
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
|
|
@ -43,6 +48,12 @@ internal interface WalletContentClickIntents {
|
|||
fun onTokenItemLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onTransactionClick(txHash: String)
|
||||
|
||||
fun onDissmissBottomSheet()
|
||||
|
||||
fun onGoToProviderClick(externalTxId: String)
|
||||
|
||||
fun onExpressTransactionClick(txId: String)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -51,6 +62,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
private val stateHolder: WalletStateController,
|
||||
private val currencyActionsClickIntents: WalletCurrencyActionsClickIntentsImplementor,
|
||||
private val walletWarningsClickIntents: WalletWarningsClickIntentsImplementor,
|
||||
private val onrampStatusFactory: OnrampStatusFactory,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
|
||||
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
|
||||
|
|
@ -164,4 +176,39 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGoToProviderClick(externalTxUrl: String) {
|
||||
router.openUrl(externalTxUrl)
|
||||
}
|
||||
|
||||
override fun onDissmissBottomSheet() {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
if (stateHolder.getSelectedWallet().bottomSheetConfig?.content is ExpressStatusBottomSheetConfig) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
onrampStatusFactory.removeTransactionOnBottomSheetClosed()
|
||||
}
|
||||
}
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId))
|
||||
}
|
||||
|
||||
override fun onExpressTransactionClick(txId: String) {
|
||||
viewModelScope.launch {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
val singleWalletState = stateHolder.getSelectedWallet() as? WalletState.SingleCurrency.Content
|
||||
?: return@launch
|
||||
|
||||
val expressTransaction = singleWalletState.expressTxsToDisplay.firstOrNull { it.info.txId == txId }
|
||||
?: return@launch
|
||||
|
||||
stateHolder.update(
|
||||
OpenBottomSheetTransformer(
|
||||
userWalletId = userWalletId,
|
||||
content = ExpressStatusBottomSheetConfig(
|
||||
value = expressTransaction,
|
||||
),
|
||||
onDismissBottomSheet = ::onDissmissBottomSheet,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,6 @@ import com.tangem.core.ui.haptic.TangemHapticEffect
|
|||
import com.tangem.core.ui.haptic.VibratorHapticManager
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.extenstions.unwrap
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.utils.lceError
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
|
|
@ -32,13 +31,14 @@ import com.tangem.domain.redux.ReduxStateHolder
|
|||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||
import com.tangem.domain.settings.usercountry.models.UserCountry
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
|
||||
import com.tangem.domain.tokens.IsCryptoCurrencyCoinCouldHideUseCase
|
||||
import com.tangem.domain.tokens.RemoveCurrencyUseCase
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
|
|
@ -53,9 +53,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|||
import dagger.hilt.android.scopes.ViewModelScoped
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
|
@ -108,8 +106,6 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
|
||||
private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase,
|
||||
private val removeCurrencyUseCase: RemoveCurrencyUseCase,
|
||||
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
|
||||
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
|
||||
private val getExploreUrlUseCase: GetExploreUrlUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
|
|
@ -136,63 +132,12 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWallet.walletId))
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
val maybeFeeCurrencyStatus =
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWallet.walletId, cryptoCurrencyStatus).getOrNull()
|
||||
when (val currency = cryptoCurrencyStatus.currency) {
|
||||
is CryptoCurrency.Coin -> sendCoin(cryptoCurrencyStatus, userWallet, maybeFeeCurrencyStatus)
|
||||
is CryptoCurrency.Token -> sendToken(
|
||||
cryptoCurrency = currency,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus.value,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
userWallet = userWallet,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendCoin(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
userWallet: UserWallet,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
) {
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.SendCoin(
|
||||
userWallet = userWallet,
|
||||
coinStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
),
|
||||
val route = AppRoute.Send(
|
||||
currency = cryptoCurrencyStatus.currency,
|
||||
userWalletId = userWallet.walletId,
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendToken(
|
||||
cryptoCurrency: CryptoCurrency.Token,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus.Value,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
userWallet: UserWallet,
|
||||
) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
getNetworkCoinStatusUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
networkId = cryptoCurrency.network.id,
|
||||
derivationPath = cryptoCurrency.network.derivationPath,
|
||||
isSingleWalletWithTokens = userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(),
|
||||
)
|
||||
.take(count = 1)
|
||||
.collectLatest {
|
||||
it.onRight { coinStatus ->
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.SendToken(
|
||||
userWallet = userWallet,
|
||||
tokenCurrency = cryptoCurrency,
|
||||
tokenFiatRate = cryptoCurrencyStatus.fiatRate,
|
||||
coinFiatRate = coinStatus.value.fiatRate,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
appRouter.push(route)
|
||||
}
|
||||
|
||||
override fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue