Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-16 20:54:25 +05:00
parent 77bc87a16e
commit 2d5e434c5f
4 changed files with 91 additions and 54 deletions

View file

@ -3,6 +3,7 @@ package com.tangem.features.onramp.success.entity
import com.tangem.common.ui.expressStatus.state.ExpressStatusUM
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.onramp.model.OnrampStatus
sealed class OnrampSuccessComponentUM {
@ -18,5 +19,6 @@ sealed class OnrampSuccessComponentUM {
val providerName: TextReference,
val providerImageUrl: String,
val notification: NotificationUM?,
val activeStatus: OnrampStatus.Status,
) : OnrampSuccessComponentUM()
}

View file

@ -51,6 +51,7 @@ internal class SetOnrampSuccessContentConverter(
status = value.status,
externalTxId = value.externalTxId,
),
activeStatus = value.status,
notification = getNotification(status = value.status, externalTxUrl = value.externalTxUrl),
)
}

View file

@ -6,6 +6,7 @@ import com.tangem.common.ui.expressStatus.state.ExpressStatusItemUM
import com.tangem.common.ui.expressStatus.state.ExpressStatusUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.onramp.model.OnrampStatus
import com.tangem.features.onramp.impl.R
import com.tangem.features.onramp.success.entity.OnrampSuccessComponentUM
import kotlinx.collections.immutable.persistentListOf
@ -24,6 +25,7 @@ internal data object OnrampSuccessComponentUMPreviewData {
toAmount = stringReference("99.99 $"),
currencyImageUrl = "",
notification = null,
activeStatus = OnrampStatus.Status.Verifying,
statusBlock = ExpressStatusUM(
title = resourceReference(R.string.express_exchange_status_title),
link = ExpressLinkUM.Empty,

View file

@ -21,6 +21,7 @@ import com.tangem.domain.onramp.model.error.OnrampError
import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.analytics.TokenOnrampAnalyticsEvent
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.onramp.component.OnrampSuccessComponent
import com.tangem.features.onramp.impl.R
@ -29,6 +30,8 @@ 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 com.tangem.utils.coroutines.PeriodicTask
import com.tangem.utils.coroutines.SingleTaskScheduler
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@ -36,6 +39,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
import kotlin.properties.Delegates
@Suppress("LongParameterList")
internal class OnrampSuccessComponentModel @Inject constructor(
@ -60,6 +64,10 @@ internal class OnrampSuccessComponentModel @Inject constructor(
val state: StateFlow<OnrampSuccessComponentUM> get() = _state.asStateFlow()
private var userWallet: UserWallet by Delegates.notNull()
private var cryptoCurrency: CryptoCurrency by Delegates.notNull()
private var expressTxStatusTaskScheduler = SingleTaskScheduler<Unit>()
init {
loadData()
}
@ -82,67 +90,87 @@ internal class OnrampSuccessComponentModel @Inject constructor(
showErrorAlert(error)
},
ifRight = { transaction ->
loadTransactionStatus(transaction)
userWallet = getUserWalletUseCase(transaction.userWalletId).getOrElse {
Timber.e("UserWallet found")
// this case should never happened
showErrorAlert(OnrampError.DomainError("UserWallet not found"))
return@launch
}
cryptoCurrency = getCryptoCurrencyUseCase(
userWallet = userWallet,
cryptoCurrencyId = transaction.toCurrencyId,
).getOrElse {
Timber.e("Crypto currency not found")
showErrorAlert(OnrampError.DomainError(null))
return@launch
}
startStatusUpdateTask(transaction)
},
)
}
}
private suspend fun loadTransactionStatus(transaction: OnrampTransaction) {
val userWallet = getUserWalletUseCase(transaction.userWalletId).getOrNull()
if (userWallet == null) {
Timber.e("UserWallet found")
// this case should never happened
showErrorAlert(OnrampError.DomainError("UserWallet not found"))
return
}
val cryptoCurrency = getCryptoCurrencyUseCase(
userWallet = userWallet,
cryptoCurrencyId = transaction.toCurrencyId,
).getOrElse {
Timber.e("Crypto currency not found")
showErrorAlert(OnrampError.DomainError(null))
return
}
getOnrampStatusUseCase(userWallet = userWallet, txId = transaction.txId)
.fold(
ifLeft = { error ->
analyticsEventHandler.sendOnrampErrorEvent(
error = error,
tokenSymbol = cryptoCurrency.symbol,
providerName = transaction.providerName,
paymentMethod = transaction.paymentMethod,
)
Timber.e(error.toString())
showErrorAlert(error)
},
ifRight = { status ->
analyticsEventHandler.send(
OnrampAnalyticsEvent.SuccessScreenOpened(
providerName = transaction.providerName,
currency = transaction.fromCurrency.code,
tokenSymbol = cryptoCurrency.symbol,
residence = transaction.residency,
paymentMethod = transaction.paymentMethod,
),
)
_state.update {
SetOnrampSuccessContentConverter(
cryptoCurrency = cryptoCurrency,
transaction = transaction,
goToProviderClick = ::goToProviderClick,
onCopyClick = ::onCopyClick,
).convert(status)
private fun startStatusUpdateTask(transaction: OnrampTransaction) {
expressTxStatusTaskScheduler.cancelTask()
expressTxStatusTaskScheduler.scheduleTask(
modelScope,
PeriodicTask(
isDelayFirst = false,
delay = EXPRESS_STATUS_UPDATE_DELAY,
task = {
runCatching {
loadTransactionStatus(transaction)
}
removeTransactionIfTerminalStatus(
cryptoCurrency = cryptoCurrency,
providerName = transaction.providerName,
paymentMethod = transaction.paymentMethod,
status = status,
)
},
)
onSuccess = { /* no-op */ },
onError = { /* no-op */ },
),
)
}
private suspend fun loadTransactionStatus(transaction: OnrampTransaction) {
val isTerminal = (state.value as? OnrampSuccessComponentUM.Content)?.activeStatus?.isTerminal
if (isTerminal == null || isTerminal == false) {
getOnrampStatusUseCase(userWallet = userWallet, txId = transaction.txId)
.fold(
ifLeft = { error ->
analyticsEventHandler.sendOnrampErrorEvent(
error = error,
tokenSymbol = cryptoCurrency.symbol,
providerName = transaction.providerName,
paymentMethod = transaction.paymentMethod,
)
Timber.e(error.toString())
showErrorAlert(error)
},
ifRight = { status ->
analyticsEventHandler.send(
OnrampAnalyticsEvent.SuccessScreenOpened(
providerName = transaction.providerName,
currency = transaction.fromCurrency.code,
tokenSymbol = cryptoCurrency.symbol,
residence = transaction.residency,
paymentMethod = transaction.paymentMethod,
),
)
_state.update {
SetOnrampSuccessContentConverter(
cryptoCurrency = cryptoCurrency,
transaction = transaction,
goToProviderClick = ::goToProviderClick,
onCopyClick = ::onCopyClick,
).convert(status)
}
removeTransactionIfTerminalStatus(
cryptoCurrency = cryptoCurrency,
providerName = transaction.providerName,
paymentMethod = transaction.paymentMethod,
status = status,
)
},
)
}
}
private fun showErrorAlert(error: OnrampError) {
@ -178,4 +206,8 @@ internal class OnrampSuccessComponentModel @Inject constructor(
}
}
}
private companion object {
const val EXPRESS_STATUS_UPDATE_DELAY = 10_000L
}
}