Updated on 2026-08-14

This commit is contained in:
Tangem 2023-02-10 19:47:44 +03:00
parent d07de86eb8
commit 8051ee983d
9 changed files with 63 additions and 28 deletions

View file

@ -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 -> {

View file

@ -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

View file

@ -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
}
}

View file

@ -11,5 +11,6 @@ sealed class TxState {
object UserCancelled : TxState()
object BlockchainError : TxState()
object TangemSdkError : TxState()
object NetworkError : TxState()
object UnknownError : TxState()
}

View file

@ -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
}

View file

@ -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)

View file

@ -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,
)
}

View file

@ -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)
}
}
}
}

View file

@ -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
}