Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-24 15:23:48 +05:00
commit 6b187159d3
15 changed files with 315 additions and 59 deletions

View file

@ -6,4 +6,6 @@ package com.tangem.features.swap.v2.api.subcomponents
*/
interface SwapAmountUpdateTrigger {
suspend fun triggerUpdateAmount(amountValue: String)
suspend fun triggerQuoteReload()
}

View file

@ -62,6 +62,8 @@ dependencies {
implementation(projects.domain.settings)
implementation(projects.domain.txhistory.models)
implementation(projects.domain.txhistory)
implementation(projects.domain.feedback.models)
implementation(projects.domain.feedback)
/** Compose */
implementation(deps.compose.foundation)

View file

@ -15,6 +15,8 @@ import javax.inject.Singleton
*/
interface SwapAmountUpdateListener {
val updateAmountTriggerFlow: Flow<String>
val reloadQuotesTriggerFlow: Flow<Unit>
}
/**
@ -54,10 +56,17 @@ internal class DefaultSwapAmountUpdateTrigger @Inject constructor() :
override val ignoreReduceTriggerFlow: SharedFlow<Unit>
field = MutableSharedFlow<Unit>()
override val reloadQuotesTriggerFlow: Flow<Unit>
field = MutableSharedFlow<Unit>()
override suspend fun triggerUpdateAmount(amountValue: String) {
updateAmountTriggerFlow.emit(amountValue)
}
override suspend fun triggerQuoteReload() {
reloadQuotesTriggerFlow.emit(Unit)
}
override suspend fun triggerReduceBy(reduceBy: ReduceByData) {
reduceByTriggerFlow.emit(reduceBy)
}

View file

@ -12,7 +12,7 @@ import com.tangem.utils.StringsSigns.PERCENT
import javax.inject.Inject
@ModelScoped
internal class SwapAlertFactory @Inject constructor(
internal class SwapAmountAlertFactory @Inject constructor(
private val uiMessageSender: UiMessageSender,
) {

View file

@ -40,6 +40,7 @@ import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import com.tangem.features.swap.v2.impl.amount.model.converter.SwapQuoteUMConverter
import com.tangem.features.swap.v2.impl.amount.model.transformers.*
import com.tangem.features.swap.v2.impl.chooseprovider.SwapChooseProviderComponent
import com.tangem.features.swap.v2.impl.common.SwapAlertFactory
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.orZero
@ -48,6 +49,7 @@ import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.math.BigDecimal
import javax.inject.Inject
import kotlin.properties.Delegates
@ -67,6 +69,7 @@ internal class SwapAmountModel @Inject constructor(
private val getAllowanceUseCase: GetAllowanceUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val appRouter: AppRouter,
private val swapAmountAlertFactory: SwapAmountAlertFactory,
private val swapAlertFactory: SwapAlertFactory,
private val swapAmountUpdateListener: SwapAmountUpdateListener,
private val swapAmountReduceListener: SwapAmountReduceListener,
@ -101,6 +104,7 @@ internal class SwapAmountModel @Inject constructor(
subscribeOnAmountReduceToTriggerUpdates()
subscribeOnAmountReduceByTriggerUpdates()
subscribeOnAmountIgnoreReduceTriggerUpdates()
subscribeOnReloadQuotesTriggerUpdates()
}
fun updateState(amountUM: SwapAmountUM) {
@ -130,7 +134,7 @@ internal class SwapAmountModel @Inject constructor(
val amountUM = uiState.value as? SwapAmountUM.Content ?: return
val selectedProvider = amountUM.selectedQuote.provider ?: return
swapAlertFactory.priceImpactAlert(
swapAmountAlertFactory.priceImpactAlert(
hasPriceImpact = (amountUM.secondaryAmount as? SwapAmountFieldUM.Content)?.priceImpact != null,
currencySymbol = amountUM.primaryCryptoCurrencyStatus.currency.symbol,
provider = selectedProvider,
@ -292,6 +296,12 @@ internal class SwapAmountModel @Inject constructor(
.launchIn(modelScope)
}
private fun subscribeOnReloadQuotesTriggerUpdates() {
swapAmountUpdateListener.reloadQuotesTriggerFlow
.onEach { loadQuotes(reloadFees = true) }
.launchIn(modelScope)
}
private fun observeChooseSelectToken() {
swapChooseTokenNetworkListener.swapChooseTokenNetworkResultFlow
.onEach { currency ->
@ -356,11 +366,19 @@ internal class SwapAmountModel @Inject constructor(
)
loadQuotes()
} else {
// todo not available currency to swap
Timber.e(
"""
Invalid cryptocurrencies status:
| Primary -> $primaryStatus
| Secondary -> $secondaryStatus
""".trimIndent(),
)
showErrorAlert(errorMessage = null)
}
},
ifLeft = {
// todo error
ifLeft = { error ->
Timber.e(error)
showErrorAlert(errorMessage = error.message)
},
)
}
@ -435,7 +453,7 @@ internal class SwapAmountModel @Inject constructor(
SwapQuoteUM.Error(
provider = provider,
expressError = error,
).takeIf { error is ExpressError.AmountError }
)
},
ifRight = { quote: SwapQuoteModel ->
SwapQuoteUMConverter(
@ -454,7 +472,7 @@ internal class SwapAmountModel @Inject constructor(
},
)
}
}.awaitAll().filterNotNull()
}.awaitAll()
uiState.transformerUpdate(
SwapAmountSetQuotesTransformer(
@ -490,6 +508,22 @@ internal class SwapAmountModel @Inject constructor(
params.callback.onAmountResult(uiState.value)
}
private fun showErrorAlert(errorMessage: String?) {
swapAlertFactory.getGenericErrorState(
expressError = ExpressError.UnknownError,
onFailedTxEmailClick = {
modelScope.launch {
swapAlertFactory.onFailedTxEmailClick(
userWallet = params.userWallet,
cryptoCurrency = primaryCryptoCurrency,
errorMessage = errorMessage,
)
}
},
popBack = appRouter::pop,
)
}
private fun configAmountNavigation() {
val params = params as? SwapAmountComponentParams.AmountParams ?: return
combine(

View file

@ -8,12 +8,14 @@ internal object SwapQuoteLoadingStateTransformer : Transformer<SwapAmountUM> {
override fun transform(prevState: SwapAmountUM): SwapAmountUM {
if (prevState !is SwapAmountUM.Content) return prevState
when (prevState.selectedQuote) {
is SwapQuoteUM.Allowance,
is SwapQuoteUM.Content,
-> return prevState // No need to set loading state if quotes are already display
else -> Unit
}
return prevState.copy(
selectedQuote = if (prevState.selectedQuote is SwapQuoteUM.Empty) {
SwapQuoteUM.Loading
} else {
prevState.selectedQuote
},
selectedQuote = SwapQuoteUM.Loading,
)
}
}

View file

@ -1,4 +1,4 @@
package com.tangem.features.swap.v2.impl.sendviaswap.confirm.model
package com.tangem.features.swap.v2.impl.common
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.express.models.ExpressRateType

View file

@ -0,0 +1,110 @@
package com.tangem.features.swap.v2.impl.common
import com.tangem.common.ui.alerts.TransactionErrorAlertConverter
import com.tangem.common.ui.alerts.models.AlertDemoModeUM
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.domain.express.models.ExpressError
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.requireColdWallet
import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.features.swap.v2.impl.R
import javax.inject.Inject
@ModelScoped
internal class SwapAlertFactory @Inject constructor(
private val uiMessageSender: UiMessageSender,
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
private val getCardInfoUseCase: GetCardInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
) {
fun getGenericErrorState(expressError: ExpressError, onFailedTxEmailClick: () -> Unit, popBack: () -> Unit = {}) {
uiMessageSender.send(
DialogMessage.Companion(
title = SwapUtils.getExpressErrorTitle(expressError),
message = SwapUtils.getExpressErrorMessage(expressError),
onDismissRequest = popBack,
dismissOnFirstAction = false,
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.common_support),
onClick = onFailedTxEmailClick,
)
},
secondActionBuilder = { cancelAction() },
),
)
}
fun getSendTransactionErrorState(
error: SendTransactionError?,
popBack: () -> Unit,
onFailedTxEmailClick: (String) -> Unit,
) {
val transactionErrorAlertConverter = TransactionErrorAlertConverter(
popBackStack = popBack,
onFailedTxEmailClick = onFailedTxEmailClick,
)
val errorAlert = error?.let { transactionErrorAlertConverter.convert(error) } ?: return
val onConfirmClick = errorAlert.onConfirmClick ?: return
uiMessageSender.send(
DialogMessage.Companion(
title = errorAlert.title,
message = errorAlert.message,
firstActionBuilder = {
EventMessageAction(
title = errorAlert.confirmButtonText,
onClick = onConfirmClick,
)
},
secondActionBuilder = if (errorAlert !is AlertDemoModeUM) {
{ cancelAction() }
} else {
null
},
),
)
}
suspend fun onFailedTxEmailClick(
userWallet: UserWallet,
cryptoCurrency: CryptoCurrency?,
errorMessage: String?,
txId: String? = null,
confirmData: ConfirmData? = null,
) {
saveBlockchainErrorUseCase(
error = BlockchainErrorInfo(
errorMessage = errorMessage.orEmpty(),
blockchainId = cryptoCurrency?.network?.rawId.orEmpty(),
derivationPath = cryptoCurrency?.network?.derivationPath?.value.orEmpty(),
destinationAddress = confirmData?.enteredDestination.orEmpty(),
tokenSymbol = confirmData?.toCryptoCurrencyStatus?.currency?.symbol.orEmpty(),
amount = confirmData?.enteredAmount?.toString().orEmpty(),
fee = confirmData?.fee?.amount?.value?.toString().orEmpty(),
),
)
val cardInfo =
getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return // TODO [REDACTED_TASK_KEY]
sendFeedbackEmailUseCase(
type = FeedbackEmailType.SwapProblem(
cardInfo = cardInfo,
providerName = confirmData?.quote?.provider?.name.orEmpty(),
txId = txId.orEmpty(),
),
)
}
}

View file

@ -3,12 +3,12 @@ package com.tangem.features.swap.v2.impl.notifications
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.ui.Modifier
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.common.ui.notifications.notifications
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.domain.express.models.ExpressError
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.swap.v2.impl.notifications.model.SwapNotificationsModel
import com.tangem.features.swap.v2.impl.notifications.ui.swapNotifications
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.StateFlow
@ -26,7 +26,7 @@ internal class SwapNotificationsComponent(
hasPaddingAbove: Boolean = false,
isClickDisabled: Boolean = false,
) {
notifications(
swapNotifications(
notifications = state,
modifier = modifier,
hasPaddingAbove = hasPaddingAbove,

View file

@ -7,6 +7,7 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.express.models.ExpressError
import com.tangem.features.swap.v2.api.subcomponents.SwapAmountUpdateTrigger
import com.tangem.features.swap.v2.impl.notifications.DefaultSwapNotificationsUpdateTrigger
import com.tangem.features.swap.v2.impl.notifications.SwapNotificationsComponent
import com.tangem.features.swap.v2.impl.notifications.SwapNotificationsComponent.Params.SwapNotificationData
@ -28,6 +29,7 @@ internal class SwapNotificationsModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val swapNotificationsUpdateListener: SwapNotificationsUpdateListener,
private val swapNotificationsUpdateTrigger: DefaultSwapNotificationsUpdateTrigger,
private val swapAmountUpdateTrigger: SwapAmountUpdateTrigger,
paramsContainer: ParamsContainer,
) : Model() {
@ -89,7 +91,9 @@ internal class SwapNotificationsModel @Inject constructor(
else -> SwapNotificationUM.Warning.ExpressGeneralError(
expressError = expressError,
onConfirmClick = {
// todo reload quotes
modelScope.launch {
swapAmountUpdateTrigger.triggerQuoteReload()
}
},
)
}

View file

@ -0,0 +1,52 @@
package com.tangem.features.swap.v2.impl.notifications.ui
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.swap.v2.impl.notifications.entity.SwapNotificationUM
import kotlinx.collections.immutable.ImmutableList
internal fun LazyListScope.swapNotifications(
notifications: ImmutableList<NotificationUM>,
modifier: Modifier = Modifier,
hasPaddingAbove: Boolean = false,
isClickDisabled: Boolean = false,
) {
itemsIndexed(
items = notifications,
key = { _, item -> item::class.java },
contentType = { _, item -> item::class.java },
itemContent = { i, item ->
val topPadding = if (i == 0 && hasPaddingAbove) 0.dp else 12.dp
Notification(
config = item.config,
modifier = modifier
.padding(top = topPadding)
.animateItem(),
containerColor = when (item) {
is SwapNotificationUM.Error,
is SwapNotificationUM.Warning.ExpressGeneralError,
is NotificationUM.Error.TokenExceedsBalance,
is NotificationUM.Warning.NetworkFeeUnreachable,
is NotificationUM.Warning.HighFeeError,
-> TangemTheme.colors.background.action
else -> TangemTheme.colors.button.disabled
},
iconTint = when (item) {
is SwapNotificationUM.Error,
is NotificationUM.Error.TokenExceedsBalance,
is NotificationUM.Warning,
-> null
is NotificationUM.Error -> TangemTheme.colors.icon.warning
is NotificationUM.Info -> TangemTheme.colors.icon.accent
},
isEnabled = !isClickDisabled,
)
},
)
}

View file

@ -10,9 +10,9 @@ import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.swap.models.SwapDirection
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.entity.PredefinedValues

View file

@ -4,6 +4,7 @@ import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.left
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.navigationButtons.NavigationButton
@ -13,6 +14,7 @@ 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.ui.extensions.resourceReference
import com.tangem.domain.express.models.ExpressError
import com.tangem.domain.express.models.ExpressProviderType
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase
@ -33,6 +35,8 @@ import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificat
import com.tangem.features.swap.v2.impl.R
import com.tangem.features.swap.v2.impl.amount.SwapAmountReduceTrigger
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import com.tangem.features.swap.v2.impl.common.ConfirmData
import com.tangem.features.swap.v2.impl.common.SwapAlertFactory
import com.tangem.features.swap.v2.impl.common.SwapUtils.INCREASE_GAS_LIMIT_FOR_CEX
import com.tangem.features.swap.v2.impl.common.entity.ConfirmUM
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
@ -53,7 +57,7 @@ import kotlinx.coroutines.launch
import java.math.BigDecimal
import com.tangem.utils.transformer.update as transformerUpdate
@Suppress("LongParameterList")
@Suppress("LongParameterList", "LargeClass")
@ModelScoped
internal class SendWithSwapConfirmModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
@ -68,6 +72,8 @@ internal class SendWithSwapConfirmModel @Inject constructor(
private val swapNotificationsUpdateListener: SwapNotificationsUpdateListener,
private val swapAmountReduceTrigger: SwapAmountReduceTrigger,
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
private val swapAlertFactory: SwapAlertFactory,
private val appRouter: AppRouter,
swapTransactionSenderFactory: SwapTransactionSender.Factory,
paramsContainer: ParamsContainer,
) : Model(), FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback {
@ -80,7 +86,6 @@ internal class SendWithSwapConfirmModel @Inject constructor(
val primaryCurrencyStatus: CryptoCurrencyStatus = params.primaryCryptoCurrencyStatusFlow.value
val secondaryCurrencyStatus: CryptoCurrencyStatus? = amountUM?.secondaryCryptoCurrencyStatus
val primaryFeePaidCurrencyStatus: CryptoCurrencyStatus = params.primaryFeePaidCurrencyStatusFlow.value
val primaryCurrency: CryptoCurrency = primaryCurrencyStatus.currency
val secondaryCurrency: CryptoCurrency = requireNotNull(amountUM?.secondaryCryptoCurrencyStatus?.currency) {
"Crypto currency must not be null"
}
@ -104,13 +109,14 @@ internal class SendWithSwapConfirmModel @Inject constructor(
onReverse = { amountUM.secondaryAmount },
)
val amountState = fromAmount?.amountField as? AmountState.Data
val isQuoteContent = amountUM?.selectedQuote is SwapQuoteUM.Content
return ConfirmData(
enteredAmount = amountState?.amountTextField?.cryptoAmount?.value,
reduceAmountBy = amountState?.reduceAmountBy.orZero(),
reduceAmountBy = amountState?.reduceAmountBy.takeIf { isQuoteContent }.orZero(),
isIgnoreReduce = amountState?.isIgnoreReduce == true,
enteredDestination = destinationUM?.addressTextField?.actualAddress,
fee = feeSelectorUM?.selectedFeeItem?.fee,
feeError = (uiState.value.feeSelectorUM as? FeeSelectorUM.Error)?.error,
fee = feeSelectorUM?.selectedFeeItem?.fee.takeIf { isQuoteContent },
feeError = (uiState.value.feeSelectorUM as? FeeSelectorUM.Error)?.error.takeIf { isQuoteContent },
fromCryptoCurrencyStatus = amountUM?.swapDirection?.withSwapDirection(
onDirect = { primaryCurrencyStatus },
onReverse = { secondaryCurrencyStatus },
@ -189,16 +195,11 @@ internal class SendWithSwapConfirmModel @Inject constructor(
suspend fun loadFee(): Either<GetFeeError, TransactionFee> {
val defaultError = GetFeeError.UnknownError.left()
val quote = amountUM?.selectedQuote as? SwapQuoteUM.Content ?: return defaultError
val amountUM = uiState.value.amountUM as? SwapAmountUM.Content ?: return defaultError
val amountField = amountUM.swapDirection.withSwapDirection(
onDirect = { amountUM.primaryAmount.amountField },
onReverse = { amountUM.secondaryAmount.amountField },
) as? AmountState.Data ?: return defaultError
val amountValue = amountField.amountTextField.cryptoAmount.value ?: return defaultError
val provider = (confirmData.quote as? SwapQuoteUM.Content)?.provider ?: return defaultError
val amountValue = confirmData.enteredAmount ?: return defaultError
return when (val providerType = quote.provider.type) {
return when (val providerType = provider.type) {
ExpressProviderType.CEX -> {
estimateFeeUseCase(
amount = amountValue,
@ -223,11 +224,37 @@ internal class SendWithSwapConfirmModel @Inject constructor(
swapTransactionSender.sendTransaction(
confirmData = confirmData,
isAmountSubtractAvailable = isAmountSubtractAvailable,
onExpressError = {
// todo error
onExpressError = { expressError ->
swapAlertFactory.getGenericErrorState(
expressError = ExpressError.UnknownError,
onFailedTxEmailClick = {
modelScope.launch {
swapAlertFactory.onFailedTxEmailClick(
userWallet = params.userWallet,
cryptoCurrency = confirmData.fromCryptoCurrencyStatus?.currency,
errorMessage = expressError.message,
confirmData = confirmData,
)
}
},
popBack = appRouter::pop,
)
},
onSendError = {
// todo error
onSendError = { error ->
swapAlertFactory.getSendTransactionErrorState(
error = error,
onFailedTxEmailClick = {
modelScope.launch {
swapAlertFactory.onFailedTxEmailClick(
userWallet = params.userWallet,
cryptoCurrency = confirmData.fromCryptoCurrencyStatus?.currency,
confirmData = confirmData,
errorMessage = error.toString(),
)
}
},
popBack = appRouter::pop,
)
},
onSendSuccess = { txHash, timestamp, data ->
val txUrl = getExplorerTransactionUrlUseCase(
@ -281,36 +308,22 @@ internal class SendWithSwapConfirmModel @Inject constructor(
}
private fun updateConfirmNotifications() {
val amountUM = uiState.value.amountUM as? SwapAmountUM.Content ?: return
val destinationUM = uiState.value.destinationUM as? DestinationUM.Content ?: return
val feeSelectorUMContent = uiState.value.feeSelectorUM as? FeeSelectorUM.Content
val feeSelectorUMError = uiState.value.feeSelectorUM as? FeeSelectorUM.Error
val amountField = amountUM.swapDirection.withSwapDirection(
onDirect = { amountUM.primaryAmount.amountField },
onReverse = { amountUM.secondaryAmount.amountField },
) as? AmountState.Data ?: return
val enteredDestination = destinationUM.addressTextField.actualAddress
modelScope.launch {
sendNotificationsUpdateTrigger.triggerUpdate(
data = NotificationData(
destinationAddress = enteredDestination,
destinationAddress = confirmData.enteredDestination.orEmpty(),
memo = null,
amountValue = amountField.amountTextField.cryptoAmount.value.orZero(),
reduceAmountBy = amountField.reduceAmountBy.orZero(),
isIgnoreReduce = amountField.isIgnoreReduce,
fee = feeSelectorUMContent?.selectedFeeItem?.fee,
feeError = feeSelectorUMError?.error,
amountValue = confirmData.enteredAmount.orZero(),
reduceAmountBy = confirmData.reduceAmountBy,
isIgnoreReduce = confirmData.isIgnoreReduce,
fee = confirmData.fee,
feeError = confirmData.feeError,
),
)
swapNotificationsUpdateTrigger.triggerUpdate(
data = SwapNotificationData(
expressError = (amountUM.selectedQuote as? SwapQuoteUM.Error)?.expressError,
fromCryptoCurrency = amountUM.swapDirection.withSwapDirection(
onDirect = { primaryCurrency },
onReverse = { secondaryCurrency },
),
expressError = (confirmData.quote as? SwapQuoteUM.Error)?.expressError,
fromCryptoCurrency = confirmData.fromCryptoCurrencyStatus?.currency,
),
)
uiState.transformerUpdate(

View file

@ -16,6 +16,7 @@ import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils
import com.tangem.features.swap.v2.impl.common.ConfirmData
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject

View file

@ -11,6 +11,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.express.models.ExpressError
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.swap.models.SwapDirection
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
@ -27,6 +28,7 @@ import com.tangem.features.swap.v2.api.SendWithSwapComponent
import com.tangem.features.swap.v2.impl.amount.SwapAmountComponent
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import com.tangem.features.swap.v2.impl.common.entity.ConfirmUM
import com.tangem.features.swap.v2.impl.common.SwapAlertFactory
import com.tangem.features.swap.v2.impl.sendviaswap.SendWithSwapRoute
import com.tangem.features.swap.v2.impl.sendviaswap.confirm.SendWithSwapConfirmComponent
import com.tangem.features.swap.v2.impl.sendviaswap.entity.SendWithSwapUM
@ -47,6 +49,7 @@ internal class SendWithSwapModel @Inject constructor(
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val swapAlertFactory: SwapAlertFactory,
paramsContainer: ParamsContainer,
) : Model(),
SwapAmountComponent.ModelCallback,
@ -133,7 +136,19 @@ internal class SendWithSwapModel @Inject constructor(
},
ifLeft = {
Timber.w(it.toString())
// todo send with swap error
swapAlertFactory.getGenericErrorState(
expressError = ExpressError.UnknownError,
onFailedTxEmailClick = {
modelScope.launch {
swapAlertFactory.onFailedTxEmailClick(
userWallet = userWallet,
cryptoCurrency = params.currency,
errorMessage = it.toString(),
)
}
},
popBack = ::onBackClick,
)
},
)
}
@ -173,8 +188,20 @@ internal class SendWithSwapModel @Inject constructor(
cryptoCurrencyStatus = cryptoCurrencyStatus,
).getOrNull() ?: cryptoCurrencyStatus
},
ifLeft = {
// todo send with swap error
ifLeft = { error ->
swapAlertFactory.getGenericErrorState(
expressError = ExpressError.UnknownError,
onFailedTxEmailClick = {
modelScope.launch {
swapAlertFactory.onFailedTxEmailClick(
userWallet = userWallet,
cryptoCurrency = params.currency,
errorMessage = error.toString(),
)
}
},
popBack = ::onBackClick,
)
},
)
}.launchIn(modelScope)