Updated on 2026-08-14
This commit is contained in:
parent
3e4e7d8da0
commit
2bbf72b837
15 changed files with 315 additions and 59 deletions
|
|
@ -6,4 +6,6 @@ package com.tangem.features.swap.v2.api.subcomponents
|
|||
*/
|
||||
interface SwapAmountUpdateTrigger {
|
||||
suspend fun triggerUpdateAmount(amountValue: String)
|
||||
|
||||
suspend fun triggerQuoteReload()
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
) {
|
||||
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue