diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 6fe946df8b..391fe57950 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -647,6 +647,8 @@
Не для пользователя!
Cеть %1$s использует концепцию экзистенциального депозита. Если баланс вашего счета будет ниже %2$s, то он будет деактивирован, а средства на счете уничтожены.
Для работы с сетью необходим депозит
+ Обмен будет доступен после завершения %s транзакции
+ У вас есть активная транзакция
У вас в списке нет монет доступных для обмена с %s
Нет доступных токенов для обмена
Cервис временно недоступен
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index cbd1bf2dab..08c1787e9b 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -228,7 +228,7 @@
Best rate
Available from %s
Unavailable for this pair
- Permission Needed
+ Permission Required
The following information is optional. You can erase it if you don\'t want to share it.
Tell us what functions you are missing, and we will try to help you.
Please tell us what card do you have
@@ -677,6 +677,8 @@
Not for users!
%1$s network requires an Existential Deposit. If your account drops below %2$s, it will be deactivated, and any remaining funds will be destroyed.
Network requires Existential Deposit
+ Swap will be available after the %s transaction is complete
+ You have active transaction
You do not have any %s exchangeable coins in your list
No available tokens to swap
To make a transaction you need to deposit some %1$s %2$s
diff --git a/features/swap/domain/build.gradle.kts b/features/swap/domain/build.gradle.kts
index f3841b400d..b1a50d2773 100644
--- a/features/swap/domain/build.gradle.kts
+++ b/features/swap/domain/build.gradle.kts
@@ -28,6 +28,7 @@ dependencies {
implementation(projects.domain.demo)
implementation(projects.domain.card)
implementation(projects.domain.appCurrency.models)
+ implementation(projects.domain.txhistory.models)
/** Core modules */
implementation(projects.core.utils)
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 6504c6d53a..f15db9df11 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
@@ -420,7 +420,6 @@ internal class SwapInteractorImpl @Inject constructor(
permissionState = PermissionDataState.Empty,
preparedSwapConfigState = state.preparedSwapConfigState.copy(
isBalanceEnough = includeFeeInAmount !is IncludeFeeInAmount.BalanceNotEnough,
- isFeeEnough = includeFeeInAmount !is IncludeFeeInAmount.BalanceNotEnough,
includeFeeInAmount = includeFeeInAmount,
),
)
@@ -798,6 +797,7 @@ internal class SwapInteractorImpl @Inject constructor(
isFeeEnough = includeFeeInAmount !is IncludeFeeInAmount.BalanceNotEnough,
isAllowedToSpend = isAllowedToSpend,
isBalanceEnough = isBalanceWithoutFeeEnough,
+ hasOutgoingTransaction = hasOutgoingTransaction(fromToken),
includeFeeInAmount = includeFeeInAmount,
),
)
@@ -873,7 +873,7 @@ internal class SwapInteractorImpl @Inject constructor(
/**
* Load swap data calls only if spend is allowed for token contract address
*/
- @Suppress("LongParameterList")
+ @Suppress("LongParameterList", "LongMethod")
private suspend fun loadDexSwapData(
provider: SwapProvider,
networkId: String,
@@ -932,6 +932,7 @@ internal class SwapInteractorImpl @Inject constructor(
isAllowedToSpend = true,
isBalanceEnough = isBalanceIncludeFeeEnough,
isFeeEnough = isFeeEnough,
+ hasOutgoingTransaction = hasOutgoingTransaction(fromToken),
includeFeeInAmount = IncludeFeeInAmount.Excluded, // exclude for dex
),
)
@@ -1226,6 +1227,10 @@ internal class SwapInteractorImpl @Inject constructor(
}
}
+ private fun hasOutgoingTransaction(cryptoCurrencyStatuses: CryptoCurrencyStatus): Boolean {
+ return cryptoCurrencyStatuses.value.pendingTransactions.any { it.isOutgoing }
+ }
+
private fun Fee.getGasLimit(): Int {
return when (this) {
is Fee.Common -> 0
diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/PreparedSwapConfigState.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/PreparedSwapConfigState.kt
index 53cfae4ded..ca44dc046e 100644
--- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/PreparedSwapConfigState.kt
+++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/domain/PreparedSwapConfigState.kt
@@ -14,6 +14,7 @@ data class PreparedSwapConfigState(
val isAllowedToSpend: Boolean,
val isBalanceEnough: Boolean,
val isFeeEnough: Boolean,
+ val hasOutgoingTransaction: Boolean,
val includeFeeInAmount: IncludeFeeInAmount,
)
diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt
index d031891ba3..d2788d35a4 100644
--- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt
+++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt
@@ -19,6 +19,7 @@ sealed interface SwapState {
isAllowedToSpend = false,
isBalanceEnough = false,
isFeeEnough = false,
+ hasOutgoingTransaction = false,
includeFeeInAmount = IncludeFeeInAmount.Excluded,
),
val permissionState: PermissionDataState = PermissionDataState.Empty,
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 e30eff225c..ae4d9ba3cf 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,6 +6,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.components.states.Item
import com.tangem.core.ui.components.states.SelectableItemsState
+import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.feature.swap.domain.models.ui.TxFee
import com.tangem.feature.swap.models.states.FeeItemState
@@ -125,6 +126,7 @@ sealed interface SwapWarning {
data class TooSmallAmountWarning(val notificationConfig: NotificationConfig) : SwapWarning
data class UnableToCoverFeeWarning(val notificationConfig: NotificationConfig) : SwapWarning
data class GeneralWarning(val notificationConfig: NotificationConfig) : SwapWarning
+ data class TransactionInProgressWarning(val title: TextReference, val description: TextReference) : SwapWarning
}
enum class GenericWarningType {
diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt
index 51f6936885..41adc2a361 100644
--- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt
+++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ProviderItem.kt
@@ -14,6 +14,7 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import coil.compose.SubcomposeAsyncImage
@@ -261,6 +262,7 @@ private fun ProviderLoadingState(modifier: Modifier = Modifier) {
CircularProgressIndicator(
modifier = Modifier.size(TangemTheme.dimens.size16),
color = TangemTheme.colors.icon.informative,
+ strokeWidth = TangemTheme.dimens.size2,
)
Text(
text = "Fetching best rates ...",
@@ -373,7 +375,7 @@ private fun PermissionBadgeItem(modifier: Modifier = Modifier) {
),
) {
Text(
- text = "Permission required",
+ text = stringResource(id = R.string.express_provider_permission_needed),
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing6),
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 6881df60df..2837f48f2a 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
@@ -320,6 +320,26 @@ internal class StateBuilder(
),
)
}
+ if (quoteModel.permissionState is PermissionDataState.PermissionLoading) {
+ warnings.add(
+ SwapWarning.TransactionInProgressWarning(
+ title = resourceReference(R.string.swapping_pending_transaction_title),
+ description = resourceReference(R.string.swapping_pending_transaction_subtitle),
+ ),
+ )
+ } else if (quoteModel.preparedSwapConfigState.hasOutgoingTransaction) {
+ warnings.add(
+ SwapWarning.TransactionInProgressWarning(
+ title = resourceReference(R.string.warning_express_active_transaction_title),
+ description = resourceReference(
+ id = R.string.warning_express_active_transaction_message,
+ formatArgs = wrappedList(
+ quoteModel.fromTokenInfo.cryptoCurrencyStatus.currency.network.currencySymbol,
+ ),
+ ),
+ ),
+ )
+ }
return warnings
}
@@ -430,10 +450,18 @@ internal class StateBuilder(
iconResId = R.drawable.ic_alert_circle_24,
),
)
- is DataError.UnknownError -> SwapWarning.GeneralWarning(
+ else -> SwapWarning.GeneralWarning(
notificationConfig = NotificationConfig(
- title = resourceReference(R.string.common_error),
- subtitle = resourceReference(R.string.swapping_generic_error),
+ title = if (dataError is DataError.UnknownError) {
+ resourceReference(R.string.common_error)
+ } else {
+ resourceReference(R.string.warning_express_refresh_required_title)
+ },
+ subtitle = if (dataError is DataError.UnknownError) {
+ resourceReference(R.string.swapping_generic_error)
+ } else {
+ resourceReference(R.string.generic_error_code, wrappedList(dataError.code.toString()))
+ },
iconResId = R.drawable.img_attention_20,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = resourceReference(R.string.warning_button_refresh),
@@ -441,13 +469,6 @@ internal class StateBuilder(
),
),
)
- else -> SwapWarning.GeneralWarning(
- notificationConfig = NotificationConfig(
- title = resourceReference(R.string.common_error),
- subtitle = resourceReference(R.string.generic_error_code, wrappedList(dataError.code.toString())),
- iconResId = R.drawable.img_attention_20,
- ),
- )
}
}
@@ -956,7 +977,7 @@ internal class StateBuilder(
// region warnings
private fun createPermissionNotificationConfig(fromTokenSymbol: String): NotificationConfig {
return NotificationConfig(
- title = resourceReference(R.string.swapping_permission_header),
+ title = resourceReference(R.string.express_provider_permission_needed),
subtitle = resourceReference(
id = R.string.swapping_permission_subheader,
formatArgs = wrappedList(fromTokenSymbol),
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 ace1d19a31..8d1fe2e6df 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
@@ -20,6 +20,7 @@ import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.getActiveIconResByCoinId
+import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.swap.domain.models.ui.FeeType
@@ -67,20 +68,6 @@ internal fun SwapScreenContent(state: SwapStateHolder, modifier: Modifier = Modi
if (state.warnings.isNotEmpty()) SwapWarnings(warnings = state.warnings)
- AnimatedVisibility(visible = state.permissionState is SwapPermissionState.InProgress) {
- CardWithIcon(
- title = stringResource(id = R.string.swapping_pending_transaction_title),
- description = stringResource(id = R.string.swapping_pending_transaction_subtitle),
- icon = {
- CircularProgressIndicator(
- modifier = Modifier
- .size(TangemTheme.dimens.size16),
- color = TangemTheme.colors.icon.primary1,
- strokeWidth = TangemTheme.dimens.size2,
- )
- },
- )
- }
MainButton(state = state, onPermissionWarningClick = state.onShowPermissionBottomSheet)
}
}
@@ -285,6 +272,20 @@ private fun SwapWarnings(warnings: List) {
config = warning.notificationConfig,
)
}
+ is SwapWarning.TransactionInProgressWarning -> {
+ CardWithIcon(
+ title = warning.title.resolveReference(),
+ description = warning.description.resolveReference(),
+ icon = {
+ CircularProgressIndicator(
+ modifier = Modifier
+ .size(TangemTheme.dimens.size16),
+ color = TangemTheme.colors.icon.primary1,
+ strokeWidth = TangemTheme.dimens.size2,
+ )
+ },
+ )
+ }
else -> {}
}
SpacerH8()
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 ac172210c3..b17c398d2e 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
@@ -346,9 +346,7 @@ internal class SwapViewModel @Inject constructor(
)
}
is SwapState.SwapError -> {
- if (state.error is DataError.UnknownError) {
- singleTaskScheduler.cancelTask()
- }
+ singleTaskScheduler.cancelTask()
uiState = stateBuilder.createQuotesErrorState(
uiStateHolder = uiState,
swapProvider = provider,