Updated on 2026-08-14

This commit is contained in:
Tangem 2024-12-09 11:02:33 +05:00
parent ed0b07bc82
commit f4422dd3fb
25 changed files with 274 additions and 115 deletions

View file

@ -3,10 +3,15 @@ package com.tangem.features.onramp.confirmresidency
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.onramp.model.OnrampCountry
import com.tangem.domain.tokens.model.CryptoCurrency
internal interface ConfirmResidencyComponent : ComposableBottomSheetComponent {
data class Params(val country: OnrampCountry, val onDismiss: (OnrampCountry) -> Unit)
data class Params(
val cryptoCurrency: CryptoCurrency,
val country: OnrampCountry,
val onDismiss: (OnrampCountry) -> Unit,
)
interface Factory : ComponentFactory<Params, ConfirmResidencyComponent>
}

View file

@ -14,10 +14,10 @@ import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyBottomSheetConfig
import com.tangem.features.onramp.confirmresidency.model.ConfirmResidencyModel
import com.tangem.features.onramp.confirmresidency.ui.ConfirmResidencyBottomSheet
import com.tangem.features.onramp.confirmresidency.ui.ConfirmResidencyBottomSheetContent
import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyBottomSheetConfig
import com.tangem.features.onramp.selectcountry.SelectCountryComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -68,7 +68,10 @@ internal class DefaultConfirmResidencyComponent @AssistedInject constructor(
): ComposableBottomSheetComponent = when (config) {
ConfirmResidencyBottomSheetConfig.SelectCountry -> selectCountryComponentFactory.create(
context = childByContext(componentContext),
params = SelectCountryComponent.Params(onDismiss = model.bottomSheetNavigation::dismiss),
params = SelectCountryComponent.Params(
cryptoCurrency = params.cryptoCurrency,
onDismiss = model.bottomSheetNavigation::dismiss,
),
)
}

View file

@ -15,6 +15,7 @@ import com.tangem.features.onramp.confirmresidency.ConfirmResidencyComponent
import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyBottomSheetConfig
import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyUM
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
@ -51,6 +52,9 @@ internal class ConfirmResidencyModel @Inject constructor(
analyticsEventHandler.send(OnrampAnalyticsEvent.ResidenceConfirmScreenOpened(params.country.name))
getOnrampCountryUseCase.invoke()
.onEach { maybeCountry ->
maybeCountry.onLeft {
analyticsEventHandler.sendOnrampErrorEvent(it, params.cryptoCurrency.symbol)
}
val country = maybeCountry.getOrNull()
if (country != null) {
params.onDismiss(country)

View file

@ -54,6 +54,7 @@ internal class DefaultOnrampMainComponent @AssistedInject constructor(
is OnrampMainBottomSheetConfig.ConfirmResidency -> confirmResidencyComponentFactory.create(
context = childByContext(componentContext),
params = ConfirmResidencyComponent.Params(
cryptoCurrency = params.cryptoCurrency,
country = config.country,
onDismiss = {
model.bottomSheetNavigation.dismiss()
@ -63,7 +64,10 @@ internal class DefaultOnrampMainComponent @AssistedInject constructor(
)
is OnrampMainBottomSheetConfig.CurrenciesList -> selectCurrencyComponentFactory.create(
context = childByContext(componentContext),
params = SelectCurrencyComponent.Params(model.bottomSheetNavigation::dismiss),
params = SelectCurrencyComponent.Params(
cryptoCurrency = params.cryptoCurrency,
onDismiss = model.bottomSheetNavigation::dismiss,
),
)
is OnrampMainBottomSheetConfig.ProvidersList -> selectProviderComponentFactory.create(
context = childByContext(componentContext),

View file

@ -1,11 +1,13 @@
package com.tangem.features.onramp.main.entity.factory.amount
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
import com.tangem.domain.onramp.model.OnrampCurrency
import com.tangem.domain.onramp.model.OnrampProviderWithQuote
import com.tangem.domain.onramp.model.OnrampQuote
@ -18,6 +20,7 @@ import com.tangem.utils.Provider
internal class OnrampAmountStateFactory(
private val currentStateProvider: Provider<OnrampMainComponentUM>,
private val analyticsEventHandler: AnalyticsEventHandler,
private val onrampIntents: OnrampIntents,
) {
@ -171,8 +174,14 @@ internal class OnrampAmountStateFactory(
}
val errorTextRes = when (error) {
is OnrampError.AmountError.TooBigError -> R.string.onramp_max_amount_restriction
is OnrampError.AmountError.TooSmallError -> R.string.onramp_min_amount_restriction
is OnrampError.AmountError.TooBigError -> {
analyticsEventHandler.send(OnrampAnalyticsEvent.MaxAmountError)
R.string.onramp_max_amount_restriction
}
is OnrampError.AmountError.TooSmallError -> {
analyticsEventHandler.send(OnrampAnalyticsEvent.MaxAmountError)
R.string.onramp_min_amount_restriction
}
}
return OnrampAmountSecondaryFieldUM.Error(

View file

@ -21,6 +21,7 @@ import com.tangem.domain.onramp.model.OnrampAvailability
import com.tangem.domain.onramp.model.OnrampCurrency
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.wallets.usecase.GetWalletsUseCase
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.main.OnrampMainComponent
@ -32,6 +33,7 @@ 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
import com.tangem.features.onramp.utils.InputManager
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.PeriodicTask
@ -69,6 +71,7 @@ internal class OnrampMainComponentModel @Inject constructor(
)
private val amountStateFactory = OnrampAmountStateFactory(
currentStateProvider = Provider { _state.value },
analyticsEventHandler = analyticsEventHandler,
onrampIntents = this,
)
private val selectedUserWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
@ -111,9 +114,12 @@ internal class OnrampMainComponentModel @Inject constructor(
private fun checkResidenceCountry() {
modelScope.launch {
checkOnrampAvailabilityUseCase.invoke()
checkOnrampAvailabilityUseCase()
.onRight(::handleOnrampAvailability)
.onLeft { Timber.e(it.toString()) }
.onLeft { error ->
Timber.e(error.toString())
sendOnrampErrorAnalytic(error)
}
}
}
@ -129,9 +135,14 @@ internal class OnrampMainComponentModel @Inject constructor(
private fun subscribeToCurrencyUpdates() {
getOnrampCurrencyUseCase.invoke()
.onEach { maybeCurrency ->
val currency = maybeCurrency.getOrNull() ?: return@onEach
_state.update { amountStateFactory.getUpdatedCurrencyState(currency) }
updatePairsAndQuotes()
maybeCurrency.fold(
ifLeft = ::sendOnrampErrorAnalytic,
ifRight = { currency ->
if (currency == null) return@onEach
_state.update { amountStateFactory.getUpdatedCurrencyState(currency) }
updatePairsAndQuotes()
},
)
}
.launchIn(modelScope)
}
@ -146,7 +157,7 @@ internal class OnrampMainComponentModel @Inject constructor(
}
private suspend fun updatePairsAndQuotes() {
fetchPairsUseCase.invoke(params.cryptoCurrency)
fetchPairsUseCase.invoke(params.cryptoCurrency).onLeft(::sendOnrampErrorAnalytic)
startLoadingQuotes()
}
@ -165,7 +176,7 @@ internal class OnrampMainComponentModel @Inject constructor(
fetchQuotesUseCase.invoke(
amount = content.amountBlockState.amountFieldModel.fiatAmount,
cryptoCurrency = params.cryptoCurrency,
)
).onLeft(::sendOnrampErrorAnalytic)
}
},
onSuccess = {},
@ -176,20 +187,27 @@ internal class OnrampMainComponentModel @Inject constructor(
private fun subscribeToQuotesUpdate() {
getOnrampQuotesUseCase.invoke()
.onEach { maybeQuotes ->
val quotes = maybeQuotes.getOrNull() ?: return@onEach
val isNoQuotes = quotes.none { it is OnrampQuote.Data }
val quote = quotes.firstOrNull() ?: return@onEach
if (quote is OnrampQuote.Data && lastAmount.value != quote.fromAmount.value) {
lastAmount.value = quote.fromAmount.value
analyticsEventHandler.send(
OnrampAnalyticsEvent.ProviderCalculated(
providerName = quote.provider.info.name,
tokenSymbol = params.cryptoCurrency.symbol,
paymentMethod = quote.paymentMethod.name,
),
)
}
_state.update { amountStateFactory.getAmountSecondaryUpdatedState(quote, isNoQuotes) }
maybeQuotes.fold(
ifLeft = ::sendOnrampErrorAnalytic,
ifRight = { quotes ->
quotes.filterIsInstance<OnrampQuote.Error>().forEach { errorState ->
sendOnrampErrorAnalytic(errorState.error)
}
val isNoQuotes = quotes.none { it is OnrampQuote.Data }
val quote = quotes.firstOrNull() ?: return@onEach
if (quote is OnrampQuote.Data && lastAmount.value != quote.fromAmount.value) {
lastAmount.value = quote.fromAmount.value
analyticsEventHandler.send(
OnrampAnalyticsEvent.ProviderCalculated(
providerName = quote.provider.info.name,
tokenSymbol = params.cryptoCurrency.symbol,
paymentMethod = quote.paymentMethod.name,
),
)
}
_state.update { amountStateFactory.getAmountSecondaryUpdatedState(quote, isNoQuotes) }
},
)
}
.launchIn(modelScope)
}
@ -270,6 +288,16 @@ internal class OnrampMainComponentModel @Inject constructor(
)
}
private fun sendOnrampErrorAnalytic(error: OnrampError) {
val content = state.value as? OnrampMainComponentUM.Content
val providerContent = content?.providerBlockState as? OnrampProviderBlockUM.Content
analyticsEventHandler.sendOnrampErrorEvent(
error = error,
tokenSymbol = params.cryptoCurrency.symbol,
providerName = providerContent?.providerName,
)
}
private companion object {
const val UPDATE_DELAY = 10_000L
}

View file

@ -5,11 +5,12 @@ import com.tangem.core.ui.extensions.TextReference
internal sealed interface ProviderListItemUM {
val providerId: String
val name: String
data class Available(
override val providerId: String,
override val name: String,
val imageUrl: String,
val name: String,
val rate: String,
val isBestRate: Boolean,
val diffRate: TextReference,
@ -19,16 +20,16 @@ internal sealed interface ProviderListItemUM {
data class AvailableWithError(
override val providerId: String,
override val name: String,
val imageUrl: String,
val name: String,
val subtitle: TextReference,
val onClick: () -> Unit,
) : ProviderListItemUM
data class Unavailable(
override val providerId: String,
override val name: String,
val imageUrl: String,
val name: String,
val subtitle: TextReference,
) : ProviderListItemUM
}

View file

@ -25,6 +25,7 @@ import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.paymentmethod.entity.PaymentMethodUM
import com.tangem.features.onramp.providers.SelectProviderComponent
import com.tangem.features.onramp.providers.entity.*
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
import com.tangem.utils.StringsSigns.MINUS
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.ImmutableList
@ -60,7 +61,12 @@ internal class SelectProviderModel @Inject constructor(
private fun subscribeToPaymentMethodUpdates() {
getOnrampSelectedPaymentMethodUseCase.invoke()
.onEach { it.getOrNull()?.let(::getProviders) }
.onEach { maybePaymentMethod ->
maybePaymentMethod.fold(
ifLeft = ::sendOnrampErrorEvent,
ifRight = ::getProviders,
)
}
.launchIn(modelScope)
}
@ -76,7 +82,10 @@ internal class SelectProviderModel @Inject constructor(
)
}
}
.onLeft { Timber.e(it.toString()) }
.onLeft { error ->
sendOnrampErrorEvent(error)
Timber.e(error.toString())
}
}
}
@ -96,7 +105,13 @@ internal class SelectProviderModel @Inject constructor(
private fun openPaymentMethods() {
analyticsEventHandler.send(OnrampAnalyticsEvent.PaymentMethodsScreenOpened)
modelScope.launch {
val methods = getSelectedPaymentMethodsUseCase.invoke().getOrNull().orEmpty()
val methods = getSelectedPaymentMethodsUseCase().fold(
ifLeft = { error ->
sendOnrampErrorEvent(error)
emptySet()
},
ifRight = { it },
)
bottomSheetNavigation.activate(
ProviderListBottomSheetConfig.PaymentMethods(
selectedMethodId = _state.value.paymentMethod.id,
@ -221,6 +236,17 @@ internal class SelectProviderModel @Inject constructor(
params.onDismiss()
}
private fun sendOnrampErrorEvent(error: OnrampError) {
val selectedProvider = state.value.providers.firstOrNull {
it.providerId == params.selectedProviderId
}
analyticsEventHandler.sendOnrampErrorEvent(
error = error,
tokenSymbol = params.cryptoCurrency.symbol,
providerName = selectedProvider?.name,
)
}
/**
* Sorting providers by rule:
*

View file

@ -1,6 +1,7 @@
package com.tangem.features.onramp.redirect.model
import androidx.compose.ui.res.stringResource
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
@ -20,16 +21,19 @@ import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.redirect.OnrampRedirectComponent
import com.tangem.features.onramp.redirect.entity.OnrampRedirectTopBarUM
import com.tangem.features.onramp.redirect.entity.OnrampRedirectUM
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@Suppress("LongParameterList")
internal class OnrampRedirectModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val urlOpener: UrlOpener,
private val getOnrampRedirectUrlUseCase: GetOnrampRedirectUrlUseCase,
private val messageSender: UiMessageSender,
private val analyticsEventHandler: AnalyticsEventHandler,
router: Router,
paramsContainer: ParamsContainer,
) : Model() {
@ -79,7 +83,11 @@ internal class OnrampRedirectModel @Inject constructor(
private fun handleError(error: OnrampError) {
Timber.e(error.toString())
analyticsEventHandler.sendOnrampErrorEvent(
error = error,
tokenSymbol = params.cryptoCurrency.symbol,
providerName = params.onrampProviderWithQuote.provider.info.name,
)
val contentMessage = ContentMessage { onDismiss ->
BasicDialog(
message = stringResource(id = R.string.common_unknown_error),

View file

@ -60,7 +60,10 @@ internal class DefaultOnrampComponent @AssistedInject constructor(
when (config) {
OnrampChild.Settings -> settingsComponentFactory.create(
context = childByContext(componentContext),
params = OnrampSettingsComponent.Params(navigation::pop),
params = OnrampSettingsComponent.Params(
params.cryptoCurrency,
navigation::pop,
),
)
OnrampChild.Main -> onrampMainComponentFactory.create(
context = childByContext(componentContext),

View file

@ -2,10 +2,11 @@ package com.tangem.features.onramp.selectcountry
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.tokens.model.CryptoCurrency
internal interface SelectCountryComponent : ComposableBottomSheetComponent {
data class Params(val onDismiss: () -> Unit)
data class Params(val cryptoCurrency: CryptoCurrency, val onDismiss: () -> Unit)
interface Factory : ComponentFactory<Params, SelectCountryComponent>
}

View file

@ -21,6 +21,7 @@ import com.tangem.features.onramp.selectcountry.entity.transformer.UpdateCountry
import com.tangem.features.onramp.utils.InputManager
import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer
import com.tangem.features.onramp.utils.UpdateSearchQueryTransformer
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@ -61,10 +62,16 @@ internal class OnrampSelectCountryModel @Inject constructor(
@OptIn(ExperimentalCoroutinesApi::class)
private suspend fun subscribeOnUpdateState() {
combine(
flow = refreshTrigger.onStart { emit(Unit) }.flatMapLatest { flowOf(getOnrampCountriesUseCase.invoke()) },
flow2 = getOnrampCountryUseCase.invoke(),
flow = refreshTrigger.onStart { emit(Unit) }.flatMapLatest { flowOf(getOnrampCountriesUseCase()) },
flow2 = getOnrampCountryUseCase(),
flow3 = searchManager.query,
) { maybeCountries, maybeCountry, query ->
maybeCountries.onLeft {
analyticsEventHandler.sendOnrampErrorEvent(it, params.cryptoCurrency.symbol)
}
maybeCountry.onLeft {
analyticsEventHandler.sendOnrampErrorEvent(it, params.cryptoCurrency.symbol)
}
UpdateCountryItemsTransformer(
maybeCountries = maybeCountries,
defaultCountry = maybeCountry.getOrNull(),

View file

@ -2,10 +2,11 @@ package com.tangem.features.onramp.selectcurrency
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.domain.tokens.model.CryptoCurrency
internal interface SelectCurrencyComponent : ComposableBottomSheetComponent {
data class Params(val onDismiss: () -> Unit)
data class Params(val cryptoCurrency: CryptoCurrency, val onDismiss: () -> Unit)
interface Factory : ComponentFactory<Params, SelectCurrencyComponent>
}

View file

@ -22,6 +22,7 @@ import com.tangem.features.onramp.selectcurrency.entity.transformer.UpdateCurren
import com.tangem.features.onramp.utils.InputManager
import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer
import com.tangem.features.onramp.utils.UpdateSearchQueryTransformer
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@ -59,6 +60,9 @@ internal class OnrampSelectCurrencyModel @Inject constructor(
flow = refreshTrigger.onStart { emit(Unit) }.flatMapLatest { flowOf(getOnrampCurrenciesUseCase.invoke()) },
flow2 = searchManager.query,
) { maybeCurrencies, query ->
maybeCurrencies.onLeft {
analyticsEventHandler.sendOnrampErrorEvent(it, params.cryptoCurrency.symbol)
}
UpdateCurrencyItemsTransformer(
maybeCurrencies = maybeCurrencies,
query = query,

View file

@ -51,7 +51,10 @@ internal class DefaultOnrampSettingsComponent @AssistedInject constructor(
): ComposableBottomSheetComponent = when (config) {
OnrampSettingsConfig.SelectCountry -> selectCountryComponentFactory.create(
context = childByContext(componentContext),
params = SelectCountryComponent.Params(model.bottomSheetNavigation::dismiss),
params = SelectCountryComponent.Params(
params.cryptoCurrency,
model.bottomSheetNavigation::dismiss,
),
)
}

View file

@ -2,10 +2,11 @@ package com.tangem.features.onramp.settings
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.tokens.model.CryptoCurrency
internal interface OnrampSettingsComponent : ComposableContentComponent {
data class Params(val onBack: () -> Unit)
data class Params(val cryptoCurrency: CryptoCurrency, val onBack: () -> Unit)
interface Factory : ComponentFactory<Params, OnrampSettingsComponent>
}

View file

@ -15,6 +15,7 @@ import com.tangem.features.onramp.settings.OnrampSettingsComponent
import com.tangem.features.onramp.settings.entity.OnrampSettingsConfig
import com.tangem.features.onramp.settings.entity.OnrampSettingsItemUM
import com.tangem.features.onramp.settings.entity.OnrampSettingsUM
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.*
@ -46,19 +47,24 @@ internal class OnrampSettingsModel @Inject constructor(
}
private fun updateResidenceState(maybeCountry: Either<OnrampError, OnrampCountry?>) {
maybeCountry.onRight { country ->
_state.update { state ->
state.copy(
items = listOf(
OnrampSettingsItemUM.Residence(
countryName = country?.name.orEmpty(),
flagUrl = country?.image.orEmpty(),
onClick = ::openSelectCountryBottomSheet,
),
).toImmutableList(),
)
}
}
maybeCountry.fold(
ifLeft = {
analyticsEventHandler.sendOnrampErrorEvent(it, params.cryptoCurrency.symbol)
},
ifRight = { country ->
_state.update { state ->
state.copy(
items = listOf(
OnrampSettingsItemUM.Residence(
countryName = country?.name.orEmpty(),
flagUrl = country?.image.orEmpty(),
onClick = ::openSelectCountryBottomSheet,
),
).toImmutableList(),
)
}
},
)
}
private fun openSelectCountryBottomSheet() {

View file

@ -12,10 +12,12 @@ 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.tokens.GetCryptoCurrencyUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.features.onramp.component.OnrampSuccessComponent
import com.tangem.features.onramp.success.entity.OnrampSuccessClickIntents
import com.tangem.features.onramp.success.entity.OnrampSuccessComponentUM
import com.tangem.features.onramp.success.entity.conterter.SetOnrampSuccessContentConverter
import com.tangem.features.onramp.utils.sendOnrampErrorEvent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -74,8 +76,13 @@ internal class OnrampSuccessComponentModel @Inject constructor(
getOnrampStatusUseCase(externalTxId = params.externalTxId)
.fold(
ifLeft = {
Timber.e(it.toString())
ifLeft = { error ->
analyticsEventHandler.sendOnrampErrorEvent(
error = error,
tokenSymbol = cryptoCurrency.symbol,
providerName = transaction.providerName,
)
Timber.e(error.toString())
},
ifRight = { status ->
analyticsEventHandler.send(
@ -94,15 +101,25 @@ internal class OnrampSuccessComponentModel @Inject constructor(
goToProviderClick = ::goToProviderClick,
).convert(status)
}
removeTransactionIfTerminalStatus(status)
removeTransactionIfTerminalStatus(cryptoCurrency, transaction.providerName, status)
},
)
}
private fun removeTransactionIfTerminalStatus(status: OnrampStatus) {
private fun removeTransactionIfTerminalStatus(
cryptoCurrency: CryptoCurrency,
providerName: String,
status: OnrampStatus,
) {
modelScope.launch {
if (status.status.isTerminal) {
onrampRemoveTransactionUseCase(status.txId)
onrampRemoveTransactionUseCase(status.txId).onLeft { error ->
analyticsEventHandler.sendOnrampErrorEvent(
error = error,
tokenSymbol = cryptoCurrency.symbol,
providerName = providerName,
)
}
}
}
}

View file

@ -0,0 +1,32 @@
package com.tangem.features.onramp.utils
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
import com.tangem.domain.onramp.model.error.OnrampError
internal fun AnalyticsEventHandler.sendOnrampErrorEvent(
error: OnrampError,
tokenSymbol: String,
providerName: String? = null,
) = when (error) {
is OnrampError.DataError -> send(
OnrampAnalyticsEvent.Errors(
tokenSymbol = tokenSymbol,
providerName = providerName,
errorCode = error.code,
),
)
is OnrampError.DomainError -> {
send(
OnrampAnalyticsEvent.AppErrors(
tokenSymbol = tokenSymbol,
errorDescription = error.description.orEmpty(),
),
)
}
is OnrampError.AmountError.TooBigError,
is OnrampError.AmountError.TooSmallError,
OnrampError.RedirectError.VerificationFailed,
OnrampError.RedirectError.WrongRequestId,
-> { /* no-op */ }
}