Updated on 2026-08-14
This commit is contained in:
commit
8e37d710a1
23 changed files with 123 additions and 53 deletions
|
|
@ -31,6 +31,9 @@ data class ExchangeDataResponse(
|
|||
)
|
||||
|
||||
data class TxDetails(
|
||||
@Json(name = "payoutAddress")
|
||||
val payoutAddress: String,
|
||||
|
||||
@Json(name = "requestId")
|
||||
val requestId: String,
|
||||
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@
|
|||
<string name="exchange_send_view_header">Вы отправляете</string>
|
||||
<string name="exchange_tokens_available_tokens_header">Мои токены</string>
|
||||
<string name="exchange_tokens_empty_tokens">У вас нет добавленных токенов. Добавьте токены для обмена</string>
|
||||
<string name="express_token_list_empty_search">Токены не найдены. Пожалуйста, попробуйте другой запрос</string>
|
||||
<string name="exchange_tokens_unavailable_tokens_header">Недоступен для обмена с %s</string>
|
||||
<string name="express_cex_fee_explanation">Кроме того, в курс обмена включена комиссия сети за отправку обмененных средств на ваш адрес</string>
|
||||
<string name="express_cex_status_button_title">Статус</string>
|
||||
|
|
|
|||
|
|
@ -192,6 +192,7 @@
|
|||
<string name="exchange_send_view_header">You send</string>
|
||||
<string name="exchange_tokens_available_tokens_header">My tokens</string>
|
||||
<string name="exchange_tokens_empty_tokens">You haven\'t added any tokens yet. Add tokens via Market to swap</string>
|
||||
<string name="express_token_list_empty_search">No tokens found. Please try another request</string>
|
||||
<string name="exchange_tokens_unavailable_tokens_header">Cannot be swapped for %s</string>
|
||||
<string name="express_cex_fee_explanation">Additionally, the network fee for sending the exchanged funds back to your address is included in the rate</string>
|
||||
<string name="express_cex_status_button_title">Status</string>
|
||||
|
|
|
|||
|
|
@ -292,6 +292,9 @@ internal class DefaultSwapRepository @Inject constructor(
|
|||
if (txDetails.requestId != requestId) {
|
||||
return@withContext DataError.InvalidRequestIdError().left()
|
||||
}
|
||||
if (toAddress != txDetails.payoutAddress) {
|
||||
return@withContext DataError.InvalidPayoutAddressError().left()
|
||||
}
|
||||
expressDataConverter.convert(
|
||||
ExchangeDataResponseWithTxDetails(
|
||||
dataResponse = response,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ internal class ExchangeStatusConverter : Converter<ExchangeStatusResponse, Excha
|
|||
it.name.lowercase() == value.externalStatus.name.lowercase()
|
||||
},
|
||||
txId = value.externalTxId,
|
||||
txUrl = value.externalTxUrl,
|
||||
txExternalUrl = value.externalTxUrl,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +36,8 @@ sealed class DataError {
|
|||
|
||||
data class InvalidRequestIdError(override val code: Int = 991) : DataError()
|
||||
|
||||
data class InvalidPayoutAddressError(override val code: Int = 992) : DataError()
|
||||
|
||||
object UnknownError : DataError() {
|
||||
override val code: Int = -1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ data class ExchangeStatusModel(
|
|||
val providerId: String,
|
||||
val status: ExchangeStatus? = null,
|
||||
val txId: String? = null,
|
||||
val txUrl: String? = null,
|
||||
val txExternalUrl: String? = null,
|
||||
)
|
||||
|
||||
enum class ExchangeStatus {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ data class TokensDataStateExpress(
|
|||
companion object {
|
||||
val EMPTY =
|
||||
TokensDataStateExpress(
|
||||
fromGroup = CurrenciesGroup(emptyList(), emptyList()),
|
||||
toGroup = CurrenciesGroup(emptyList(), emptyList()),
|
||||
fromGroup = CurrenciesGroup(emptyList(), emptyList(), false),
|
||||
toGroup = CurrenciesGroup(emptyList(), emptyList(), false),
|
||||
allProviders = emptyList(),
|
||||
)
|
||||
}
|
||||
|
|
@ -21,4 +21,5 @@ data class TokensDataStateExpress(
|
|||
data class CurrenciesGroup(
|
||||
val available: List<CryptoCurrencySwapInfo>,
|
||||
val unavailable: List<CryptoCurrencySwapInfo>,
|
||||
val afterSearch: Boolean,
|
||||
)
|
||||
|
|
@ -9,9 +9,8 @@ sealed class TxState {
|
|||
val fromAmountValue: BigDecimal? = null,
|
||||
val toAmount: String? = null,
|
||||
val toAmountValue: BigDecimal? = null,
|
||||
val txAddress: String,
|
||||
val txHash: String,
|
||||
val txExternalUrl: String? = null,
|
||||
val txUrl: String? = null,
|
||||
val timestamp: Long,
|
||||
) : TxState()
|
||||
|
||||
|
|
|
|||
|
|
@ -11,5 +11,5 @@ interface BlockchainInteractor {
|
|||
*/
|
||||
fun getBlockchainInfo(networkId: String): NetworkInfo
|
||||
|
||||
fun getExplorerTransactionLink(networkId: String, txAddress: String): String
|
||||
fun getExplorerTransactionLink(networkId: String, txHash: String): String
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ internal class DefaultBlockchainInteractor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getExplorerTransactionLink(networkId: String, txAddress: String): String {
|
||||
return transactionManager.getExplorerTransactionLink(networkId, txAddress)
|
||||
override fun getExplorerTransactionLink(networkId: String, txHash: String): String {
|
||||
return transactionManager.getExplorerTransactionLink(networkId, txHash)
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,6 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
private val dispatcher: CoroutineDispatcherProvider,
|
||||
private val swapTransactionRepository: SwapTransactionRepository,
|
||||
private val initialToCurrencyResolver: InitialToCurrencyResolver,
|
||||
private val blockchainInteractor: BlockchainInteractor,
|
||||
) : SwapInteractor {
|
||||
|
||||
private val estimateFeeUseCase by lazy(LazyThreadSafetyMode.NONE) {
|
||||
|
|
@ -148,6 +147,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
return CurrenciesGroup(
|
||||
available = availableCryptoCurrencies,
|
||||
unavailable = unavailableCryptoCurrencies.map { CryptoCurrencySwapInfo(it, emptyList()) },
|
||||
afterSearch = false,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -214,7 +214,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
is SendTxResult.Success -> {
|
||||
allowPermissionsHandler.addAddressToInProgress(permissionOptions.forTokenContractAddress)
|
||||
TxState.TxSent(
|
||||
txAddress = userWalletManager.getLastTransactionHash(networkId, derivationPath).orEmpty(),
|
||||
txHash = userWalletManager.getLastTransactionHash(networkId, derivationPath).orEmpty(),
|
||||
timestamp = System.currentTimeMillis(),
|
||||
)
|
||||
}
|
||||
|
|
@ -462,7 +462,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
currencyToGet.symbol,
|
||||
),
|
||||
toAmountValue = swapData.toTokenAmount.value,
|
||||
txAddress = userWalletManager.getLastTransactionHash(networkId, derivationPath).orEmpty(),
|
||||
txHash = userWalletManager.getLastTransactionHash(networkId, derivationPath).orEmpty(),
|
||||
timestamp = System.currentTimeMillis(),
|
||||
)
|
||||
}
|
||||
|
|
@ -536,10 +536,6 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
},
|
||||
ifRight = {
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val txUrl = blockchainInteractor.getExplorerTransactionLink(
|
||||
networkId = currencyToSend.currency.network.backendId,
|
||||
txAddress = exchangeData.transaction.txTo,
|
||||
)
|
||||
storeSwapTransaction(
|
||||
currencyToSend = currencyToSend,
|
||||
currencyToGet = currencyToGet,
|
||||
|
|
@ -547,7 +543,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
swapProvider = swapProvider,
|
||||
swapDataModel = exchangeData,
|
||||
timestamp = timestamp,
|
||||
txUrl = txUrl,
|
||||
txExternalUrl = externalUrl.orEmpty(),
|
||||
)
|
||||
storeLastCryptoCurrencyId(currencyToGet.currency)
|
||||
TxState.TxSent(
|
||||
|
|
@ -561,12 +557,11 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
currencyToGet.currency.symbol,
|
||||
),
|
||||
toAmountValue = exchangeData.toTokenAmount.value,
|
||||
txAddress = userWalletManager.getLastTransactionHash(
|
||||
txHash = userWalletManager.getLastTransactionHash(
|
||||
currencyToSend.currency.network.backendId,
|
||||
derivationPath,
|
||||
).orEmpty(),
|
||||
txExternalUrl = externalUrl,
|
||||
txUrl = txUrl,
|
||||
timestamp = timestamp,
|
||||
)
|
||||
},
|
||||
|
|
@ -601,7 +596,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
swapProvider: SwapProvider,
|
||||
swapDataModel: SwapDataModel,
|
||||
timestamp: Long,
|
||||
txUrl: String,
|
||||
txExternalUrl: String,
|
||||
) {
|
||||
swapTransactionRepository.storeTransaction(
|
||||
userWalletId = UserWalletId(userWalletManager.getWalletId()),
|
||||
|
|
@ -617,7 +612,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
providerId = swapProvider.providerId,
|
||||
status = ExchangeStatus.New,
|
||||
txId = swapDataModel.transaction.txId,
|
||||
txUrl = txUrl,
|
||||
txExternalUrl = txExternalUrl,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ class SwapDomainModule {
|
|||
walletManagersFacade: WalletManagersFacade,
|
||||
coroutineDispatcherProvider: CoroutineDispatcherProvider,
|
||||
initialToCurrencyResolver: InitialToCurrencyResolver,
|
||||
blockchainInteractor: BlockchainInteractor,
|
||||
): SwapInteractor {
|
||||
return SwapInteractorImpl(
|
||||
transactionManager = transactionManager,
|
||||
|
|
@ -57,7 +56,6 @@ class SwapDomainModule {
|
|||
dispatcher = coroutineDispatcherProvider,
|
||||
swapTransactionRepository = swapTransactionRepository,
|
||||
initialToCurrencyResolver = initialToCurrencyResolver,
|
||||
blockchainInteractor = blockchainInteractor,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class TokensDataConverter(
|
|||
.toImmutableList(),
|
||||
onSearchEntered = onSearchEntered,
|
||||
onTokenSelected = onTokenSelected,
|
||||
afterSearch = value.afterSearch,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
data class SwapSelectTokenStateHolder(
|
||||
val availableTokens: ImmutableList<TokenToSelectState>,
|
||||
val unavailableTokens: ImmutableList<TokenToSelectState>,
|
||||
val afterSearch: Boolean,
|
||||
val onSearchEntered: (String) -> Unit,
|
||||
val onTokenSelected: (String) -> Unit,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ sealed class SwapCardState {
|
|||
|
||||
data class SwapButton(
|
||||
val enabled: Boolean,
|
||||
val loading: Boolean = false,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -260,7 +260,9 @@ private fun ProviderLoadingState(modifier: Modifier = Modifier) {
|
|||
),
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size16),
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size16)
|
||||
.align(Alignment.CenterVertically),
|
||||
color = TangemTheme.colors.icon.informative,
|
||||
strokeWidth = TangemTheme.dimens.size2,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ internal class StateBuilder(
|
|||
),
|
||||
fee = FeeItemState.Empty,
|
||||
networkCurrency = networkInfo.blockchainCurrency,
|
||||
swapButton = SwapButton(enabled = false, loading = true, onClick = {}),
|
||||
swapButton = SwapButton(enabled = false, onClick = {}),
|
||||
onRefresh = {},
|
||||
onBackClicked = actions.onBackClicked,
|
||||
onChangeCardsClicked = actions.onChangeCardsClicked,
|
||||
|
|
@ -138,7 +138,6 @@ internal class StateBuilder(
|
|||
fee = FeeItemState.Empty,
|
||||
swapButton = SwapButton(
|
||||
enabled = false,
|
||||
loading = false,
|
||||
onClick = { },
|
||||
),
|
||||
changeCardsButtonState = ChangeCardsButtonState.DISABLED,
|
||||
|
|
@ -187,7 +186,7 @@ internal class StateBuilder(
|
|||
),
|
||||
warnings = emptyList(),
|
||||
fee = FeeItemState.Empty,
|
||||
swapButton = SwapButton(enabled = false, loading = true, onClick = {}),
|
||||
swapButton = SwapButton(enabled = false, onClick = {}),
|
||||
providerState = ProviderState.Loading(),
|
||||
permissionState = uiStateHolder.permissionState,
|
||||
changeCardsButtonState = ChangeCardsButtonState.UPDATE_IN_PROGRESS,
|
||||
|
|
@ -260,7 +259,6 @@ internal class StateBuilder(
|
|||
fee = feeState,
|
||||
swapButton = SwapButton(
|
||||
enabled = getSwapButtonEnabled(quoteModel.preparedSwapConfigState),
|
||||
loading = false,
|
||||
onClick = actions.onSwapClick,
|
||||
),
|
||||
changeCardsButtonState = if (isReverseSwapPossible) {
|
||||
|
|
@ -454,7 +452,6 @@ internal class StateBuilder(
|
|||
fee = FeeItemState.Empty,
|
||||
swapButton = SwapButton(
|
||||
enabled = false,
|
||||
loading = false,
|
||||
onClick = actions.onSwapClick,
|
||||
),
|
||||
changeCardsButtonState = if (isReverseSwapPossible) {
|
||||
|
|
@ -566,7 +563,6 @@ internal class StateBuilder(
|
|||
fee = FeeItemState.Empty,
|
||||
swapButton = SwapButton(
|
||||
enabled = false,
|
||||
loading = false,
|
||||
onClick = { },
|
||||
),
|
||||
changeCardsButtonState = if (isReverseSwapPossible) {
|
||||
|
|
@ -582,7 +578,6 @@ internal class StateBuilder(
|
|||
fun createSwapInProgressState(uiState: SwapStateHolder): SwapStateHolder {
|
||||
return uiState.copy(
|
||||
swapButton = uiState.swapButton.copy(
|
||||
loading = true,
|
||||
enabled = false,
|
||||
),
|
||||
)
|
||||
|
|
@ -737,6 +732,7 @@ internal class StateBuilder(
|
|||
dataState: SwapProcessDataState,
|
||||
onExploreClick: () -> Unit,
|
||||
onStatusClick: () -> Unit,
|
||||
txUrl: String,
|
||||
): SwapStateHolder {
|
||||
val fee = requireNotNull(dataState.selectedFee)
|
||||
val fromCryptoCurrency = requireNotNull(dataState.fromCryptoCurrency)
|
||||
|
|
@ -751,7 +747,7 @@ internal class StateBuilder(
|
|||
return uiState.copy(
|
||||
successState = SwapSuccessStateHolder(
|
||||
timestamp = txState.timestamp,
|
||||
txUrl = txState.txUrl.orEmpty(),
|
||||
txUrl = txUrl,
|
||||
providerName = stringReference(providerState.name),
|
||||
providerType = stringReference(providerState.type),
|
||||
showStatusButton = providerState.type == ExchangeProviderType.CEX.name,
|
||||
|
|
|
|||
|
|
@ -399,7 +399,6 @@ private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> U
|
|||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.swapping_insufficient_funds),
|
||||
enabled = false,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -408,7 +407,6 @@ private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> U
|
|||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.swapping_give_permission),
|
||||
enabled = true,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = onPermissionWarningClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -418,7 +416,6 @@ private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> U
|
|||
text = stringResource(id = R.string.common_swap),
|
||||
iconResId = R.drawable.ic_tangem_24,
|
||||
enabled = state.swapButton.enabled,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -487,7 +484,7 @@ private val state = SwapStateHolder(
|
|||
),
|
||||
),
|
||||
networkCurrency = "MATIC",
|
||||
swapButton = SwapButton(enabled = true, loading = false, onClick = {}),
|
||||
swapButton = SwapButton(enabled = true, onClick = {}),
|
||||
onRefresh = {},
|
||||
onBackClicked = {},
|
||||
onChangeCardsClicked = {},
|
||||
|
|
|
|||
|
|
@ -42,10 +42,16 @@ fun SwapSelectTokenScreen(state: SwapSelectTokenStateHolder, onBack: () -> Unit)
|
|||
.background(color = TangemTheme.colors.background.secondary),
|
||||
content = { padding ->
|
||||
val modifier = Modifier.padding(padding)
|
||||
if (state.availableTokens.isEmpty() && state.unavailableTokens.isEmpty()) {
|
||||
EmptyTokensList(modifier)
|
||||
} else {
|
||||
ListOfTokens(state = state, modifier = modifier)
|
||||
when {
|
||||
state.availableTokens.isEmpty() && state.unavailableTokens.isEmpty() && state.afterSearch -> {
|
||||
TokensNotFound(modifier)
|
||||
}
|
||||
state.availableTokens.isEmpty() && state.unavailableTokens.isEmpty() && !state.afterSearch -> {
|
||||
EmptyTokensList(modifier)
|
||||
}
|
||||
else -> {
|
||||
ListOfTokens(state = state, modifier = modifier)
|
||||
}
|
||||
}
|
||||
},
|
||||
topBar = {
|
||||
|
|
@ -91,6 +97,26 @@ private fun EmptyTokensList(modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokensNotFound(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.background(TangemTheme.colors.background.secondary)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(top = TangemTheme.dimens.spacing32)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing30)
|
||||
.align(Alignment.TopCenter),
|
||||
text = stringResource(id = R.string.express_token_list_empty_search),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier = Modifier) {
|
||||
val screenBackgroundColor = TangemTheme.colors.background.secondary
|
||||
|
|
@ -283,6 +309,7 @@ private fun TokenScreenPreview() {
|
|||
state = SwapSelectTokenStateHolder(
|
||||
availableTokens = listOf(title, token, token, token).toImmutableList(),
|
||||
unavailableTokens = listOf(title, token, token, token).toImmutableList(),
|
||||
afterSearch = false,
|
||||
onSearchEntered = {},
|
||||
onTokenSelected = {},
|
||||
),
|
||||
|
|
|
|||
|
|
@ -418,7 +418,7 @@ private fun TokenIcon(
|
|||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.padding(all = TangemTheme.dimens.spacing0_5),
|
||||
modifier = Modifier.padding(all = TangemTheme.dimens.spacing2),
|
||||
painter = painterResource(id = networkIconRes),
|
||||
contentDescription = null,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,8 +12,11 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
|
||||
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.feature.swap.analytics.SwapEvents
|
||||
import com.tangem.feature.swap.domain.BlockchainInteractor
|
||||
import com.tangem.feature.swap.domain.SwapInteractor
|
||||
|
|
@ -32,6 +35,7 @@ import com.tangem.feature.swap.ui.StateBuilder
|
|||
import com.tangem.utils.coroutines.*
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -57,6 +61,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getCryptoCurrencyStatusUseCase: GetCryptoCurrencyStatusSyncUseCase,
|
||||
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), DefaultLifecycleObserver {
|
||||
|
||||
|
|
@ -93,6 +98,11 @@ internal class SwapViewModel @Inject constructor(
|
|||
private var isOrderReversed = false
|
||||
private val lastAmount = mutableStateOf(INITIAL_AMOUNT)
|
||||
private var swapRouter: SwapRouter by Delegates.notNull()
|
||||
|
||||
private val isExchangeTooSmallAmountError: (SwapState) -> Boolean = {
|
||||
it is SwapState.SwapError && it.error is DataError.ExchangeTooSmallAmountError
|
||||
}
|
||||
|
||||
val currentScreen: SwapNavScreen
|
||||
get() = swapRouter.currentScreen
|
||||
|
||||
|
|
@ -380,13 +390,17 @@ internal class SwapViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun selectProvider(state: Map<SwapProvider, SwapState>): SwapProvider {
|
||||
val stateSuccess = state.getLastLoadedSuccessStates()
|
||||
return if (stateSuccess.isNotEmpty()) {
|
||||
val consideredProviders = state.filter {
|
||||
it.value is SwapState.QuotesLoadedState || isExchangeTooSmallAmountError(it.value)
|
||||
}
|
||||
|
||||
return if (consideredProviders.isNotEmpty()) {
|
||||
val currentSelected = dataState.selectedProvider
|
||||
if (currentSelected != null && stateSuccess.keys.contains(currentSelected)) {
|
||||
if (currentSelected != null && consideredProviders.keys.contains(currentSelected)) {
|
||||
currentSelected
|
||||
} else {
|
||||
findBestQuoteProvider(stateSuccess) ?: stateSuccess.keys.first()
|
||||
findBestQuoteProvider(consideredProviders.getLastLoadedSuccessStates())
|
||||
?: consideredProviders.keys.first()
|
||||
}
|
||||
} else {
|
||||
state.keys.first()
|
||||
|
|
@ -448,14 +462,19 @@ internal class SwapViewModel @Inject constructor(
|
|||
}.onSuccess {
|
||||
when (it) {
|
||||
is TxState.TxSent -> {
|
||||
val url = blockchainInteractor.getExplorerTransactionLink(
|
||||
networkId = fromCurrency.currency.network.backendId,
|
||||
txHash = it.txHash,
|
||||
)
|
||||
updateWalletBalance()
|
||||
uiState = stateBuilder.createSuccessState(
|
||||
uiState = uiState,
|
||||
txState = it,
|
||||
dataState = dataState,
|
||||
txUrl = url,
|
||||
onExploreClick = {
|
||||
val txUrl = it.txUrl
|
||||
if (!txUrl.isNullOrBlank()) {
|
||||
swapRouter.openUrl(txUrl)
|
||||
if (it.txHash.isNotEmpty()) {
|
||||
swapRouter.openUrl(url)
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
event = SwapEvents.ButtonExplore(initialCryptoCurrency.symbol),
|
||||
|
|
@ -580,13 +599,16 @@ internal class SwapViewModel @Inject constructor(
|
|||
fromGroup = tokenDataState.fromGroup.copy(
|
||||
available = available,
|
||||
unavailable = unavailable,
|
||||
afterSearch = true,
|
||||
),
|
||||
|
||||
)
|
||||
} else {
|
||||
tokenDataState.copy(
|
||||
toGroup = tokenDataState.toGroup.copy(
|
||||
available = available,
|
||||
unavailable = unavailable,
|
||||
afterSearch = true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -957,10 +979,32 @@ internal class SwapViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateWalletBalance() {
|
||||
swapInteractor.getSelectedWallet()?.let { userWallet ->
|
||||
dataState.fromCryptoCurrency?.currency?.network?.let { network ->
|
||||
viewModelScope.launch {
|
||||
withContext(NonCancellable) {
|
||||
updateForBalance(userWallet, network)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateForBalance(userWallet: UserWallet, network: Network) {
|
||||
updateDelayedCurrencyStatusUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
network = network,
|
||||
delayMillis = UPDATE_BALANCE_DELAY_MILLIS,
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val loggingTag = "SwapViewModel"
|
||||
private const val INITIAL_AMOUNT = ""
|
||||
private const val UPDATE_DELAY = 10000L
|
||||
private const val DEBOUNCE_AMOUNT_DELAY = 1000L
|
||||
private const val UPDATE_BALANCE_DELAY_MILLIS = 11000L
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
SwapTransactionsState(
|
||||
txId = transaction.txId,
|
||||
provider = transaction.provider,
|
||||
txUrl = transaction.status?.txUrl,
|
||||
txUrl = transaction.status?.txExternalUrl,
|
||||
timestamp = TextReference.Str("${timestamp.toDateFormat()}, ${timestamp.toTimeFormat()}"),
|
||||
fiatSymbol = appCurrency.symbol,
|
||||
statuses = getStatuses(transaction.status?.status),
|
||||
|
|
@ -74,7 +74,7 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
activeStatus = transaction.status?.status,
|
||||
notification = getNotification(
|
||||
transaction.status?.status,
|
||||
transaction.status?.txUrl,
|
||||
transaction.status?.txExternalUrl,
|
||||
),
|
||||
toCryptoCurrencyId = toCurrency.currency.id,
|
||||
toCryptoAmount = BigDecimalFormatter.formatCryptoAmount(
|
||||
|
|
@ -112,9 +112,9 @@ internal class TokenDetailsSwapTransactionsStateConverter(
|
|||
return tx.copy(
|
||||
activeStatus = statusModel.status,
|
||||
hasFailed = hasFailed,
|
||||
notification = getNotification(statusModel.status, statusModel.txUrl),
|
||||
notification = getNotification(statusModel.status, statusModel.txExternalUrl),
|
||||
statuses = getStatuses(statusModel.status, hasFailed),
|
||||
txUrl = statusModel.txUrl,
|
||||
txUrl = statusModel.txExternalUrl,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue