From 7504fe1822f05e90f8dee1dfb06bc221b59ac335 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 25 Sep 2024 20:30:40 +0500 Subject: [PATCH 1/3] Updated on 2026-08-14 --- .../domain/feedback/FeedbackDataBuilder.kt | 5 ++++ .../feedback/models/FeedbackEmailType.kt | 6 ++++ .../utils/EmailMessageBodyResolver.kt | 30 +++++++++++++++++++ .../utils/EmailMessageTitleResolver.kt | 1 + .../feedback/utils/EmailSubjectResolver.kt | 6 ++-- 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt index 91f1f6b990..654e910d2d 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/FeedbackDataBuilder.kt @@ -95,6 +95,11 @@ internal class FeedbackDataBuilder { ) } + fun addSwapInfo(providerName: String, txId: String) { + builder.appendKeyValue("Provider", providerName) + builder.appendKeyValue("Transaction ID", txId) + } + fun addDelimiter(): StringBuilder = builder.appendDelimiter() fun build(): String = builder.trimEnd().toString() diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt index 735365aeac..6abec03efc 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/models/FeedbackEmailType.kt @@ -30,4 +30,10 @@ sealed interface FeedbackEmailType { val transactionTypes: List, val unsignedTransactions: List, ) : FeedbackEmailType + + data class SwapProblem( + override val cardInfo: CardInfo, + val providerName: String, + val txId: String, + ) : FeedbackEmailType } \ No newline at end of file diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt index ae17e1da30..5f9e52a0e2 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageBodyResolver.kt @@ -29,6 +29,7 @@ internal class EmailMessageBodyResolver( type.transactionTypes, type.unsignedTransactions, ) + is FeedbackEmailType.SwapProblem -> addSwapProblemBody(type.cardInfo, type.providerName, type.txId) } return build() @@ -108,6 +109,35 @@ internal class EmailMessageBodyResolver( addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo()) } + private suspend fun FeedbackDataBuilder.addSwapProblemBody( + cardInfo: CardInfo, + providerName: String, + txId: String, + ) { + addCardInfo(cardInfo) + addDelimiter() + + val userWalletId = requireNotNull(cardInfo.userWalletId) { "UserWalletId must be not null" } + val blockchainError = feedbackRepository.getBlockchainErrorInfo(userWalletId = userWalletId) + val blockchainInfo = blockchainError?.let { + feedbackRepository.getBlockchainInfo( + userWalletId = userWalletId, + blockchainId = blockchainError.blockchainId, + derivationPath = blockchainError.derivationPath, + ) + } + + if (blockchainInfo != null) { + addBlockchainError(blockchainInfo, blockchainError) + addDelimiter() + } + + addSwapInfo(providerName, txId) + addDelimiter() + + addPhoneInfo(phoneInfo = feedbackRepository.getPhoneInfo()) + } + private fun FeedbackDataBuilder.addCardAndPhoneInfo(cardInfo: CardInfo) { addCardInfo(cardInfo) addDelimiter() diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt index bfa9cfdede..5f052face7 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailMessageTitleResolver.kt @@ -21,6 +21,7 @@ internal class EmailMessageTitleResolver(private val resources: Resources) { is FeedbackEmailType.ScanningProblem -> R.string.feedback_preface_scan_failed is FeedbackEmailType.TransactionSendingProblem, is FeedbackEmailType.StakingProblem, + is FeedbackEmailType.SwapProblem, -> R.string.feedback_preface_tx_failed } .let(resources::getString) diff --git a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt index 503f1f514b..ba9c710413 100644 --- a/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt +++ b/domain/feedback/src/main/java/com/tangem/domain/feedback/utils/EmailSubjectResolver.kt @@ -25,8 +25,10 @@ internal class EmailSubjectResolver(private val resources: Resources) { } is FeedbackEmailType.RateCanBeBetter -> R.string.feedback_subject_rate_negative is FeedbackEmailType.ScanningProblem -> R.string.feedback_subject_scan_failed - is FeedbackEmailType.TransactionSendingProblem -> R.string.feedback_subject_tx_failed - is FeedbackEmailType.StakingProblem -> R.string.feedback_subject_tx_failed + is FeedbackEmailType.TransactionSendingProblem, + is FeedbackEmailType.StakingProblem, + is FeedbackEmailType.SwapProblem, + -> R.string.feedback_subject_tx_failed } .let(resources::getString) } From 321caf73a87d1953c3a7a9594e16c39536edfef9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 25 Sep 2024 20:31:05 +0500 Subject: [PATCH 2/3] Updated on 2026-08-14 --- .../presentation/ui/StakingEventEffect.kt | 34 ++---- features/swap/domain/models/build.gradle.kts | 1 + .../domain/models/ui/SwapTransactionState.kt | 23 ++-- .../feature/swap/domain/SwapInteractorImpl.kt | 46 ++----- features/swap/presentation/build.gradle.kts | 7 +- .../SwapTransactionErrorStateConverter.kt | 33 +++++ .../tangem/feature/swap/models/SwapAlertUM.kt | 38 ++++++ .../feature/swap/models/SwapStateHolder.kt | 12 +- .../swap/models/states/events/SwapEvent.kt | 11 ++ .../tangem/feature/swap/ui/StateBuilder.kt | 114 +++++++----------- .../tangem/feature/swap/ui/SwapEventEffect.kt | 67 ++++++++++ .../feature/swap/ui/SwapScreenContent.kt | 19 +-- .../tangem/feature/swap/utils/SwapUtils.kt | 41 +++++++ .../feature/swap/viewmodels/SwapViewModel.kt | 103 +++++++++++----- 14 files changed, 357 insertions(+), 192 deletions(-) create mode 100644 features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/SwapTransactionErrorStateConverter.kt create mode 100644 features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapAlertUM.kt create mode 100644 features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/events/SwapEvent.kt create mode 100644 features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapEventEffect.kt create mode 100644 features/swap/presentation/src/main/java/com/tangem/feature/swap/utils/SwapUtils.kt diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingEventEffect.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingEventEffect.kt index b134e9d549..c67e77ecd9 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingEventEffect.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingEventEffect.kt @@ -50,29 +50,17 @@ internal fun StakingEventEffect(event: StateEvent, snackbarHostSta @Composable internal fun StakingAlert(state: AlertUM, onDismiss: () -> Unit) { - val confirmButton: DialogButtonUM - val dismissButton: DialogButtonUM? - - val onActionClick = state.onConfirmClick - if (onActionClick != null) { - confirmButton = DialogButtonUM( - title = state.confirmButtonText.resolveReference(), - onClick = { - onActionClick() - onDismiss() - }, - ) - dismissButton = DialogButtonUM( - title = stringResource(id = R.string.common_cancel), - onClick = onDismiss, - ) - } else { - confirmButton = DialogButtonUM( - title = state.confirmButtonText.resolveReference(), - onClick = onDismiss, - ) - dismissButton = null - } + val confirmButton = DialogButtonUM( + title = state.confirmButtonText.resolveReference(), + onClick = { + state.onConfirmClick() + onDismiss() + }, + ) + val dismissButton = DialogButtonUM( + title = stringResource(id = R.string.common_cancel), + onClick = onDismiss, + ) BasicDialog( message = state.message.resolveReference(), diff --git a/features/swap/domain/models/build.gradle.kts b/features/swap/domain/models/build.gradle.kts index 2ec7133686..431789f3d2 100644 --- a/features/swap/domain/models/build.gradle.kts +++ b/features/swap/domain/models/build.gradle.kts @@ -12,6 +12,7 @@ android { dependencies { /** Domain */ implementation(projects.domain.tokens.models) + implementation(projects.domain.transaction.models) /** Core modules */ implementation(projects.core.utils) diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapTransactionState.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapTransactionState.kt index bd29c42170..6db16f52c8 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapTransactionState.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapTransactionState.kt @@ -1,6 +1,7 @@ package com.tangem.feature.swap.domain.models.ui -import com.tangem.feature.swap.domain.models.DataError +import com.tangem.domain.transaction.error.SendTransactionError +import com.tangem.feature.swap.domain.models.ExpressDataError import java.math.BigDecimal sealed class SwapTransactionState { @@ -15,17 +16,13 @@ sealed class SwapTransactionState { val timestamp: Long, ) : SwapTransactionState() - data object UserCancelled : SwapTransactionState() - - data object BlockchainError : SwapTransactionState() - - data object TangemSdkError : SwapTransactionState() - - data object NetworkError : SwapTransactionState() - - data object UnknownError : SwapTransactionState() - - data class ExpressError(val dataError: DataError) : SwapTransactionState() - data object DemoMode : SwapTransactionState() + + sealed class Error : SwapTransactionState() { + data class TransactionError(val error: SendTransactionError?) : Error() + + data class ExpressError(val error: ExpressDataError) : Error() + + data object UnknownError : Error() + } } \ No newline at end of file diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 2a1a873a96..541327a97a 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -22,7 +22,6 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.transaction.error.GetFeeError -import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.transaction.models.TransactionType import com.tangem.domain.transaction.usecase.* import com.tangem.domain.utils.convertToSdkAmount @@ -227,7 +226,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( ), ).getOrElse { Timber.e(it, "Failed to create approveTransaction") - return SwapTransactionState.UnknownError + return SwapTransactionState.Error.UnknownError } val result = sendTransactionUseCase( @@ -243,16 +242,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( timestamp = System.currentTimeMillis(), ) }, - ifLeft = { - when (it) { - SendTransactionError.UserCancelledError -> SwapTransactionState.UserCancelled - is SendTransactionError.BlockchainSdkError -> SwapTransactionState.BlockchainError - is SendTransactionError.TangemSdkError -> SwapTransactionState.TangemSdkError - is SendTransactionError.NetworkError -> SwapTransactionState.NetworkError - is SendTransactionError.DemoCardError -> SwapTransactionState.DemoMode - else -> SwapTransactionState.UnknownError - } - }, + ifLeft = { SwapTransactionState.Error.TransactionError(it) }, ) } @@ -591,7 +581,6 @@ internal class SwapInteractorImpl @AssistedInject constructor( """.trimIndent(), ) - val userWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: return SwapTransactionState.UnknownError val cardId = userWallet.scanResponse.card.cardId if (isDemoCardUseCase(cardId)) return SwapTransactionState.DemoMode @@ -693,12 +682,12 @@ internal class SwapInteractorImpl @AssistedInject constructor( hash = dataToSign, ).getOrElse { Timber.e(it, "Failed to create swap dex tx data") - return SwapTransactionState.UnknownError + return SwapTransactionState.Error.UnknownError } val result = sendTransactionUseCase( txData = txData, - userWallet = getUserWalletUseCase(userWalletId).getOrElse { return SwapTransactionState.UnknownError }, + userWallet = userWallet, network = currencyToSendStatus.currency.network, ) return result.fold( @@ -738,7 +727,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( timestamp = System.currentTimeMillis(), ) }, - ifLeft = { handleSendTxError(it) }, + ifLeft = { SwapTransactionState.Error.TransactionError(it) }, ) } @@ -773,14 +762,14 @@ internal class SwapInteractorImpl @AssistedInject constructor( toAddress = currencyToGet.value.networkAddress?.defaultAddress?.value.orEmpty(), refundAddress = currencyToSend.value.networkAddress?.defaultAddress?.value, refundExtraId = null, // currently always null - ).getOrElse { return SwapTransactionState.ExpressError(it) } + ).getOrElse { return SwapTransactionState.Error.ExpressError(it) } val exchangeDataCex = - exchangeData.transaction as? ExpressTransactionModel.CEX ?: return SwapTransactionState.UnknownError + exchangeData.transaction as? ExpressTransactionModel.CEX ?: return SwapTransactionState.Error.UnknownError val cardId = userWallet.scanResponse.card.cardId - if (isDemoCardUseCase(cardId)) return SwapTransactionState.UnknownError + if (isDemoCardUseCase(cardId)) return SwapTransactionState.Error.UnknownError val txData = createTransactionUseCase( amount = amount.value.convertToSdkAmount(currencyToSend.currency), @@ -794,11 +783,11 @@ internal class SwapInteractorImpl @AssistedInject constructor( network = currencyToSend.currency.network, ).getOrElse { Timber.e(it, "Failed to create swap CEX tx data") - return SwapTransactionState.UnknownError + return SwapTransactionState.Error.UnknownError } if (txData.extras == null && exchangeDataCex.txExtraId != null) { - return SwapTransactionState.UnknownError + return SwapTransactionState.Error.UnknownError } val result = sendTransactionUseCase( @@ -809,9 +798,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( val derivationPath = currencyToSend.currency.network.derivationPath.value return result.fold( - ifLeft = { - handleSendTxError(it) - }, + ifLeft = { SwapTransactionState.Error.TransactionError(it) }, ifRight = { txHash -> repository.exchangeSent( txId = exchangeDataCex.txId, @@ -856,17 +843,6 @@ internal class SwapInteractorImpl @AssistedInject constructor( ) } - private fun handleSendTxError(txError: SendTransactionError?): SwapTransactionState { - return when (txError) { - SendTransactionError.UserCancelledError -> SwapTransactionState.UserCancelled - is SendTransactionError.BlockchainSdkError -> SwapTransactionState.BlockchainError - is SendTransactionError.TangemSdkError -> SwapTransactionState.TangemSdkError - is SendTransactionError.NetworkError -> SwapTransactionState.NetworkError - is SendTransactionError.DemoCardError -> SwapTransactionState.DemoMode - else -> SwapTransactionState.UnknownError - } - } - private fun getFeeForTransaction(fee: TxFee, blockchain: Blockchain): Fee { val feeAmountValue = fee.feeValue val feeAmount = Amount( diff --git a/features/swap/presentation/build.gradle.kts b/features/swap/presentation/build.gradle.kts index bea28f028c..3b0dbe2d6d 100644 --- a/features/swap/presentation/build.gradle.kts +++ b/features/swap/presentation/build.gradle.kts @@ -24,6 +24,7 @@ dependencies { implementation(projects.core.decompose) // For Route supertype /** Domain modules **/ + implementation(projects.domain.models) implementation(projects.domain.appCurrency) implementation(projects.domain.appCurrency.models) implementation(projects.domain.balanceHiding) @@ -31,13 +32,17 @@ dependencies { implementation(projects.domain.tokens) implementation(projects.domain.tokens.models) implementation(projects.domain.transaction) + implementation(projects.domain.transaction.models) implementation(projects.domain.wallets) implementation(projects.domain.wallets.models) implementation(projects.domain.settings) + implementation(projects.domain.staking) + implementation(projects.domain.feedback) + + /** Feature modules */ implementation(projects.features.swap.domain) implementation(projects.features.swap.domain.api) implementation(projects.features.swap.domain.models) - implementation(projects.domain.staking) /** AndroidX */ implementation(deps.androidx.activity.compose) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/SwapTransactionErrorStateConverter.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/SwapTransactionErrorStateConverter.kt new file mode 100644 index 0000000000..ad158cc32e --- /dev/null +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/converters/SwapTransactionErrorStateConverter.kt @@ -0,0 +1,33 @@ +package com.tangem.feature.swap.converters + +import com.tangem.common.ui.alerts.TransactionErrorAlertConverter +import com.tangem.common.ui.alerts.models.AlertUM +import com.tangem.domain.transaction.error.SendTransactionError +import com.tangem.feature.swap.domain.models.ui.SwapTransactionState +import com.tangem.feature.swap.models.SwapAlertUM +import com.tangem.feature.swap.utils.getExpressErrorMessage +import com.tangem.utils.converter.Converter + +internal class SwapTransactionErrorStateConverter( + private val onDismiss: () -> Unit, + private val onSupportClick: (String) -> Unit, +) : Converter { + override fun convert(value: SwapTransactionState.Error): AlertUM? { + return when (value) { + is SwapTransactionState.Error.TransactionError -> { + when (val error = value.error) { + is SendTransactionError.UserCancelledError -> return null + null -> SwapAlertUM.GenericError(onDismiss) + else -> TransactionErrorAlertConverter(onDismiss, onSupportClick).convert(error) + } + } + is SwapTransactionState.Error.ExpressError -> { + SwapAlertUM.ExpressErrorAlert( + message = getExpressErrorMessage(value.error), + onConfirmClick = { onSupportClick(value.error.code.toString()) }, + ) + } + SwapTransactionState.Error.UnknownError -> SwapAlertUM.GenericError(onDismiss) + } + } +} \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapAlertUM.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapAlertUM.kt new file mode 100644 index 0000000000..f99c9278ea --- /dev/null +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapAlertUM.kt @@ -0,0 +1,38 @@ +package com.tangem.feature.swap.models + +import com.tangem.common.ui.alerts.models.AlertUM +import com.tangem.core.ui.R +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference + +sealed class SwapAlertUM : AlertUM { + + data class GenericError( + override val onConfirmClick: (() -> Unit), + override val message: TextReference = resourceReference(R.string.common_unknown_error), + ) : SwapAlertUM() { + override val title: TextReference? = null + override val confirmButtonText: TextReference = + resourceReference(id = R.string.common_support) + } + + data class ExpressErrorAlert( + override val message: TextReference = resourceReference(R.string.common_unknown_error), + override val onConfirmClick: (() -> Unit), + ) : SwapAlertUM() { + override val title: TextReference? = null + override val confirmButtonText: TextReference = + resourceReference(id = R.string.common_support) + } + + data class FeesAlert( + override val message: TextReference, + override val onConfirmClick: (() -> Unit), + ) : SwapAlertUM() { + override val title: TextReference = resourceReference( + com.tangem.feature.swap.presentation.R.string.swapping_alert_title, + ) + override val confirmButtonText: TextReference = + resourceReference(id = R.string.common_ok) + } +} \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt index 003f258f6f..8b21ec6919 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt @@ -6,18 +6,21 @@ import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionState import com.tangem.core.ui.R import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.notifications.NotificationConfig +import com.tangem.core.ui.event.StateEvent +import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.swap.domain.models.ui.PriceImpact import com.tangem.feature.swap.models.states.FeeItemState import com.tangem.feature.swap.models.states.ProviderState +import com.tangem.feature.swap.models.states.events.SwapEvent -data class SwapStateHolder( +internal data class SwapStateHolder( val sendCardData: SwapCardState, val receiveCardData: SwapCardState, val blockchainId: String, // not the same as networkId, its local id in app val warnings: List = emptyList(), - val alert: SwapWarning.GenericWarning? = null, + val event: StateEvent = consumedEvent(), val changeCardsButtonState: ChangeCardsButtonState = ChangeCardsButtonState.ENABLED, val providerState: ProviderState, @@ -110,7 +113,6 @@ sealed interface SwapWarning { data class GenericWarning( val title: TextReference? = null, val message: TextReference? = null, - val type: GenericWarningType = GenericWarningType.OTHER, val onClick: () -> Unit, ) : SwapWarning @@ -133,10 +135,6 @@ sealed interface SwapWarning { } } -enum class GenericWarningType { - NETWORK, OTHER -} - enum class ChangeCardsButtonState { ENABLED, DISABLED, UPDATE_IN_PROGRESS } \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/events/SwapEvent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/events/SwapEvent.kt new file mode 100644 index 0000000000..1741d69029 --- /dev/null +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/events/SwapEvent.kt @@ -0,0 +1,11 @@ +package com.tangem.feature.swap.models.states.events + +import androidx.compose.runtime.Immutable +import com.tangem.common.ui.alerts.models.AlertUM + +@Immutable +internal sealed class SwapEvent { + data class ShowAlert(val alert: AlertUM) : SwapEvent() + + data class ShowShareDialog(val txUrl: String) : SwapEvent() +} \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 34bf5fb2ec..2575e2d24b 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -2,15 +2,19 @@ package com.tangem.feature.swap.ui import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue +import com.tangem.common.ui.alerts.models.AlertDemoModeUM import com.tangem.common.ui.bottomsheet.permission.state.* import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.components.notifications.NotificationConfig +import com.tangem.core.ui.event.consumedEvent +import com.tangem.core.ui.event.triggeredEvent import com.tangem.core.ui.extensions.* import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.feature.swap.converters.SwapTransactionErrorStateConverter import com.tangem.feature.swap.converters.TokensDataConverter import com.tangem.feature.swap.domain.models.DataError import com.tangem.feature.swap.domain.models.SwapAmount @@ -19,7 +23,10 @@ import com.tangem.feature.swap.domain.models.formatToUIRepresentation import com.tangem.feature.swap.domain.models.ui.* import com.tangem.feature.swap.models.* import com.tangem.feature.swap.models.states.* +import com.tangem.feature.swap.models.states.events.SwapEvent import com.tangem.feature.swap.presentation.R +import com.tangem.feature.swap.utils.getExpressErrorMessage +import com.tangem.feature.swap.utils.getExpressErrorTitle import com.tangem.feature.swap.viewmodels.SwapProcessDataState import com.tangem.utils.Provider import com.tangem.utils.StringsSigns.DASH_SIGN @@ -1045,81 +1052,43 @@ internal class StateBuilder( ) } - fun createErrorTransaction( + fun createErrorTransactionAlert( uiState: SwapStateHolder, - swapTransactionState: SwapTransactionState, - onAlertClick: () -> Unit, + error: SwapTransactionState.Error, + onDismiss: () -> Unit, + onSupportClick: (String) -> Unit, ): SwapStateHolder { + val errorAlert = SwapTransactionErrorStateConverter( + onSupportClick = onSupportClick, + onDismiss = onDismiss, + ).convert(error) return uiState.copy( - alert = SwapWarning.GenericWarning( - message = if (swapTransactionState is SwapTransactionState.ExpressError) { - getProviderErrorMessage(swapTransactionState.dataError) - } else { - null - }, - onClick = onAlertClick, - type = if (swapTransactionState is SwapTransactionState.NetworkError) { - GenericWarningType.NETWORK - } else { - GenericWarningType.OTHER - }, - ), + event = errorAlert?.let { + triggeredEvent( + data = SwapEvent.ShowAlert(errorAlert), + onConsume = onDismiss, + ) + } ?: consumedEvent(), changeCardsButtonState = ChangeCardsButtonState.ENABLED, ) } - fun createDemoModeAlert(uiState: SwapStateHolder, onAlertClick: () -> Unit): SwapStateHolder { + fun createDemoModeAlert(uiState: SwapStateHolder, onDismiss: () -> Unit): SwapStateHolder { return uiState.copy( - alert = SwapWarning.GenericWarning( - title = resourceReference(id = R.string.warning_demo_mode_title), - message = resourceReference(id = R.string.warning_demo_mode_message), - onClick = onAlertClick, - type = GenericWarningType.OTHER, + event = triggeredEvent( + data = SwapEvent.ShowAlert(AlertDemoModeUM(onDismiss)), + onConsume = onDismiss, ), changeCardsButtonState = ChangeCardsButtonState.ENABLED, ) } - private fun getProviderErrorMessage(dataError: DataError): TextReference { - return when (dataError) { - is DataError.SwapsAreUnavailableNowError -> resourceReference( - id = R.string.express_error_swap_unavailable, - formatArgs = wrappedList(dataError.code), - ) - is DataError.ExchangeNotPossibleError -> resourceReference( - id = R.string.warning_express_pair_unavailable_message, - formatArgs = wrappedList(dataError.code), - ) - is DataError.UnknownError -> resourceReference(R.string.common_unknown_error) - is DataError.ExchangeProviderNotActiveError, - is DataError.ExchangeProviderNotFoundError, - is DataError.ExchangeProviderNotAvailableError, - is DataError.ExchangeProviderProviderInternalError, - -> resourceReference( - id = R.string.express_error_swap_pair_unavailable, - formatArgs = wrappedList(dataError.code), - ) - else -> resourceReference(R.string.express_error_code, wrappedList(dataError.code.toString())) - } - } - - private fun getProviderErrorTitle(dataError: DataError): TextReference { - return when (dataError) { - is DataError.ExchangeNotPossibleError -> resourceReference( - id = R.string.warning_express_pair_unavailable_title, - formatArgs = wrappedList(dataError.code), - ) - is DataError.UnknownError -> resourceReference(R.string.common_error) - else -> resourceReference(R.string.warning_express_refresh_required_title) - } - } - fun createAlert( uiState: SwapStateHolder, isPriceImpact: Boolean, token: String, providerType: ExchangeProviderType, - onAlertClick: () -> Unit, + onDismiss: () -> Unit, ): SwapStateHolder { val message = when (providerType) { ExchangeProviderType.CEX -> resourceReference(R.string.swapping_alert_cex_description, wrappedList(token)) @@ -1136,29 +1105,38 @@ internal class StateBuilder( } } return uiState.copy( - alert = SwapWarning.GenericWarning( - title = resourceReference(R.string.swapping_alert_title), - message = message, - onClick = onAlertClick, - type = GenericWarningType.OTHER, + event = triggeredEvent( + SwapEvent.ShowAlert( + SwapAlertUM.FeesAlert( + message = message, + onConfirmClick = onDismiss, + ), + ), + onConsume = onDismiss, ), changeCardsButtonState = ChangeCardsButtonState.ENABLED, ) } - fun addAlert(uiState: SwapStateHolder, message: TextReference? = null, onClick: () -> Unit): SwapStateHolder { + fun addAlert( + uiState: SwapStateHolder, + message: TextReference = resourceReference(R.string.common_unknown_error), + onDismiss: () -> Unit = { clearAlert(uiState) }, + ): SwapStateHolder { return uiState.copy( - alert = SwapWarning.GenericWarning( - message = message, - onClick = onClick, + event = triggeredEvent( + SwapEvent.ShowAlert( + SwapAlertUM.GenericError(onDismiss, message), + ), + onConsume = onDismiss, ), ) } - fun clearAlert(uiState: SwapStateHolder): SwapStateHolder = uiState.copy(alert = null) + fun clearAlert(uiState: SwapStateHolder): SwapStateHolder = uiState.copy(event = consumedEvent()) fun addWarning(uiState: SwapStateHolder, message: TextReference?, onClick: () -> Unit): SwapStateHolder { - val renewWarnings = uiState.warnings.filterNot { it is SwapWarning.GenericWarning }.toMutableList() + val renewWarnings = uiState.warnings.toMutableList() renewWarnings.add( SwapWarning.GenericWarning( message = message, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapEventEffect.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapEventEffect.kt new file mode 100644 index 0000000000..c356624cdb --- /dev/null +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapEventEffect.kt @@ -0,0 +1,67 @@ +package com.tangem.feature.swap.ui + +import androidx.compose.runtime.* +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.res.stringResource +import com.tangem.common.ui.alerts.models.AlertUM +import com.tangem.core.ui.components.BasicDialog +import com.tangem.core.ui.components.DialogButtonUM +import com.tangem.core.ui.event.EventEffect +import com.tangem.core.ui.event.StateEvent +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.shareText +import com.tangem.feature.swap.models.states.events.SwapEvent +import com.tangem.feature.swap.presentation.R + +@Composable +internal fun SwapEventEffect(event: StateEvent) { + val context = LocalContext.current + var alertConfig by remember { mutableStateOf(value = null) } + + val keyboardController = LocalSoftwareKeyboardController.current + LaunchedEffect(key1 = alertConfig) { + keyboardController?.hide() + } + + alertConfig?.let { + SwapAlert(state = it, onDismiss = { alertConfig = null }) + } + + EventEffect( + event = event, + onTrigger = { value -> + when (value) { + is SwapEvent.ShowAlert -> { + alertConfig = value.alert + } + is SwapEvent.ShowShareDialog -> { + context.shareText(value.txUrl) + } + } + }, + ) +} + +@Composable +internal fun SwapAlert(state: AlertUM, onDismiss: () -> Unit) { + val confirmButton = DialogButtonUM( + title = state.confirmButtonText.resolveReference(), + onClick = { + state.onConfirmClick() + onDismiss() + }, + ) + val dismissButton = DialogButtonUM( + title = stringResource(id = R.string.common_cancel), + onClick = onDismiss, + ) + + BasicDialog( + message = state.message.resolveReference(), + confirmButton = confirmButton, + onDismissDialog = onDismiss, + title = state.title?.resolveReference(), + dismissButton = dismissButton, + ) +} \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt index f64eb3344e..318e1dd104 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt @@ -101,22 +101,9 @@ internal fun SwapScreenContent(state: SwapStateHolder, modifier: Modifier = Modi ) } - if (state.alert != null) { - val message = if (state.alert.type == GenericWarningType.NETWORK) { - stringResource(id = R.string.disclaimer_error_loading) - } else { - state.alert.message?.resolveReference() ?: stringResource(id = R.string.common_unknown_error) - } - BasicDialog( - title = state.alert.title?.resolveReference(), - message = message, - confirmButton = DialogButtonUM( - title = stringResource(id = R.string.common_ok), - onClick = state.alert.onClick, - ), - onDismissDialog = state.alert.onClick, - ) - } + SwapEventEffect( + event = state.event, + ) } } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/utils/SwapUtils.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/utils/SwapUtils.kt new file mode 100644 index 0000000000..e288546e37 --- /dev/null +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/utils/SwapUtils.kt @@ -0,0 +1,41 @@ +package com.tangem.feature.swap.utils + +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.feature.swap.domain.models.ExpressDataError +import com.tangem.feature.swap.presentation.R + +internal fun getExpressErrorMessage(expressDataError: ExpressDataError): TextReference { + return when (expressDataError) { + is ExpressDataError.SwapsAreUnavailableNowError -> resourceReference( + id = R.string.express_error_swap_unavailable, + formatArgs = wrappedList(expressDataError.code), + ) + is ExpressDataError.ExchangeNotPossibleError -> resourceReference( + id = R.string.warning_express_pair_unavailable_message, + formatArgs = wrappedList(expressDataError.code), + ) + is ExpressDataError.UnknownError -> resourceReference(R.string.common_unknown_error) + is ExpressDataError.ExchangeProviderNotActiveError, + is ExpressDataError.ExchangeProviderNotFoundError, + is ExpressDataError.ExchangeProviderNotAvailableError, + is ExpressDataError.ExchangeProviderProviderInternalError, + -> resourceReference( + id = R.string.express_error_swap_pair_unavailable, + formatArgs = wrappedList(expressDataError.code), + ) + else -> resourceReference(R.string.express_error_code, wrappedList(expressDataError.code.toString())) + } +} + +internal fun getExpressErrorTitle(expressDataError: ExpressDataError): TextReference { + return when (expressDataError) { + is ExpressDataError.ExchangeNotPossibleError -> resourceReference( + id = R.string.warning_express_pair_unavailable_title, + formatArgs = wrappedList(expressDataError.code), + ) + is ExpressDataError.UnknownError -> resourceReference(R.string.common_error) + else -> resourceReference(R.string.warning_express_refresh_required_title) + } +} \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index cc9b9d3132..e88d4698d1 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -19,13 +19,20 @@ import com.tangem.core.ui.utils.InputNumberFormatter import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase +import com.tangem.domain.feedback.FeedbackManager +import com.tangem.domain.feedback.GetCardInfoUseCase +import com.tangem.domain.feedback.SaveBlockchainErrorUseCase +import com.tangem.domain.feedback.models.BlockchainErrorInfo +import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.Network +import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.feature.swap.analytics.SwapEvents import com.tangem.feature.swap.domain.BlockchainInteractor import com.tangem.feature.swap.domain.SwapInteractor @@ -64,7 +71,6 @@ typealias SuccessLoadedSwapData = Map @Suppress("LargeClass", "LongParameterList") @HiltViewModel internal class SwapViewModel @Inject constructor( - private val swapInteractorFactory: SwapInteractor.Factory, private val blockchainInteractor: BlockchainInteractor, private val dispatchers: CoroutineDispatcherProvider, private val analyticsEventHandler: AnalyticsEventHandler, @@ -73,6 +79,11 @@ internal class SwapViewModel @Inject constructor( private val getCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusSyncUseCase, private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase, private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase, + private val getUserWalletUseCase: GetUserWalletUseCase, + private val getCardInfoUseCase: GetCardInfoUseCase, + private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase, + private val feedbackManager: FeedbackManager, + swapInteractorFactory: SwapInteractor.Factory, savedStateHandle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver { @@ -90,6 +101,7 @@ internal class SwapViewModel @Inject constructor( private val swapInteractor = swapInteractorFactory.create(userWalletId) private lateinit var initialCryptoCurrencyStatus: CryptoCurrencyStatus + private var userWallet: UserWallet by Delegates.notNull() private var isBalanceHidden = true @@ -123,7 +135,10 @@ internal class SwapViewModel @Inject constructor( private val isUserResolvableError: (SwapState) -> Boolean = { it is SwapState.SwapError && - (it.error is DataError.ExchangeTooSmallAmountError || it.error is DataError.ExchangeTooBigAmountError) + ( + it.error is ExpressDataError.ExchangeTooSmallAmountError || + it.error is ExpressDataError.ExchangeTooBigAmountError + ) } private val fromTokenBalanceJobHolder = JobHolder() @@ -136,9 +151,11 @@ internal class SwapViewModel @Inject constructor( viewModelScope.launch(dispatchers.io) { val cryptoCurrencyStatus = getCryptoCurrencyStatusUseCase(userWalletId, initialCryptoCurrency.id).getOrNull() - if (cryptoCurrencyStatus == null) { - uiState = stateBuilder.addAlert(uiState = uiState, onClick = swapRouter::back) + val wallet = getUserWalletUseCase(userWalletId).getOrNull() + if (cryptoCurrencyStatus == null || wallet == null) { + uiState = stateBuilder.addAlert(uiState = uiState, onDismiss = swapRouter::back) } else { + userWallet = wallet initialCryptoCurrencyStatus = cryptoCurrencyStatus initTokens(isInitiallyReversed) } @@ -573,20 +590,19 @@ internal class SwapViewModel @Inject constructor( swapRouter.openScreen(SwapNavScreen.Success) } - is SwapTransactionState.UserCancelled -> { - startLoadingQuotesFromLastState() - } - is SwapTransactionState.DemoMode -> { - startLoadingQuotesFromLastState() + SwapTransactionState.DemoMode -> { uiState = stateBuilder.createDemoModeAlert(uiState) { uiState = stateBuilder.clearAlert(uiState) } } - else -> { + is SwapTransactionState.Error -> { startLoadingQuotesFromLastState() - uiState = stateBuilder.createErrorTransaction(uiState, it) { - uiState = stateBuilder.clearAlert(uiState) - } + uiState = stateBuilder.createErrorTransactionAlert( + uiState = uiState, + error = it, + onDismiss = { uiState = stateBuilder.clearAlert(uiState) }, + onSupportClick = ::onFailedTxEmailClick, + ) } } }.onFailure { @@ -657,23 +673,28 @@ internal class SwapViewModel @Inject constructor( uiState = stateBuilder.dismissBottomSheet(uiState) startLoadingQuotesFromLastState(isSilent = true) } - is SwapTransactionState.UserCancelled -> Unit - else -> { - uiState = stateBuilder.createErrorTransaction(uiState, it) { + is SwapTransactionState.Error -> { + uiState = stateBuilder.createErrorTransactionAlert( + uiState = uiState, + error = it, + onDismiss = { uiState = stateBuilder.clearAlert(uiState) }, + onSupportClick = ::onFailedTxEmailClick, + ) + } + SwapTransactionState.DemoMode -> { + uiState = stateBuilder.createDemoModeAlert(uiState) { uiState = stateBuilder.clearAlert(uiState) } } } }.onFailure { makeDefaultAlert() } - }.onFailure { showGenericError(it.message.orEmpty()) } + }.onFailure { + Timber.e(it.message.orEmpty()) + makeDefaultAlert() + } } } - private fun showGenericError(message: String) { - makeDefaultAlert(resourceReference(R.string.common_unknown_error)) - Timber.e(message) - } - private fun onSearchEntered(searchQuery: String) { viewModelScope.launch(dispatchers.io) { val tokenDataState = dataState.tokensDataState ?: return@launch @@ -880,15 +901,11 @@ internal class SwapViewModel @Inject constructor( } private fun makeDefaultAlert() { - uiState = stateBuilder.addAlert(uiState) { - uiState = stateBuilder.clearAlert(uiState) - } + uiState = stateBuilder.addAlert(uiState) } private fun makeDefaultAlert(message: TextReference) { - uiState = stateBuilder.addAlert(uiState, message) { - uiState = stateBuilder.clearAlert(uiState) - } + uiState = stateBuilder.addAlert(uiState, message) } @Suppress("LongMethod", "CyclomaticComplexMethod") @@ -1130,7 +1147,8 @@ internal class SwapViewModel @Inject constructor( toToken.currency.id.value } - return groupToFind.available.find { idToFind == it.currencyStatus.currency.id.value }?.providers ?: emptyList() + return groupToFind.available.find { idToFind == it.currencyStatus.currency.id.value }?.providers + ?: emptyList() } private fun Map.getLastLoadedSuccessStates(): SuccessLoadedSwapData { @@ -1218,6 +1236,33 @@ internal class SwapViewModel @Inject constructor( analyticsEventHandler.send(event = event) } + private fun onFailedTxEmailClick(errorMessage: String) { + viewModelScope.launch { + val network = initialCryptoCurrencyStatus.currency.network + val cardInfo = getCardInfoUseCase(userWallet.scanResponse).getOrElse { error("CardInfo must be not null") } + + saveBlockchainErrorUseCase( + error = BlockchainErrorInfo( + errorMessage = errorMessage, + blockchainId = network.id.value, + derivationPath = network.derivationPath.value, + destinationAddress = dataState.swapDataModel?.transaction?.txTo.orEmpty(), + tokenSymbol = initialCryptoCurrency.symbol, + amount = dataState.amount.orEmpty(), + fee = dataState.selectedFee?.feeCryptoFormatted.orEmpty(), + ), + ) + + val email = FeedbackEmailType.SwapProblem( + cardInfo = cardInfo, + providerName = dataState.selectedProvider?.name.orEmpty(), + txId = dataState.swapDataModel?.transaction?.txId.orEmpty(), + ) + + feedbackManager.sendEmail(email) + } + } + private companion object { const val INITIAL_AMOUNT = "" const val UPDATE_DELAY = 10000L From 4c373e75430499d229fdcb7a5d8b08870568df1a Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 25 Sep 2024 20:31:57 +0500 Subject: [PATCH 3/3] Updated on 2026-08-14 --- .../feature/swap/DefaultSwapRepository.kt | 20 +++--- .../swap/converters/ErrorsDataConverter.kt | 61 ++++++++++--------- .../feature/swap/domain/api/SwapRepository.kt | 8 +-- .../{DataError.kt => ExpressDataError.kt} | 41 +++++++------ .../swap/domain/models/ExpressException.kt | 2 +- .../swap/domain/models/ui/SwapState.kt | 4 +- .../feature/swap/domain/SwapInteractorImpl.kt | 12 ++-- .../tangem/feature/swap/ui/StateBuilder.kt | 38 ++++++------ .../feature/swap/viewmodels/SwapViewModel.kt | 8 +-- 9 files changed, 99 insertions(+), 95 deletions(-) rename features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/{DataError.kt => ExpressDataError.kt} (59%) diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt index f3a0d6cbe4..f55cfe8655 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt @@ -27,7 +27,7 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.swap.converters.* import com.tangem.feature.swap.domain.api.SwapRepository -import com.tangem.feature.swap.domain.models.DataError +import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.ExpressException import com.tangem.feature.swap.domain.models.createFromAmountWithOffset import com.tangem.feature.swap.domain.models.domain.* @@ -196,7 +196,7 @@ internal class DefaultSwapRepository @Inject constructor( toDecimals: Int, providerId: String, rateType: RateType, - ): Either { + ): Either { return withContext(coroutineDispatcher.io) { try { val response = tangemExpressApi.getExchangeQuote( @@ -234,7 +234,7 @@ internal class DefaultSwapRepository @Inject constructor( toAddress: String, refundAddress: String?, // for cex only refundExtraId: String?, // for cex only - ): Either { + ): Either { return withContext(coroutineDispatcher.io) { try { val requestId = UUID.randomUUID().toString() @@ -256,12 +256,12 @@ internal class DefaultSwapRepository @Inject constructor( ).getOrThrow() if (dataSignatureVerifier.verifySignature(response.signature, response.txDetailsJson)) { val txDetails = parseTxDetails(response.txDetailsJson) - ?: return@withContext DataError.UnknownError.left() + ?: return@withContext ExpressDataError.UnknownError.left() if (txDetails.requestId != requestId) { - return@withContext DataError.InvalidRequestIdError().left() + return@withContext ExpressDataError.InvalidRequestIdError().left() } if (!toAddress.equals(txDetails.payoutAddress, ignoreCase = true)) { - return@withContext DataError.InvalidPayoutAddressError().left() + return@withContext ExpressDataError.InvalidPayoutAddressError().left() } expressDataConverter.convert( ExchangeDataResponseWithTxDetails( @@ -270,7 +270,7 @@ internal class DefaultSwapRepository @Inject constructor( ), ).right() } else { - DataError.InvalidSignatureError().left() + ExpressDataError.InvalidSignatureError().left() } } catch (ex: Exception) { getDataError(ex).left() @@ -285,7 +285,7 @@ internal class DefaultSwapRepository @Inject constructor( payInAddress: String, txHash: String, payInExtraId: String?, - ): Either = withContext(coroutineDispatcher.io) { + ): Either = withContext(coroutineDispatcher.io) { try { tangemExpressApi.exchangeSent( ExchangeSentRequestBody( @@ -400,11 +400,11 @@ internal class DefaultSwapRepository @Inject constructor( ) } - private fun getDataError(ex: Exception): DataError { + private fun getDataError(ex: Exception): ExpressDataError { return if (ex is ApiResponseError.HttpException) { errorsDataConverter.convert(ex.errorBody ?: "") } else { - DataError.UnknownError + ExpressDataError.UnknownError } } } \ No newline at end of file diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt index ee0cf5d41d..9fc5d1725d 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/converters/ErrorsDataConverter.kt @@ -3,76 +3,77 @@ package com.tangem.feature.swap.converters import com.squareup.moshi.JsonAdapter import com.tangem.datasource.api.express.models.response.ExpressError import com.tangem.datasource.api.express.models.response.ExpressErrorResponse -import com.tangem.feature.swap.domain.models.DataError +import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.createFromAmountWithOffset import com.tangem.utils.converter.Converter internal class ErrorsDataConverter( private val jsonAdapter: JsonAdapter, -) : Converter { +) : Converter { @Suppress("MagicNumber", "CyclomaticComplexMethod") - override fun convert(value: String): DataError { + override fun convert(value: String): ExpressDataError { try { - val error = jsonAdapter.fromJson(value)?.error ?: return DataError.UnknownError + val error = jsonAdapter.fromJson(value)?.error ?: return ExpressDataError.UnknownError return when (error.code) { - 2010 -> DataError.BadRequest(code = error.code) - 2200 -> DataError.SwapsAreUnavailableNowError(code = error.code) - 2210 -> DataError.ExchangeProviderNotFoundError(code = error.code) - 2220 -> DataError.ExchangeProviderNotActiveError(code = error.code) - 2230 -> DataError.ExchangeProviderNotAvailableError(code = error.code) - 2231 -> DataError.ExchangeProviderProviderInternalError(code = error.code) - 2240 -> DataError.ExchangeNotPossibleError(code = error.code) + 2010 -> ExpressDataError.BadRequest(code = error.code) + 2200 -> ExpressDataError.SwapsAreUnavailableNowError(code = error.code) + 2210 -> ExpressDataError.ExchangeProviderNotFoundError(code = error.code) + 2220 -> ExpressDataError.ExchangeProviderNotActiveError(code = error.code) + 2230 -> ExpressDataError.ExchangeProviderNotAvailableError(code = error.code) + 2231 -> ExpressDataError.ExchangeProviderProviderInternalError(code = error.code) + 2240 -> ExpressDataError.ExchangeNotPossibleError(code = error.code) 2250 -> tryParseExchangeTooSmallAmountError(error = error) 2251 -> tryParseExchangeTooBigAmountError(error = error) 2260 -> tryParseExchangeNotEnoughAllowanceError(error = error) - 2270 -> DataError.ExchangeNotEnoughBalanceError(code = error.code) - 2280 -> DataError.ExchangeInvalidAddressError(code = error.code) + 2270 -> ExpressDataError.ExchangeNotEnoughBalanceError(code = error.code) + 2280 -> ExpressDataError.ExchangeInvalidAddressError(code = error.code) 2290 -> tryParseExchangeInvalidFromDecimalsError(error = error) - else -> DataError.UnknownErrorWithCode(error.code) + else -> ExpressDataError.UnknownErrorWithCode(error.code) } } catch (e: Exception) { - return DataError.UnknownError + return ExpressDataError.UnknownError } } - private fun tryParseExchangeTooSmallAmountError(error: ExpressError): DataError { - val minAmount = error.value?.minAmount ?: return DataError.UnknownErrorWithCode(error.code) - val decimals = error.value?.decimals ?: return DataError.UnknownErrorWithCode(error.code) + private fun tryParseExchangeTooSmallAmountError(error: ExpressError): ExpressDataError { + val minAmount = error.value?.minAmount ?: return ExpressDataError.UnknownErrorWithCode(error.code) + val decimals = error.value?.decimals ?: return ExpressDataError.UnknownErrorWithCode(error.code) - return DataError.ExchangeTooSmallAmountError( + return ExpressDataError.ExchangeTooSmallAmountError( code = error.code, amount = createFromAmountWithOffset(minAmount, decimals), ) } - private fun tryParseExchangeTooBigAmountError(error: ExpressError): DataError { - val minAmount = error.value?.maxAmount ?: return DataError.UnknownErrorWithCode(error.code) - val decimals = error.value?.decimals ?: return DataError.UnknownErrorWithCode(error.code) + private fun tryParseExchangeTooBigAmountError(error: ExpressError): ExpressDataError { + val minAmount = error.value?.maxAmount ?: return ExpressDataError.UnknownErrorWithCode(error.code) + val decimals = error.value?.decimals ?: return ExpressDataError.UnknownErrorWithCode(error.code) - return DataError.ExchangeTooBigAmountError( + return ExpressDataError.ExchangeTooBigAmountError( code = error.code, amount = createFromAmountWithOffset(minAmount, decimals), ) } - private fun tryParseExchangeNotEnoughAllowanceError(error: ExpressError): DataError { - val currentAllowance = error.value?.currentAllowance ?: return DataError.UnknownErrorWithCode(error.code) + private fun tryParseExchangeNotEnoughAllowanceError(error: ExpressError): ExpressDataError { + val currentAllowance = error.value?.currentAllowance ?: return ExpressDataError.UnknownErrorWithCode(error.code) - return DataError.ExchangeNotEnoughAllowanceError( + return ExpressDataError.ExchangeNotEnoughAllowanceError( code = error.code, currentAllowance = currentAllowance, ) } - private fun tryParseExchangeInvalidFromDecimalsError(error: ExpressError): DataError { - val receivedFromDecimals = error.value?.receivedFromDecimals ?: return DataError.UnknownErrorWithCode( + private fun tryParseExchangeInvalidFromDecimalsError(error: ExpressError): ExpressDataError { + val receivedFromDecimals = error.value?.receivedFromDecimals ?: return ExpressDataError.UnknownErrorWithCode( code = error.code, ) - val expressFromDecimals = error.value?.expressFromDecimals ?: return DataError.UnknownErrorWithCode(error.code) + val expressFromDecimals = + error.value?.expressFromDecimals ?: return ExpressDataError.UnknownErrorWithCode(error.code) - return DataError.ExchangeInvalidFromDecimalsError( + return ExpressDataError.ExchangeInvalidFromDecimalsError( code = error.code, receivedFromDecimals = receivedFromDecimals, expressFromDecimals = expressFromDecimals, diff --git a/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt b/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt index 99bc772d79..0f47053aa3 100644 --- a/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt +++ b/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt @@ -3,7 +3,7 @@ package com.tangem.feature.swap.domain.api import arrow.core.Either import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.swap.domain.models.DataError +import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.domain.* import java.math.BigDecimal @@ -27,7 +27,7 @@ interface SwapRepository { toDecimals: Int, providerId: String, rateType: RateType, - ): Either + ): Either @Suppress("LongParameterList") @Throws(IllegalStateException::class) @@ -66,7 +66,7 @@ interface SwapRepository { toAddress: String, refundAddress: String? = null, // for cex only refundExtraId: String? = null, // for cex only - ): Either + ): Either // TODO: Add target error handling, remove either ([REDACTED_JIRA]) @Suppress("LongParameterList") @@ -77,7 +77,7 @@ interface SwapRepository { payInAddress: String, txHash: String, payInExtraId: String?, - ): Either + ): Either fun getNativeTokenForNetwork(networkId: String): CryptoCurrency } \ No newline at end of file diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/DataError.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ExpressDataError.kt similarity index 59% rename from features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/DataError.kt rename to features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ExpressDataError.kt index 12e5db5e37..b2ac31292d 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/DataError.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ExpressDataError.kt @@ -2,49 +2,52 @@ package com.tangem.feature.swap.domain.models import java.math.BigDecimal -sealed class DataError { +sealed class ExpressDataError { abstract val code: Int - data class BadRequest(override val code: Int) : DataError() + data class BadRequest(override val code: Int) : ExpressDataError() - data class SwapsAreUnavailableNowError(override val code: Int) : DataError() + data class SwapsAreUnavailableNowError(override val code: Int) : ExpressDataError() - data class ExchangeProviderNotFoundError(override val code: Int) : DataError() + data class ExchangeProviderNotFoundError(override val code: Int) : ExpressDataError() - data class ExchangeProviderNotActiveError(override val code: Int) : DataError() + data class ExchangeProviderNotActiveError(override val code: Int) : ExpressDataError() - data class ExchangeProviderNotAvailableError(override val code: Int) : DataError() + data class ExchangeProviderNotAvailableError(override val code: Int) : ExpressDataError() - data class ExchangeProviderProviderInternalError(override val code: Int) : DataError() + data class ExchangeProviderProviderInternalError(override val code: Int) : ExpressDataError() - data class ExchangeNotPossibleError(override val code: Int) : DataError() + data class ExchangeNotPossibleError(override val code: Int) : ExpressDataError() - data class ExchangeTooSmallAmountError(override val code: Int, val amount: SwapAmount) : DataError() + data class ExchangeTooSmallAmountError(override val code: Int, val amount: SwapAmount) : ExpressDataError() - data class ExchangeTooBigAmountError(override val code: Int, val amount: SwapAmount) : DataError() + data class ExchangeTooBigAmountError(override val code: Int, val amount: SwapAmount) : ExpressDataError() - data class ExchangeNotEnoughAllowanceError(override val code: Int, val currentAllowance: BigDecimal) : DataError() + data class ExchangeNotEnoughAllowanceError( + override val code: Int, + val currentAllowance: BigDecimal, + ) : ExpressDataError() - data class ExchangeNotEnoughBalanceError(override val code: Int) : DataError() + data class ExchangeNotEnoughBalanceError(override val code: Int) : ExpressDataError() - data class ExchangeInvalidAddressError(override val code: Int) : DataError() + data class ExchangeInvalidAddressError(override val code: Int) : ExpressDataError() data class ExchangeInvalidFromDecimalsError( override val code: Int, val receivedFromDecimals: Int, val expressFromDecimals: Int, - ) : DataError() + ) : ExpressDataError() - data class UnknownErrorWithCode(override val code: Int) : DataError() + data class UnknownErrorWithCode(override val code: Int) : ExpressDataError() - data class InvalidSignatureError(override val code: Int = 990) : DataError() + data class InvalidSignatureError(override val code: Int = 990) : ExpressDataError() - data class InvalidRequestIdError(override val code: Int = 991) : DataError() + data class InvalidRequestIdError(override val code: Int = 991) : ExpressDataError() - data class InvalidPayoutAddressError(override val code: Int = 992) : DataError() + data class InvalidPayoutAddressError(override val code: Int = 992) : ExpressDataError() - data object UnknownError : DataError() { + data object UnknownError : ExpressDataError() { override val code: Int = -1 } } \ No newline at end of file diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ExpressException.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ExpressException.kt index cc416b82ca..0c4a1fc3de 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ExpressException.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ExpressException.kt @@ -1,3 +1,3 @@ package com.tangem.feature.swap.domain.models -class ExpressException(val dataError: DataError) : Exception() \ No newline at end of file +class ExpressException(val expressDataError: ExpressDataError) : Exception() \ No newline at end of file diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt index 854e53526b..04b5faf49e 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt @@ -1,7 +1,7 @@ package com.tangem.feature.swap.domain.models.ui import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.feature.swap.domain.models.DataError +import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.SwapAmount import com.tangem.feature.swap.domain.models.domain.* import java.math.BigDecimal @@ -34,7 +34,7 @@ sealed interface SwapState { data class SwapError( val fromTokenInfo: TokenSwapInfo, - val error: DataError, + val error: ExpressDataError, val includeFeeInAmount: IncludeFeeInAmount, ) : SwapState } diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 541327a97a..45a5990989 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -30,7 +30,7 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.feature.swap.domain.api.SwapRepository import com.tangem.feature.swap.domain.converters.SwapCurrencyConverter -import com.tangem.feature.swap.domain.models.DataError +import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.SwapAmount import com.tangem.feature.swap.domain.models.domain.* import com.tangem.feature.swap.domain.models.toStringWithRightOffset @@ -1057,7 +1057,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( @Suppress("LongMethod") private suspend fun getQuotesState( provider: SwapProvider, - quoteDataModel: Either, + quoteDataModel: Either, amount: SwapAmount, fromToken: CryptoCurrencyStatus, toToken: CryptoCurrencyStatus, @@ -1136,7 +1136,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( fromToken = fromToken, amount = amount, includeFeeInAmount = includeFeeInAmount, - dataError = error, + expressDataError = error, ) }, ) @@ -1146,7 +1146,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( fromToken: CryptoCurrencyStatus, amount: SwapAmount, includeFeeInAmount: IncludeFeeInAmount, - dataError: DataError, + expressDataError: ExpressDataError, ): SwapState.SwapError { val rates = getQuotes(fromToken.currency.id) val fromTokenSwapInfo = TokenSwapInfo( @@ -1155,7 +1155,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( ?: BigDecimal.ZERO, cryptoCurrencyStatus = fromToken, ) - return SwapState.SwapError(fromTokenSwapInfo, dataError, includeFeeInAmount) + return SwapState.SwapError(fromTokenSwapInfo, expressDataError, includeFeeInAmount) } @Suppress("CyclomaticComplexMethod") @@ -1502,7 +1502,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( fromToken = fromTokenStatus, amount = swapAmount, includeFeeInAmount = IncludeFeeInAmount.Excluded, - dataError = DataError.UnknownError, + expressDataError = ExpressDataError.UnknownError, ) } } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 2575e2d24b..96ae124966 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -16,7 +16,7 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.swap.converters.SwapTransactionErrorStateConverter import com.tangem.feature.swap.converters.TokensDataConverter -import com.tangem.feature.swap.domain.models.DataError +import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.SwapAmount import com.tangem.feature.swap.domain.models.domain.* import com.tangem.feature.swap.domain.models.formatToUIRepresentation @@ -632,13 +632,13 @@ internal class StateBuilder( fromToken: TokenSwapInfo, toToken: CryptoCurrencyStatus?, includeFeeInAmount: IncludeFeeInAmount, - dataError: DataError, + expressDataError: ExpressDataError, isReverseSwapPossible: Boolean, ): SwapStateHolder { if (uiStateHolder.sendCardData !is SwapCardState.SwapCardData) return uiStateHolder if (uiStateHolder.receiveCardData !is SwapCardState.SwapCardData) return uiStateHolder val warnings = mutableListOf() - warnings.add(getWarningForError(dataError, fromToken.cryptoCurrencyStatus.currency)) + warnings.add(getWarningForError(expressDataError, fromToken.cryptoCurrencyStatus.currency)) if (includeFeeInAmount is IncludeFeeInAmount.Included && uiStateHolder.fee is FeeItemState.Content) { val feeCoverageNotification = createNetworkFeeCoverageNotificationConfig( uiStateHolder.fee.amountCrypto, @@ -649,7 +649,7 @@ internal class StateBuilder( val providerState = getProviderStateForError( swapProvider = swapProvider, fromToken = fromToken.cryptoCurrencyStatus.currency, - dataError = dataError, + expressDataError = expressDataError, onProviderClick = actions.onProviderClick, selectionType = ProviderState.SelectionType.CLICK, ) @@ -705,28 +705,28 @@ internal class StateBuilder( private fun getProviderStateForError( swapProvider: SwapProvider, fromToken: CryptoCurrency, - dataError: DataError, + expressDataError: ExpressDataError, onProviderClick: (String) -> Unit, selectionType: ProviderState.SelectionType, ): ProviderState { - return when (dataError) { - is DataError.ExchangeTooSmallAmountError -> { + return when (expressDataError) { + is ExpressDataError.ExchangeTooSmallAmountError -> { swapProvider.convertToAvailableFromProviderState( swapProvider = swapProvider, alertText = resourceReference( R.string.express_provider_min_amount, - wrappedList(dataError.amount.getFormattedCryptoAmount(fromToken)), + wrappedList(expressDataError.amount.getFormattedCryptoAmount(fromToken)), ), selectionType = selectionType, onProviderClick = onProviderClick, ) } - is DataError.ExchangeTooBigAmountError -> { + is ExpressDataError.ExchangeTooBigAmountError -> { swapProvider.convertToAvailableFromProviderState( swapProvider = swapProvider, alertText = resourceReference( R.string.express_provider_max_amount, - wrappedList(dataError.amount.getFormattedCryptoAmount(fromToken)), + wrappedList(expressDataError.amount.getFormattedCryptoAmount(fromToken)), ), selectionType = selectionType, onProviderClick = onProviderClick, @@ -738,25 +738,25 @@ internal class StateBuilder( } } - private fun getWarningForError(dataError: DataError, fromToken: CryptoCurrency): SwapWarning { - val providerErrorMessage = getProviderErrorMessage(dataError) - val providerErrorTitle = getProviderErrorTitle(dataError) - return when (dataError) { - is DataError.ExchangeTooSmallAmountError -> SwapWarning.GeneralError( + private fun getWarningForError(expressDataError: ExpressDataError, fromToken: CryptoCurrency): SwapWarning { + val providerErrorMessage = getExpressErrorMessage(expressDataError) + val providerErrorTitle = getExpressErrorTitle(expressDataError) + return when (expressDataError) { + is ExpressDataError.ExchangeTooSmallAmountError -> SwapWarning.GeneralError( notificationConfig = NotificationConfig( title = resourceReference( id = R.string.warning_express_too_minimal_amount_title, - formatArgs = wrappedList(dataError.amount.getFormattedCryptoAmount(fromToken)), + formatArgs = wrappedList(expressDataError.amount.getFormattedCryptoAmount(fromToken)), ), subtitle = resourceReference(R.string.warning_express_wrong_amount_description), iconResId = R.drawable.ic_alert_circle_24, ), ) - is DataError.ExchangeTooBigAmountError -> SwapWarning.GeneralError( + is ExpressDataError.ExchangeTooBigAmountError -> SwapWarning.GeneralError( notificationConfig = NotificationConfig( title = resourceReference( id = R.string.warning_express_too_maximum_amount_title, - formatArgs = wrappedList(dataError.amount.getFormattedCryptoAmount(fromToken)), + formatArgs = wrappedList(expressDataError.amount.getFormattedCryptoAmount(fromToken)), ), subtitle = resourceReference(R.string.warning_express_wrong_amount_description), iconResId = R.drawable.ic_alert_circle_24, @@ -1399,7 +1399,7 @@ internal class StateBuilder( is SwapState.SwapError -> getProviderStateForError( swapProvider = provider, fromToken = state.fromTokenInfo.cryptoCurrencyStatus.currency, - dataError = state.error, + expressDataError = state.error, onProviderClick = onProviderSelect, selectionType = ProviderState.SelectionType.SELECT, ) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index e88d4698d1..973778874e 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -36,7 +36,7 @@ import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.feature.swap.analytics.SwapEvents import com.tangem.feature.swap.domain.BlockchainInteractor import com.tangem.feature.swap.domain.SwapInteractor -import com.tangem.feature.swap.domain.models.DataError +import com.tangem.feature.swap.domain.models.ExpressDataError import com.tangem.feature.swap.domain.models.ExpressException import com.tangem.feature.swap.domain.models.SwapAmount import com.tangem.feature.swap.domain.models.domain.* @@ -243,7 +243,7 @@ internal class SwapViewModel @Inject constructor( uiState = stateBuilder.createInitialErrorState( uiState, - (it as? ExpressException)?.dataError?.code ?: DataError.UnknownError.code, + (it as? ExpressException)?.expressDataError?.code ?: ExpressDataError.UnknownError.code, ) { uiState = stateBuilder.createInitialLoadingState( initialCurrency = initialCryptoCurrency, @@ -431,7 +431,7 @@ internal class SwapViewModel @Inject constructor( swapProvider = provider, fromToken = state.fromTokenInfo, toToken = dataState.toCryptoCurrency, - dataError = state.error, + expressDataError = state.error, includeFeeInAmount = state.includeFeeInAmount, isReverseSwapPossible = isReverseSwapPossible(), ) @@ -440,7 +440,7 @@ internal class SwapViewModel @Inject constructor( } } - private fun sendErrorAnalyticsEvent(error: DataError, provider: SwapProvider) { + private fun sendErrorAnalyticsEvent(error: ExpressDataError, provider: SwapProvider) { val receiveToken = dataState.toCryptoCurrency?.currency?.let { "${it.network.backendId}:${it.symbol}" }