diff --git a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt index 9ad9faf3a0..dd7ecf0d49 100644 --- a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt @@ -14,6 +14,7 @@ import com.tangem.blockchain.common.TransactionSigner import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult +import com.tangem.blockchain.extensions.isNetworkError import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes import com.tangem.domain.common.extensions.fromNetworkId @@ -167,6 +168,7 @@ class TransactionManagerImpl( when (result) { is SimpleResult.Success -> return SendTxResult.Success is SimpleResult.Failure -> { + if (result.isNetworkError()) return SendTxResult.NetworkError(result.error) val error = result.error as? BlockchainSdkError ?: return SendTxResult.UnknownError() when (error) { is BlockchainSdkError.WrappedTangemError -> { diff --git a/buildSrc/src/main/java/Versions.kt b/buildSrc/src/main/java/Versions.kt index 0a9a6e9661..2c0370693c 100644 --- a/buildSrc/src/main/java/Versions.kt +++ b/buildSrc/src/main/java/Versions.kt @@ -64,7 +64,7 @@ object Versions { const val tangemBlockchainSdk = "develop-155" // const val tangemBlockchainSdk = "0.0.1" // Keep it! - used for local builds - const val tangemCardSdk = "develop-186" + const val tangemCardSdk = "develop-190" // const val tangemCardSgk = "0.0.1" // Keep it! - used for local builds // endregion Tangem 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 7d45473ab1..60d78b3919 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 @@ -134,6 +134,7 @@ internal class SwapInteractorImpl @Inject constructor( SendTxResult.UserCancelledError -> TxState.UserCancelled is SendTxResult.BlockchainSdkError -> TxState.BlockchainError is SendTxResult.TangemSdkError -> TxState.TangemSdkError + is SendTxResult.NetworkError -> TxState.NetworkError is SendTxResult.UnknownError -> TxState.UnknownError } } @@ -225,6 +226,7 @@ internal class SwapInteractorImpl @Inject constructor( SendTxResult.UserCancelledError -> TxState.UserCancelled is SendTxResult.BlockchainSdkError -> TxState.BlockchainError is SendTxResult.TangemSdkError -> TxState.TangemSdkError + is SendTxResult.NetworkError -> TxState.NetworkError is SendTxResult.UnknownError -> TxState.UnknownError } } diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/TxState.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/TxState.kt index 85f32a42ed..ca95f626e3 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/TxState.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/TxState.kt @@ -11,5 +11,6 @@ sealed class TxState { object UserCancelled : TxState() object BlockchainError : TxState() object TangemSdkError : TxState() + object NetworkError : TxState() object UnknownError : TxState() } \ 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 2c03bf924e..cacf9a5bd4 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 @@ -75,9 +75,14 @@ sealed interface SwapWarning { object InsufficientFunds : SwapWarning data class GenericWarning( val message: String? = null, + val type: GenericWarningType = GenericWarningType.OTHER, val shouldWrapMessage: Boolean = false, val onClick: () -> Unit, ) : SwapWarning // data class RateExpired(val onClick: () -> Unit) : SwapWarning // object HighPriceImpact : SwapWarning +} + +enum class GenericWarningType { + NETWORK, OTHER } \ 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 7e989320ac..0e3878c821 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 @@ -13,6 +13,7 @@ import com.tangem.feature.swap.domain.models.ui.TxState import com.tangem.feature.swap.models.ApprovePermissionButton import com.tangem.feature.swap.models.CancelPermissionButton import com.tangem.feature.swap.models.FeeState +import com.tangem.feature.swap.models.GenericWarningType import com.tangem.feature.swap.models.SwapButton import com.tangem.feature.swap.models.SwapCardData import com.tangem.feature.swap.models.SwapPermissionState @@ -275,39 +276,28 @@ internal class StateBuilder(val actions: UiActions) { ) } - fun createSwapErrorTransaction(uiState: SwapStateHolder, onAlertClick: () -> Unit): SwapStateHolder { + fun createErrorTransaction( + uiState: SwapStateHolder, + txState: TxState, + onAlertClick: () -> Unit, + ): SwapStateHolder { return uiState.copy( - swapButton = uiState.swapButton.copy( - enabled = true, - loading = false, - ), alert = SwapWarning.GenericWarning( message = null, onClick = onAlertClick, + type = if (txState is TxState.NetworkError) GenericWarningType.NETWORK else GenericWarningType.OTHER, ), updateInProgress = false, ) } - fun addWarning(uiState: SwapStateHolder, message: String?, shouldWrapMessage: Boolean = false): SwapStateHolder { - return if (message != null) { - val renewWarnings = uiState.warnings.filterNot { it is SwapWarning.GenericWarning }.toMutableList() - renewWarnings.add(SwapWarning.GenericWarning(message, shouldWrapMessage = shouldWrapMessage) {}) - uiState.copy( - warnings = renewWarnings, - ) - } else { - uiState - } - } - - fun mapError(uiState: SwapStateHolder, error: DataError): SwapStateHolder { + fun mapError(uiState: SwapStateHolder, error: DataError, onClick: () -> Unit): SwapStateHolder { return when (error) { // todo use if needed later // DataError.InsufficientLiquidity -> TODO() // DataError.NoError -> TODO() - is DataError.UnknownError -> addWarning(uiState, error.message, true) - else -> addWarning(uiState, null) + is DataError.UnknownError -> addWarning(uiState, error.message, true, onClick) + else -> addWarning(uiState, null, false) {} } } @@ -322,6 +312,19 @@ internal class StateBuilder(val actions: UiActions) { fun clearAlert(uiState: SwapStateHolder): SwapStateHolder = uiState.copy(alert = null) + fun addWarning( + uiState: SwapStateHolder, + message: String?, + shouldWrapMessage: Boolean = false, + onClick: () -> Unit, + ): SwapStateHolder { + val renewWarnings = uiState.warnings.filterNot { it is SwapWarning.GenericWarning }.toMutableList() + renewWarnings.add(SwapWarning.GenericWarning(message, shouldWrapMessage = shouldWrapMessage, onClick = onClick)) + return uiState.copy( + warnings = renewWarnings, + ) + } + private fun getShortAddressValue(fullAddress: String): String { check(fullAddress.length > ADDRESS_MIN_LENGTH) { "Invalid address" } val firstAddressPart = fullAddress.substring(startIndex = 0, endIndex = ADDRESS_FIRST_PART_LENGTH) 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 9aa2ed1468..c71240070f 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 @@ -45,6 +45,7 @@ import com.tangem.core.ui.extensions.getActiveIconRes import com.tangem.core.ui.extensions.getActiveIconResByCoinId import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.swap.models.FeeState +import com.tangem.feature.swap.models.GenericWarningType import com.tangem.feature.swap.models.SwapButton import com.tangem.feature.swap.models.SwapCardData import com.tangem.feature.swap.models.SwapPermissionState @@ -137,8 +138,13 @@ internal fun SwapScreenContent(state: SwapStateHolder, onPermissionWarningClick: } if (state.alert != null) { + val message = if (state.alert.type == GenericWarningType.NETWORK) { + stringResource(id = R.string.disclaimer_error_loading) + } else { + state.alert.message ?: stringResource(id = R.string.swapping_generic_error) + } SimpleOkDialog( - message = state.alert.message ?: stringResource(id = R.string.swapping_generic_error), + message = message, onDismissDialog = state.alert.onClick, ) } 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 99ba524bdd..68daa1ca45 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 @@ -22,8 +22,8 @@ import com.tangem.feature.swap.domain.models.ui.TxState import com.tangem.feature.swap.models.SwapStateHolder import com.tangem.feature.swap.models.UiActions import com.tangem.feature.swap.presentation.SwapFragment -import com.tangem.feature.swap.router.SwapRouter import com.tangem.feature.swap.router.SwapNavScreen +import com.tangem.feature.swap.router.SwapRouter import com.tangem.feature.swap.ui.StateBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.Debouncer @@ -143,6 +143,15 @@ internal class SwapViewModel @Inject constructor( ) } + private fun startLoadingQuotesFromLastState() { + val fromCurrency = dataState.fromCurrency + val toCurrency = dataState.toCurrency + val amount = dataState.amount + if (fromCurrency != null && toCurrency != null && amount != null) { + startLoadingQuotes(fromCurrency, toCurrency, amount) + } + } + private fun loadQuotesTask( fromToken: Currency, toToken: Currency, @@ -184,12 +193,12 @@ internal class SwapViewModel @Inject constructor( ) } is SwapState.SwapError -> { - uiState = stateBuilder.mapError(uiState, swapState.error) + uiState = stateBuilder.mapError(uiState, swapState.error) { startLoadingQuotesFromLastState() } } } }, onError = { - uiState = stateBuilder.addWarning(uiState, it.message) + uiState = stateBuilder.addWarning(uiState, null) { startLoadingQuotesFromLastState() } }, ) } @@ -238,13 +247,17 @@ internal class SwapViewModel @Inject constructor( swapRouter.openScreen(SwapNavScreen.Success) } else -> { - uiState = stateBuilder.createSwapErrorTransaction(uiState) { + startLoadingQuotesFromLastState() + uiState = stateBuilder.createErrorTransaction(uiState, it) { uiState = stateBuilder.clearAlert(uiState) } } } } - .onFailure { makeDefaultAlert() } + .onFailure { + startLoadingQuotesFromLastState() + makeDefaultAlert() + } } } @@ -265,7 +278,9 @@ internal class SwapViewModel @Inject constructor( uiState = stateBuilder.loadingPermissionState(uiState) } else -> { - makeDefaultAlert() + uiState = stateBuilder.createErrorTransaction(uiState, it) { + uiState = stateBuilder.clearAlert(uiState) + } } } } diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/transactions/SendTxResult.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/transactions/SendTxResult.kt index 25655c4119..b3bf9c7e7b 100644 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/transactions/SendTxResult.kt +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/transactions/SendTxResult.kt @@ -6,5 +6,6 @@ sealed interface SendTxResult { object UserCancelledError : SendTxResult data class TangemSdkError(val code: Int, val cause: Throwable?) : SendTxResult data class BlockchainSdkError(val code: Int, val cause: Throwable?) : SendTxResult + data class NetworkError(val ex: Exception? = null) : SendTxResult data class UnknownError(val ex: Exception? = null) : SendTxResult } \ No newline at end of file