Updated on 2026-08-14
This commit is contained in:
commit
4ce200b8d4
9 changed files with 58 additions and 30 deletions
|
|
@ -185,6 +185,7 @@ internal class SwapModel @Inject constructor(
|
|||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||
isAccountsModeProvider = Provider { isAccountsMode },
|
||||
iGaslessFeeSupportedForNetwork = iGaslessFeeSupportedForNetwork,
|
||||
appRouter = appRouter,
|
||||
)
|
||||
|
||||
private val inputNumberFormatter = InputNumberFormatter(
|
||||
|
|
@ -1499,16 +1500,6 @@ internal class SwapModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
},
|
||||
onBuyClick = {
|
||||
val fromSwapCurrencyStatus = dataState.fromSwapCurrencyStatus ?: return@UiActions
|
||||
val feePaidCryptoCurrency = dataState.feePaidCryptoCurrency ?: return@UiActions
|
||||
val route = AppRoute.CurrencyDetails(
|
||||
userWalletId = fromSwapCurrencyStatus.userWalletId,
|
||||
currency = feePaidCryptoCurrency.currency,
|
||||
)
|
||||
|
||||
appRouter.push(route)
|
||||
},
|
||||
onRetryClick = {
|
||||
startLoadingQuotesFromLastState()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.feature.swap.model
|
||||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addDustWarningNotification
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addExistentialWarningNotification
|
||||
|
|
@ -106,6 +108,7 @@ internal class SwapNotificationsFactory(
|
|||
selectedFeeType: FeeType,
|
||||
providerName: String,
|
||||
hideFee: Boolean,
|
||||
appRouter: AppRouter,
|
||||
): ImmutableList<NotificationUM> {
|
||||
val warnings = buildList {
|
||||
maybeAddRentExemptionError(quoteModel)
|
||||
|
|
@ -113,7 +116,12 @@ internal class SwapNotificationsFactory(
|
|||
maybeAddNeedReserveToCreateAccountWarning(quoteModel)
|
||||
maybeAddPermissionNeededWarning(quoteModel, providerName)
|
||||
maybeAddNetworkFeeCoverageWarning(quoteModel, selectedFeeType)
|
||||
maybeAddUnableCoverFeeWarning(quoteModel, feeCryptoCurrencyStatus, hideFee)
|
||||
maybeAddUnableCoverFeeWarning(
|
||||
quoteModel = quoteModel,
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
hideFee = hideFee,
|
||||
appRouter = appRouter,
|
||||
)
|
||||
maybeAddTransactionInProgressWarning(quoteModel)
|
||||
maybeAddPriceImpactNotification(quoteModel.priceImpact)
|
||||
}
|
||||
|
|
@ -299,17 +307,20 @@ internal class SwapNotificationsFactory(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
private fun MutableList<NotificationUM>.maybeAddUnableCoverFeeWarning(
|
||||
quoteModel: SwapState.QuotesLoadedState,
|
||||
feeCryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||
hideFee: Boolean,
|
||||
appRouter: AppRouter,
|
||||
) {
|
||||
if (hideFee) return
|
||||
val fromCurrency = quoteModel.fromTokenInfo.swapCurrencyStatus.currency
|
||||
if (hideFee || feeCryptoCurrencyStatus == null) return
|
||||
val fromSwapCurrency = quoteModel.fromTokenInfo.swapCurrencyStatus
|
||||
val fromCurrency = fromSwapCurrency.currency
|
||||
val feeEnoughState = quoteModel.preparedSwapConfigState.feeState as? SwapFeeState.NotEnough
|
||||
val shouldShowCoverWarning = !quoteModel.preparedSwapConfigState.isBalanceEnough &&
|
||||
quoteModel.permissionState !is PermissionDataState.PermissionLoading &&
|
||||
feeCryptoCurrencyStatus?.currency != fromCurrency
|
||||
feeCryptoCurrencyStatus.currency != fromCurrency
|
||||
|
||||
val isCEXProvider = quoteModel.swapProvider.type == ExchangeProviderType.CEX
|
||||
|
||||
|
|
@ -320,13 +331,25 @@ internal class SwapNotificationsFactory(
|
|||
|
||||
if (shouldShowCoverWarning && !isGaslessAvailable || isNotEnoughFee) {
|
||||
add(
|
||||
SwapNotificationUM.Error.UnableToCoverFeeWarning(
|
||||
fromToken = fromCurrency,
|
||||
feeCurrency = feeCryptoCurrencyStatus?.currency,
|
||||
currencyName = feeEnoughState?.currencyName ?: fromCurrency.network.name,
|
||||
currencySymbol = feeEnoughState?.currencySymbol ?: fromCurrency.network.currencySymbol,
|
||||
onConfirmClick = actions.onBuyClick,
|
||||
),
|
||||
if (fromCurrency.id == feeCryptoCurrencyStatus.currency.id) {
|
||||
SwapNotificationUM.Error.InsufficientFunds
|
||||
} else {
|
||||
val route = AppRoute.CurrencyDetails(
|
||||
userWalletId = fromSwapCurrency.userWalletId,
|
||||
currency = feeCryptoCurrencyStatus.currency,
|
||||
)
|
||||
SwapNotificationUM.Error.UnableToCoverFeeWarning(
|
||||
fromToken = fromCurrency,
|
||||
feeCurrency = feeCryptoCurrencyStatus.currency,
|
||||
currencyName = feeEnoughState?.currencyName ?: fromCurrency.network.name,
|
||||
currencySymbol = feeEnoughState?.currencySymbol ?: fromCurrency.network.currencySymbol,
|
||||
onConfirmClick = if (!appRouter.stack.contains(route)) {
|
||||
{ appRouter.push(route) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ internal data class UiActions(
|
|||
val onSelectFeeType: (TxFee.Legacy) -> Unit,
|
||||
val onProviderClick: (String) -> Unit,
|
||||
val onProviderSelect: (String) -> Unit,
|
||||
val onBuyClick: () -> Unit,
|
||||
val onSelectTokenClick: (TokenSelectionDirection) -> Unit,
|
||||
val onSuccess: () -> Unit,
|
||||
val onLinkClick: (String) -> Unit,
|
||||
|
|
|
|||
|
|
@ -55,12 +55,17 @@ internal object SwapNotificationUM {
|
|||
),
|
||||
)
|
||||
|
||||
data object InsufficientFunds : Error(
|
||||
title = resourceReference(R.string.swapping_insufficient_funds),
|
||||
subtitle = resourceReference(R.string.swapping_insufficient_funds_description),
|
||||
)
|
||||
|
||||
data class UnableToCoverFeeWarning(
|
||||
val fromToken: CryptoCurrency,
|
||||
val currencyName: String,
|
||||
val currencySymbol: String,
|
||||
val feeCurrency: CryptoCurrency?,
|
||||
val onConfirmClick: () -> Unit,
|
||||
val feeCurrency: CryptoCurrency,
|
||||
val onConfirmClick: (() -> Unit)?,
|
||||
) : Error(
|
||||
title = resourceReference(
|
||||
R.string.warning_express_not_enough_fee_for_token_tx_title,
|
||||
|
|
@ -71,7 +76,7 @@ internal object SwapNotificationUM {
|
|||
wrappedList(currencyName, currencySymbol),
|
||||
),
|
||||
iconResId = fromToken.networkIconResId,
|
||||
buttonState = feeCurrency?.let {
|
||||
buttonState = onConfirmClick?.let {
|
||||
NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.common_buy_currency, wrappedList(currencySymbol)),
|
||||
onClick = onConfirmClick,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.swap.ui
|
|||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.account.AccountIconUM
|
||||
import com.tangem.common.ui.account.AccountTitleUM
|
||||
import com.tangem.common.ui.account.CryptoPortfolioIconConverter
|
||||
|
|
@ -58,6 +59,7 @@ internal class StateBuilder(
|
|||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val isAccountsModeProvider: Provider<Boolean>,
|
||||
private val iGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork,
|
||||
private val appRouter: AppRouter,
|
||||
) {
|
||||
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
|
||||
|
||||
|
|
@ -463,6 +465,7 @@ internal class StateBuilder(
|
|||
selectedFeeType = selectedFeeType,
|
||||
providerName = swapProvider.name,
|
||||
hideFee = hideFee,
|
||||
appRouter = appRouter,
|
||||
)
|
||||
|
||||
val fromAccountTitleUM = when {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.swap
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.express.models.ExpressError
|
||||
import com.tangem.domain.models.account.Account
|
||||
|
|
@ -31,6 +32,7 @@ internal class StateBuilderInitialStateTest {
|
|||
private val appCurrencyProvider: Provider<AppCurrency> = mockk()
|
||||
private val isAccountsModeProvider: Provider<Boolean> = mockk()
|
||||
private val iGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork = mockk()
|
||||
private val appRouter: AppRouter = mockk()
|
||||
|
||||
private lateinit var sut: StateBuilder
|
||||
|
||||
|
|
@ -48,6 +50,7 @@ internal class StateBuilderInitialStateTest {
|
|||
appCurrencyProvider = appCurrencyProvider,
|
||||
isAccountsModeProvider = isAccountsModeProvider,
|
||||
iGaslessFeeSupportedForNetwork = iGaslessFeeSupportedForNetwork,
|
||||
appRouter = appRouter
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.swap
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -26,6 +27,7 @@ internal class StateBuilderPairsTest {
|
|||
private val appCurrencyProvider: Provider<AppCurrency> = mockk()
|
||||
private val isAccountsModeProvider: Provider<Boolean> = mockk()
|
||||
private val iGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork = mockk()
|
||||
private val appRouter: AppRouter = mockk()
|
||||
|
||||
private lateinit var sut: StateBuilder
|
||||
|
||||
|
|
@ -53,6 +55,7 @@ internal class StateBuilderPairsTest {
|
|||
appCurrencyProvider = appCurrencyProvider,
|
||||
isAccountsModeProvider = isAccountsModeProvider,
|
||||
iGaslessFeeSupportedForNetwork = iGaslessFeeSupportedForNetwork,
|
||||
appRouter = appRouter,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.feature.swap
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.swap.models.SwapCurrencyStatus
|
||||
|
|
@ -14,12 +14,10 @@ import com.tangem.feature.swap.domain.models.ui.*
|
|||
import com.tangem.feature.swap.models.*
|
||||
import com.tangem.feature.swap.models.states.FeeItemState
|
||||
import com.tangem.feature.swap.models.states.ProviderState
|
||||
import com.tangem.feature.swap.models.states.SwapNotificationUM
|
||||
import com.tangem.feature.swap.ui.StateBuilder
|
||||
import com.tangem.utils.Provider
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
|
@ -32,6 +30,7 @@ internal class StateBuilderQuotesTest {
|
|||
private val appCurrencyProvider: Provider<AppCurrency> = mockk()
|
||||
private val isAccountsModeProvider: Provider<Boolean> = mockk()
|
||||
private val iGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork = mockk()
|
||||
private val appRouter: AppRouter = mockk()
|
||||
|
||||
private lateinit var sut: StateBuilder
|
||||
|
||||
|
|
@ -60,6 +59,7 @@ internal class StateBuilderQuotesTest {
|
|||
appCurrencyProvider = appCurrencyProvider,
|
||||
isAccountsModeProvider = isAccountsModeProvider,
|
||||
iGaslessFeeSupportedForNetwork = iGaslessFeeSupportedForNetwork,
|
||||
appRouter = appRouter,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
package com.tangem.feature.swap
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.swap.models.SwapCurrencyStatus
|
||||
import com.tangem.domain.transaction.usecase.gasless.IsGaslessFeeSupportedForNetwork
|
||||
import com.tangem.feature.swap.domain.models.domain.*
|
||||
import com.tangem.feature.swap.domain.models.ui.*
|
||||
import com.tangem.feature.swap.model.SwapProcessDataState
|
||||
import com.tangem.feature.swap.models.*
|
||||
import com.tangem.feature.swap.models.states.FeeItemState
|
||||
import com.tangem.feature.swap.models.states.ProviderState
|
||||
import com.tangem.feature.swap.models.states.SwapNotificationUM
|
||||
import com.tangem.feature.swap.ui.StateBuilder
|
||||
|
|
@ -31,6 +30,7 @@ internal class StateBuilderSwapDataTest {
|
|||
private val appCurrencyProvider: Provider<AppCurrency> = mockk()
|
||||
private val isAccountsModeProvider: Provider<Boolean> = mockk()
|
||||
private val iGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork = mockk()
|
||||
private val appRouter: AppRouter = mockk()
|
||||
|
||||
private lateinit var sut: StateBuilder
|
||||
|
||||
|
|
@ -59,6 +59,7 @@ internal class StateBuilderSwapDataTest {
|
|||
appCurrencyProvider = appCurrencyProvider,
|
||||
isAccountsModeProvider = isAccountsModeProvider,
|
||||
iGaslessFeeSupportedForNetwork = iGaslessFeeSupportedForNetwork,
|
||||
appRouter = appRouter,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue