Updated on 2026-08-14
This commit is contained in:
parent
d07de86eb8
commit
8051ee983d
9 changed files with 63 additions and 28 deletions
|
|
@ -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 -> {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,5 +11,6 @@ sealed class TxState {
|
|||
object UserCancelled : TxState()
|
||||
object BlockchainError : TxState()
|
||||
object TangemSdkError : TxState()
|
||||
object NetworkError : TxState()
|
||||
object UnknownError : TxState()
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue