Updated on 2026-08-14
This commit is contained in:
commit
0470e1f010
1144 changed files with 48463 additions and 11166 deletions
|
|
@ -23,6 +23,7 @@ dependencies {
|
|||
implementation(projects.features.swapV2.api)
|
||||
implementation(projects.features.manageTokens.api)
|
||||
implementation(projects.features.sendV2.api)
|
||||
implementation(projects.features.commonFeatures.api)
|
||||
|
||||
/** Core */
|
||||
implementation(projects.core.decompose)
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ sealed class SwapAmountFieldUM {
|
|||
val subtitleEllipsisLeft: TextEllipsis,
|
||||
val subtitleEllipsisRight: TextEllipsis,
|
||||
val isClickEnabled: Boolean,
|
||||
val shouldShowApproximatePrefix: Boolean,
|
||||
) : SwapAmountFieldUM()
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +96,6 @@ data class PriceImpact(
|
|||
val amountSignificance: AmountSignificance,
|
||||
val type: Type,
|
||||
) {
|
||||
|
||||
enum class Type {
|
||||
NONE, LOW, MEDIUM, HIGH
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
private val amountAnalyticsSender = SwapAmountAnalyticsSender(analyticsEventHandler)
|
||||
|
||||
private var autoUpdateSubscriberJob: Job? = null
|
||||
private var navigationJob: Job? = null
|
||||
|
||||
val uiState: StateFlow<SwapAmountUM>
|
||||
field = MutableStateFlow(params.amountUM)
|
||||
|
|
@ -139,7 +140,6 @@ internal class SwapAmountModel @Inject constructor(
|
|||
?: UserCountry.Other(Locale.getDefault().country)
|
||||
isShowBestRateAnimation = swapBestRateAnimationStore.getSyncOrNull()
|
||||
}
|
||||
configAmountNavigation()
|
||||
subscribeOnCryptoCurrencyStatusFlow()
|
||||
subscribeOnAmountUpdateTriggerUpdates()
|
||||
observeChooseSelectToken()
|
||||
|
|
@ -156,6 +156,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
} else {
|
||||
QUOTES_UPDATE_DELAY
|
||||
}
|
||||
configAmountNavigation()
|
||||
quoteTaskScheduler.scheduleTask(
|
||||
scope = modelScope,
|
||||
task = loadQuotesTask(initialDelay = initialDelay),
|
||||
|
|
@ -166,6 +167,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
fun onStop() {
|
||||
quoteTaskScheduler.cancelTask()
|
||||
autoUpdateSubscriberJob?.cancel()
|
||||
navigationJob?.cancel()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
|
@ -901,7 +903,8 @@ internal class SwapAmountModel @Inject constructor(
|
|||
|
||||
private fun configAmountNavigation() {
|
||||
val params = params as? SwapAmountComponentParams.AmountParams ?: return
|
||||
combine(
|
||||
navigationJob?.cancel()
|
||||
navigationJob = combine(
|
||||
flow = uiState,
|
||||
flow2 = params.currentRoute,
|
||||
transform = { state, route -> state to route },
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import com.tangem.common.ui.amountScreen.converters.AmountAccountConverter
|
|||
import com.tangem.common.ui.amountScreen.converters.AmountStateConverter
|
||||
import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter
|
||||
import com.tangem.common.ui.amountScreen.models.AmountParameters
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.account.Account
|
||||
|
|
@ -39,6 +39,7 @@ internal class SwapAmountFieldConverter(
|
|||
isSelected: Boolean,
|
||||
isAmountEmpty: Boolean = true,
|
||||
displayAmount: BigDecimal? = null,
|
||||
showApproximatePrefix: Boolean = false,
|
||||
): SwapAmountFieldUM {
|
||||
val walletTitle = if (isSingleWallet) {
|
||||
resourceReference(R.string.send_from_title)
|
||||
|
|
@ -60,6 +61,7 @@ internal class SwapAmountFieldConverter(
|
|||
subtitleRight = subtitles.subtitleRight,
|
||||
subtitleEllipsisRight = subtitles.subtitleEllipsisRight,
|
||||
isClickEnabled = true,
|
||||
shouldShowApproximatePrefix = showApproximatePrefix,
|
||||
amountField = AmountStateConverter(
|
||||
clickIntents = clickIntents,
|
||||
appCurrency = appCurrency,
|
||||
|
|
@ -77,7 +79,7 @@ internal class SwapAmountFieldConverter(
|
|||
walletTitle = walletTitle,
|
||||
prefixText = when {
|
||||
swapAmountType.isEnteringField() -> resourceReference(R.string.common_from)
|
||||
else -> TextReference.Companion.EMPTY
|
||||
else -> TextReference.EMPTY
|
||||
},
|
||||
).convert(account)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ internal class SwapAmountChangeAmountTypeTransformer(
|
|||
field = field,
|
||||
cryptoCurrencyStatus = prevState.secondaryCryptoCurrencyStatus,
|
||||
isAmountEmpty = true,
|
||||
).copy(
|
||||
shouldShowApproximatePrefix = swapRateType == ExpressRateType.Float,
|
||||
)
|
||||
} ?: prevState.secondaryAmount
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ internal class SwapAmountSecondaryReadyStateTransformer(
|
|||
swapAmountType = SwapAmountType.To,
|
||||
cryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
|
||||
isSelected = prevState.selectedAmountType == SwapAmountType.To,
|
||||
showApproximatePrefix = selectedRateType == ExpressRateType.Float,
|
||||
),
|
||||
secondaryCryptoCurrencyStatus = secondaryCryptoCurrencyStatus,
|
||||
swapCurrencies = swapCurrencies,
|
||||
|
|
|
|||
|
|
@ -64,7 +64,9 @@ internal class SwapAmountSelectQuoteTransformer(
|
|||
val hasInsufficientFundsForFixed = hasInsufficientFundsForFixed(prevState, quoteContent)
|
||||
|
||||
return prevState.copy(
|
||||
isPrimaryButtonEnabled = quoteUM is SwapQuoteUM.Content && !hasInsufficientFundsForFixed,
|
||||
isPrimaryButtonEnabled = quoteUM is SwapQuoteUM.Content &&
|
||||
!hasInsufficientBalance(prevState, quoteContent, newPrimaryAmount) &&
|
||||
!hasInsufficientFundsForFixed,
|
||||
selectedQuote = quoteUM,
|
||||
isShowFCAWarning = isNeedApplyFCARestrictions && quoteUM.provider?.isRestrictedByFCA() == true,
|
||||
primaryAmount = newPrimaryAmount,
|
||||
|
|
@ -214,4 +216,22 @@ internal class SwapAmountSelectQuoteTransformer(
|
|||
prevState.secondaryAmount
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasInsufficientBalance(
|
||||
prevState: SwapAmountUM.Content,
|
||||
quoteContent: SwapQuoteUM.Content?,
|
||||
newPrimaryAmount: SwapAmountFieldUM,
|
||||
): Boolean {
|
||||
if (quoteContent == null) return false
|
||||
|
||||
val primaryBalance = prevState.primaryCryptoCurrencyStatus.value.amount ?: return false
|
||||
val fromAmount = if (prevState.selectedAmountType == SwapAmountType.To) {
|
||||
quoteContent.fromAmount
|
||||
} else {
|
||||
val amountData = (newPrimaryAmount as? SwapAmountFieldUM.Content)?.amountField as? AmountState.Data
|
||||
amountData?.amountTextField?.cryptoAmount?.value
|
||||
}
|
||||
|
||||
return fromAmount != null && fromAmount > primaryBalance
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,7 @@ import com.tangem.core.ui.res.TangemThemePreview
|
|||
import com.tangem.domain.swap.models.SwapAmountType
|
||||
import com.tangem.features.swap.v2.impl.R
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.PriceImpact
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
|
||||
import com.tangem.features.swap.v2.impl.amount.ui.preview.SwapAmountContentPreview
|
||||
import com.tangem.features.swap.v2.impl.chooseprovider.ui.SwapChooseProviderContent
|
||||
|
|
@ -120,11 +121,13 @@ private fun ConstraintLayoutScope.SwapAmountBlock(
|
|||
end.linkTo(parent.end)
|
||||
},
|
||||
)
|
||||
val secondaryContent = amountUM.secondaryAmount as? SwapAmountFieldUM.Content
|
||||
AmountBlockV2(
|
||||
amountState = (amountUM.secondaryAmount.amountField as? AmountState.Data)?.copy(
|
||||
accountTitleUM = AccountTitleUM.Text(resourceReference(R.string.send_with_swap_recipient_amount_title)),
|
||||
availableBalanceCrypto = TextReference.EMPTY,
|
||||
) ?: amountUM.secondaryAmount.amountField,
|
||||
shouldShowApproximatePrefix = secondaryContent?.shouldShowApproximatePrefix == true,
|
||||
isClickDisabled = true,
|
||||
isEditingDisabled = false,
|
||||
modifier = Modifier.constrainAs(toAmountRef) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ internal data object SwapAmountContentPreview {
|
|||
value = "bitcoin",
|
||||
derivationPath = Network.DerivationPath.None,
|
||||
),
|
||||
backendId = "bitcoin",
|
||||
name = "Bitcoin",
|
||||
currencySymbol = "BTC",
|
||||
derivationPath = Network.DerivationPath.None,
|
||||
|
|
@ -109,6 +108,7 @@ internal data object SwapAmountContentPreview {
|
|||
isClickEnabled = false,
|
||||
subtitleEllipsisLeft = TextEllipsis.OffsetEnd(3),
|
||||
subtitleEllipsisRight = TextEllipsis.OffsetEnd(1),
|
||||
shouldShowApproximatePrefix = false,
|
||||
),
|
||||
secondaryAmount = SwapAmountFieldUM.Content(
|
||||
amountType = SwapAmountType.To,
|
||||
|
|
@ -121,6 +121,7 @@ internal data object SwapAmountContentPreview {
|
|||
isClickEnabled = false,
|
||||
subtitleEllipsisLeft = TextEllipsis.End,
|
||||
subtitleEllipsisRight = TextEllipsis.End,
|
||||
shouldShowApproximatePrefix = true,
|
||||
),
|
||||
appCurrency = AppCurrency.Default,
|
||||
swapDirection = SwapDirection.Direct,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.swap.v2.impl.choosetoken.fromSupported.model.transformers
|
||||
|
||||
import com.tangem.core.ui.extensions.iconResId
|
||||
import com.tangem.common.ui.extensions.iconResId
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.express.models.ExpressRateType
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
|
|
@ -29,7 +29,7 @@ internal class SwapChooseContentStateTransformer(
|
|||
|
||||
val isMain = cryptoCurrency is CryptoCurrency.Coin
|
||||
val subtitle = when {
|
||||
BlockchainUtils.isL2Network(networkId = network.backendId) -> MAIN_NETWORK_L2_TYPE_NAME
|
||||
BlockchainUtils.isL2Network(networkId = network.rawId) -> MAIN_NETWORK_L2_TYPE_NAME
|
||||
isMain -> MAIN_NETWORK_TYPE_NAME
|
||||
network.standardType !is Network.StandardType.Unspecified -> network.standardType.name
|
||||
else -> ""
|
||||
|
|
|
|||
|
|
@ -75,8 +75,7 @@ internal class SwapAlertFactory @Inject constructor(
|
|||
saveBlockchainErrorUseCase(
|
||||
error = BlockchainErrorInfo(
|
||||
errorMessage = errorMessage.orEmpty(),
|
||||
blockchainId = cryptoCurrency?.network?.rawId.orEmpty(),
|
||||
derivationPath = cryptoCurrency?.network?.derivationPath?.value.orEmpty(),
|
||||
networkId = cryptoCurrency?.network?.id,
|
||||
destinationAddress = confirmData?.enteredDestination.orEmpty(),
|
||||
tokenSymbol = confirmData?.toCryptoCurrencyStatus?.currency?.symbol.orEmpty(),
|
||||
amount = confirmData?.enteredFromAmount?.toString().orEmpty(),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyByAddressUseCase
|
||||
|
|
@ -100,7 +99,6 @@ internal class SendWithSwapConfirmModel @Inject constructor(
|
|||
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger,
|
||||
private val swapAlertFactory: SwapAlertFactory,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
|
||||
swapTransactionSenderFactory: SwapTransactionSender.Factory,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model(), FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback {
|
||||
|
|
@ -574,8 +572,7 @@ internal class SendWithSwapConfirmModel @Inject constructor(
|
|||
val confirmUM = state.confirmUM
|
||||
val isContent = confirmUM is ConfirmUM.Content
|
||||
val isReadyToSend = isContent && !confirmUM.isTransactionInProcess
|
||||
val isHoldToConfirm = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
|
||||
params.userWallet.isHotWallet && isContent
|
||||
val isHoldToConfirm = params.userWallet.isHotWallet && isContent
|
||||
params.callback.onResult(
|
||||
route = SendWithSwapRoute.Confirm,
|
||||
sendWithSwapUM = state.copy(
|
||||
|
|
|
|||
|
|
@ -26,12 +26,11 @@ import com.tangem.domain.utils.convertToSdkAmount
|
|||
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils
|
||||
import com.tangem.features.swap.v2.impl.common.ConfirmData
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class SwapTransactionSender @AssistedInject constructor(
|
||||
|
|
@ -91,7 +90,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
|
|||
val feeValue = confirmData.fee?.amount?.value ?: return
|
||||
val destination = confirmData.enteredDestination ?: return
|
||||
|
||||
val (swapDataRequestAmount, swapDataRequestCurrency) = when (confirmData.amountType) {
|
||||
val swapDataRequestAmount = when (confirmData.amountType) {
|
||||
SwapAmountType.From -> {
|
||||
val amountValue = confirmData.enteredFromAmount ?: return
|
||||
val subtracted = FeeCalculationUtils.checkAndCalculateSubtractedAmount(
|
||||
|
|
@ -101,11 +100,11 @@ internal class SwapTransactionSender @AssistedInject constructor(
|
|||
feeValue = feeValue,
|
||||
reduceAmountBy = confirmData.reduceAmountBy,
|
||||
)
|
||||
subtracted to fromStatus
|
||||
subtracted
|
||||
}
|
||||
SwapAmountType.To -> {
|
||||
val amountValue = confirmData.enteredToAmount ?: return
|
||||
amountValue to toStatus
|
||||
amountValue
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +116,7 @@ internal class SwapTransactionSender @AssistedInject constructor(
|
|||
val swapData = getSwapDataUseCase(
|
||||
userWallet = userWallet,
|
||||
fromCryptoCurrencyStatus = fromStatus,
|
||||
amount = swapDataRequestAmount.toStringWithRightOffset(swapDataRequestCurrency.currency.decimals),
|
||||
amount = swapDataRequestAmount,
|
||||
amountType = confirmData.amountType,
|
||||
toCryptoCurrency = toStatus.currency,
|
||||
toAddress = destination,
|
||||
|
|
@ -208,7 +207,8 @@ internal class SwapTransactionSender @AssistedInject constructor(
|
|||
ifRight = { txHash ->
|
||||
val timestamp = System.currentTimeMillis()
|
||||
swapTransactionSentUseCase.invoke(
|
||||
userWallet = userWallet,
|
||||
fromUserWallet = userWallet,
|
||||
toUserWallet = userWallet,
|
||||
fromCryptoCurrencyStatus = fromStatus,
|
||||
toCryptoCurrencyStatus = toStatus,
|
||||
fromAccount = fromAccount,
|
||||
|
|
@ -225,10 +225,6 @@ internal class SwapTransactionSender @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun BigDecimal.toStringWithRightOffset(decimals: Int): String {
|
||||
return setScale(decimals, RoundingMode.HALF_DOWN).movePointRight(decimals).toPlainString()
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(userWallet: UserWallet): SwapTransactionSender
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue