Updated on 2026-08-14

This commit is contained in:
Tangem 2024-12-03 20:17:14 +05:00
parent 26419a5754
commit b5a54b0e29
7 changed files with 105 additions and 14 deletions

View file

@ -305,7 +305,7 @@ internal class DefaultOnrampRepository(
toAmount = quote.toAmount.value,
)
} else {
error("Express signature invalid")
throw OnrampRedirectError.VerificationFailed
}
} catch (e: Exception) {
Timber.e(e)

View file

@ -0,0 +1,8 @@
package com.tangem.domain.onramp.model
sealed class OnrampRedirectError : Throwable() {
data class DataError(override val cause: Throwable?) : OnrampRedirectError()
data object VerificationFailed : OnrampRedirectError()
}

View file

@ -2,6 +2,7 @@ package com.tangem.domain.onramp
import arrow.core.Either
import com.tangem.domain.onramp.model.OnrampProviderWithQuote
import com.tangem.domain.onramp.model.OnrampRedirectError
import com.tangem.domain.onramp.repositories.OnrampRepository
import com.tangem.domain.onramp.repositories.OnrampTransactionRepository
import com.tangem.domain.tokens.model.CryptoCurrency
@ -16,7 +17,7 @@ class GetOnrampRedirectUrlUseCase(
userWalletId: UserWalletId,
quote: OnrampProviderWithQuote.Data,
cryptoCurrency: CryptoCurrency,
): Either<Throwable, String> {
): Either<OnrampRedirectError, String> {
return Either.catch {
val transaction = repository.getOnrampData(
userWalletId = userWalletId,
@ -25,6 +26,8 @@ class GetOnrampRedirectUrlUseCase(
)
transactionRepository.storeTransaction(transaction)
transaction.redirectUrl
}.mapLeft { throwable ->
if (throwable is OnrampRedirectError) throwable else OnrampRedirectError.DataError(throwable)
}
}
}

View file

@ -5,10 +5,12 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.onramp.model.OnrampProviderWithQuote
import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
internal interface OnrampMainComponent : ComposableContentComponent {
data class Params(
val userWalletId: UserWalletId,
val cryptoCurrency: CryptoCurrency,
val source: OnrampSource,
val openSettings: () -> Unit,

View file

@ -1,16 +1,26 @@
package com.tangem.features.onramp.main.model
import androidx.compose.ui.res.stringResource
import com.arkivanov.decompose.router.slot.SlotNavigation
import com.arkivanov.decompose.router.slot.activate
import com.tangem.common.ui.alerts.models.AlertDemoModeUM
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.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButtonUM
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.message.ContentMessage
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.onramp.*
import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent
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.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
@ -31,12 +41,15 @@ internal class OnrampMainComponentModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val analyticsEventHandler: AnalyticsEventHandler,
private val router: Router,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val checkOnrampAvailabilityUseCase: CheckOnrampAvailabilityUseCase,
private val getOnrampCurrencyUseCase: GetOnrampCurrencyUseCase,
private val clearOnrampCacheUseCase: ClearOnrampCacheUseCase,
private val fetchQuotesUseCase: OnrampFetchQuotesUseCase,
private val getOnrampQuotesUseCase: GetOnrampQuotesUseCase,
private val amountInputManager: InputManager,
private val messageSender: UiMessageSender,
getWalletsUseCase: GetWalletsUseCase,
paramsContainer: ParamsContainer,
) : Model(), OnrampIntents {
@ -50,7 +63,7 @@ internal class OnrampMainComponentModel @Inject constructor(
currentStateProvider = Provider { _state.value },
onrampIntents = this,
)
private val selectedUserWallet = getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId }
private val _state: MutableStateFlow<OnrampMainComponentUM> = MutableStateFlow(
value = stateFactory.getInitialState(
currency = params.cryptoCurrency.name,
@ -145,15 +158,19 @@ internal class OnrampMainComponentModel @Inject constructor(
}
override fun onBuyClick(quote: OnrampProviderWithQuote.Data) {
val currentContentState = state.value as? OnrampMainComponentUM.Content ?: return
analyticsEventHandler.send(
OnrampAnalyticsEvent.OnBuyClick(
providerName = quote.provider.info.name,
currency = currentContentState.amountBlockState.currencyUM.code,
cryptoCurrency = params.cryptoCurrency.name,
),
)
params.openRedirectPage(quote)
if (isDemoCardUseCase.invoke(selectedUserWallet.cardId)) {
showDemoWarning()
} else {
val currentContentState = state.value as? OnrampMainComponentUM.Content ?: return
analyticsEventHandler.send(
OnrampAnalyticsEvent.OnBuyClick(
providerName = quote.provider.info.name,
currency = currentContentState.amountBlockState.currencyUM.code,
cryptoCurrency = params.cryptoCurrency.name,
),
)
params.openRedirectPage(quote)
}
}
override fun openCurrenciesList() {
@ -175,4 +192,33 @@ internal class OnrampMainComponentModel @Inject constructor(
modelScope.launch { clearOnrampCacheUseCase.invoke() }
super.onDestroy()
}
private fun showDemoWarning() {
val alertUM = AlertDemoModeUM(onConfirmClick = {})
messageSender.send(
message = ContentMessage { onDismiss ->
val confirmButton = DialogButtonUM(
title = alertUM.confirmButtonText.resolveReference(),
onClick = {
alertUM.onConfirmClick()
onDismiss()
},
)
val dismissButton = DialogButtonUM(
title = stringResource(id = R.string.common_cancel),
onClick = onDismiss,
)
BasicDialog(
message = alertUM.message.resolveReference(),
confirmButton = confirmButton,
onDismissDialog = onDismiss,
title = alertUM.title.resolveReference(),
dismissButton = dismissButton,
)
},
)
}
}

View file

@ -1,14 +1,19 @@
package com.tangem.features.onramp.redirect.model
import androidx.compose.ui.res.stringResource
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.components.appbar.models.TopAppBarButtonUM
import com.tangem.core.ui.extensions.combinedReference
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.message.ContentMessage
import com.tangem.domain.onramp.GetOnrampRedirectUrlUseCase
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.redirect.OnrampRedirectComponent
@ -23,6 +28,7 @@ internal class OnrampRedirectModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val urlOpener: UrlOpener,
private val getOnrampRedirectUrlUseCase: GetOnrampRedirectUrlUseCase,
private val messageSender: UiMessageSender,
router: Router,
paramsContainer: ParamsContainer,
) : Model() {
@ -62,8 +68,33 @@ internal class OnrampRedirectModel @Inject constructor(
quote = params.onrampProviderWithQuote,
cryptoCurrency = params.cryptoCurrency,
)
.onLeft { Timber.e(it) }
.onRight { urlOpener.openUrl(it) }
.onLeft(::handleError)
.onRight {
params.onBack()
urlOpener.openUrl(it)
}
}
}
private fun handleError(throwable: Throwable) {
Timber.e(throwable)
val contentMessage = ContentMessage { onDismiss ->
BasicDialog(
message = stringResource(id = R.string.common_unknown_error),
confirmButton = DialogButtonUM(
title = stringResource(id = R.string.common_ok),
onClick = {
params.onBack()
onDismiss()
},
),
onDismissDialog = {
params.onBack()
onDismiss()
},
)
}
messageSender.send(contentMessage)
}
}

View file

@ -65,6 +65,7 @@ internal class DefaultOnrampComponent @AssistedInject constructor(
OnrampChild.Main -> onrampMainComponentFactory.create(
context = childByContext(componentContext),
params = OnrampMainComponent.Params(
userWalletId = params.userWalletId,
cryptoCurrency = params.cryptoCurrency,
openSettings = { navigation.push(OnrampChild.Settings) },
source = params.source,