diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldContainer.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldContainer.kt index fe714763b9..a1ba4366f4 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldContainer.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountFieldContainer.kt @@ -165,12 +165,12 @@ private fun AmountInfo(amountUM: AmountState, onMaxAmountClick: () -> Unit, modi .padding(end = 16.dp) .clip(RoundedCornerShape(16.dp)) .background(TangemTheme.colors.background.secondary) - .padding(horizontal = 12.dp, vertical = 4.dp) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = ripple(), onClick = onMaxAmountClick, - ), + ) + .padding(horizontal = 12.dp, vertical = 4.dp), ) } } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt index 2ba34fa932..26267a4710 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/entity/SwapAmountUM.kt @@ -19,8 +19,11 @@ internal sealed class SwapAmountUM { abstract val primaryAmount: SwapAmountFieldUM abstract val secondaryAmount: SwapAmountFieldUM abstract val selectedAmountType: SwapAmountType + abstract val swapDirection: SwapDirection - data object Empty : SwapAmountUM() { + data class Empty( + override val swapDirection: SwapDirection, + ) : SwapAmountUM() { override val isPrimaryButtonEnabled = false override val selectedAmountType = SwapAmountType.From override val primaryAmount = SwapAmountFieldUM.Empty(SwapAmountType.From) @@ -29,16 +32,16 @@ internal sealed class SwapAmountUM { data class Content( override val isPrimaryButtonEnabled: Boolean, + override val swapDirection: SwapDirection, + override val selectedAmountType: SwapAmountType, // two amount fields override val primaryAmount: SwapAmountFieldUM, override val secondaryAmount: SwapAmountFieldUM, - override val selectedAmountType: SwapAmountType, val primaryCryptoCurrencyStatus: CryptoCurrencyStatus, - val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus, + val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus?, // selected swap route - val swapDirection: SwapDirection, val swapRateType: ExpressRateType, // swap models diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index 4debca50a0..f722d3e1ea 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -13,13 +13,9 @@ 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.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringReference -import com.tangem.core.ui.format.bigdecimal.crypto -import com.tangem.core.ui.format.bigdecimal.format import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.express.models.ExpressError -import com.tangem.domain.express.models.ExpressProvider import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.swap.models.SwapDirection import com.tangem.domain.swap.models.SwapQuoteModel @@ -38,17 +34,17 @@ import com.tangem.features.swap.v2.impl.amount.SwapAmountUpdateListener import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM -import com.tangem.features.swap.v2.impl.amount.model.converter.SwapAmountReadyStateConverter +import com.tangem.features.swap.v2.impl.amount.model.converter.SwapQuoteUMConverter import com.tangem.features.swap.v2.impl.amount.model.transformers.* import com.tangem.features.swap.v2.impl.chooseprovider.SwapChooseProviderComponent import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM -import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM.Content.DifferencePercent import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.extensions.orZero import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch +import java.math.BigDecimal import javax.inject.Inject import kotlin.properties.Delegates import com.tangem.utils.transformer.update as transformerUpdate @@ -77,18 +73,11 @@ internal class SwapAmountModel @Inject constructor( private var userWallet = params.userWallet private var primaryCryptoCurrency: CryptoCurrency = params.primaryCryptoCurrencyStatusFlow.value.currency - private var secondaryCryptoCurrency: CryptoCurrency? = params.secondaryCryptoCurrency - - private var primaryCryptoCurrencyStatus: CryptoCurrencyStatus = params.primaryCryptoCurrencyStatusFlow.value - private var secondaryCryptoCurrencyStatus: CryptoCurrencyStatus = CryptoCurrencyStatus( - currency = primaryCryptoCurrencyStatus.currency, - value = CryptoCurrencyStatus.Loading, - ) private var primaryMaximumAmountBoundary: EnterAmountBoundary by Delegates.notNull() - private var secondaryMaximumAmountBoundary: EnterAmountBoundary by Delegates.notNull() private var primaryMinimumAmountBoundary: EnterAmountBoundary by Delegates.notNull() - private var secondaryMinimumAmountBoundary: EnterAmountBoundary by Delegates.notNull() + private var secondaryMaximumAmountBoundary: EnterAmountBoundary? = null + private var secondaryMinimumAmountBoundary: EnterAmountBoundary? = null val bottomSheetNavigation: SlotNavigation = SlotNavigation() @@ -103,7 +92,6 @@ internal class SwapAmountModel @Inject constructor( subscribeOnCryptoCurrencyStatusFlow() subscribeOnAmountUpdateTriggerUpdates() observeChooseSelectToken() - // todo observe balance hiding flow } fun updateState(amountUM: SwapAmountUM) { @@ -132,11 +120,10 @@ internal class SwapAmountModel @Inject constructor( override fun onInfoClick() { val amountUM = uiState.value as? SwapAmountUM.Content ?: return val selectedProvider = amountUM.selectedQuote.provider ?: return - val cryptoCurrency = secondaryCryptoCurrency ?: return swapAlertFactory.priceImpactAlert( hasPriceImpact = (amountUM.secondaryAmount as? SwapAmountFieldUM.Content)?.priceImpact != null, - currencySymbol = cryptoCurrency.symbol, + currencySymbol = amountUM.primaryCryptoCurrencyStatus.currency.symbol, provider = selectedProvider, ) } @@ -193,32 +180,58 @@ internal class SwapAmountModel @Inject constructor( } override fun onSeparatorClick() { - // todo send with swap make swap types + // todo handle reverse pair click with swap redesign val amountFieldData = uiState.value.primaryAmount.amountField as? AmountState.Data val callback = (params as? SwapAmountComponentParams.AmountParams)?.callback ?: return + val primaryCryptoCurrencyStatus = (uiState.value as? SwapAmountUM.Content)?.primaryCryptoCurrencyStatus + if (primaryCryptoCurrencyStatus != null) { + uiState.transformerUpdate( + SwapAmountPrimaryReadyStateTransformer( + userWallet = userWallet, + primaryCryptoCurrencyStatus = primaryCryptoCurrencyStatus, + appCurrency = appCurrency, + swapDirection = swapDirection, + clickIntents = this, + isBalanceHidden = params.isBalanceHidingFlow.value, + ), + ) + } + callback.onSeparatorClick(lastAmount = amountFieldData?.amountTextField?.value.orEmpty()) + callback.onAmountResult(uiState.value) } private fun subscribeOnCryptoCurrencyStatusFlow() { - params.primaryCryptoCurrencyStatusFlow.onEach { primaryCurrencyStatus -> - primaryCryptoCurrencyStatus = primaryCurrencyStatus - - val state = uiState.value - if (state is SwapAmountUM.Content) { - secondaryCryptoCurrency = state.primaryCryptoCurrencyStatus.currency - secondaryCryptoCurrencyStatus = state.primaryCryptoCurrencyStatus + params.primaryCryptoCurrencyStatusFlow + .distinctUntilChanged { old, new -> old.value.amount == new.value.amount } // Check only balance changes + .onEach { primaryCurrencyStatus -> + val secondaryStatus = (uiState.value as? SwapAmountUM.Content)?.secondaryCryptoCurrencyStatus initCurrencies( - primaryStatus = state.primaryCryptoCurrencyStatus, - secondaryStatus = state.secondaryCryptoCurrencyStatus, + primaryStatus = primaryCurrencyStatus, + secondaryStatus = secondaryStatus, ) - } else { - initPairs( - primaryCryptoCurrency = primaryCryptoCurrency, - secondaryCryptoCurrency = secondaryCryptoCurrency, - ) - } - }.launchIn(modelScope) + if (secondaryStatus != null) { + uiState.transformerUpdate( + SwapAmountUpdateBalanceTransformer( + cryptoCurrencyStatus = primaryCurrencyStatus, + primaryMaximumAmountBoundary = primaryMaximumAmountBoundary, + primaryMinimumAmountBoundary = primaryMinimumAmountBoundary, + ), + ) + } else { + uiState.transformerUpdate( + SwapAmountPrimaryReadyStateTransformer( + userWallet = userWallet, + primaryCryptoCurrencyStatus = primaryCurrencyStatus, + appCurrency = appCurrency, + swapDirection = swapDirection, + clickIntents = this, + isBalanceHidden = params.isBalanceHidingFlow.value, + ), + ) + } + }.launchIn(modelScope) } private fun subscribeOnAmountUpdateTriggerUpdates() { @@ -226,6 +239,7 @@ internal class SwapAmountModel @Inject constructor( .onEach { if (uiState.value is SwapAmountUM.Content) { onAmountValueChange(it) + saveResult() } } .launchIn(modelScope) @@ -246,12 +260,12 @@ internal class SwapAmountModel @Inject constructor( amountUM } } - initPairs(primaryCryptoCurrency, currency) + initPairs(currency) } .launchIn(modelScope) } - private fun initPairs(primaryCryptoCurrency: CryptoCurrency, secondaryCryptoCurrency: CryptoCurrency?) { + private fun initPairs(secondaryCryptoCurrency: CryptoCurrency?) { modelScope.launch { val cryptoCurrencyStatusList = getMultiCryptoCurrencyStatusUseCase .invokeMultiWalletSync(userWallet.walletId) @@ -277,22 +291,22 @@ internal class SwapAmountModel @Inject constructor( swapDirection = params.swapDirection, ) - if (secondaryStatus != null) { - initCurrencies(primaryCryptoCurrencyStatus, secondaryStatus) - this@SwapAmountModel.secondaryCryptoCurrency = secondaryCryptoCurrency - secondaryCryptoCurrencyStatus = secondaryStatus - uiState.update { - SwapAmountReadyStateConverter( - swapCurrencies = swapCurrencies, + val primaryStatus = (uiState.value as? SwapAmountUM.Content)?.primaryCryptoCurrencyStatus + if (secondaryStatus != null && primaryStatus != null) { + initCurrencies(primaryStatus, secondaryStatus) + uiState.transformerUpdate( + SwapAmountSecondaryReadyStateTransformer( userWallet = userWallet, - primaryCryptoCurrencyStatus = primaryCryptoCurrencyStatus, - secondaryCryptoCurrencyStatus = secondaryCryptoCurrencyStatus, + swapCurrencies = swapCurrencies, + primaryCryptoCurrencyStatus = primaryStatus, + secondaryCryptoCurrencyStatus = secondaryStatus, appCurrency = appCurrency, swapDirection = swapDirection, clickIntents = this@SwapAmountModel, isBalanceHidden = params.isBalanceHidingFlow.value, - ).convert(Unit) - } + ), + ) + loadQuotes() } else { // todo not available currency to swap } @@ -304,7 +318,7 @@ internal class SwapAmountModel @Inject constructor( } } - private suspend fun initCurrencies(primaryStatus: CryptoCurrencyStatus, secondaryStatus: CryptoCurrencyStatus) { + private suspend fun initCurrencies(primaryStatus: CryptoCurrencyStatus, secondaryStatus: CryptoCurrencyStatus?) { primaryMinimumAmountBoundary = EnterAmountBoundary( amount = getMinimumTransactionAmountSyncUseCase .invoke( @@ -314,21 +328,25 @@ internal class SwapAmountModel @Inject constructor( fiatRate = primaryStatus.value.fiatRate, fiatAmount = primaryStatus.value.fiatAmount, ) - secondaryMinimumAmountBoundary = EnterAmountBoundary( - amount = getMinimumTransactionAmountSyncUseCase - .invoke( - userWalletId = userWallet.walletId, - cryptoCurrencyStatus = secondaryStatus, - ).getOrNull().orZero(), - fiatRate = secondaryStatus.value.fiatRate, - fiatAmount = secondaryStatus.value.fiatAmount, - ) primaryMaximumAmountBoundary = MaxEnterAmountConverter().convert(primaryStatus) - secondaryMaximumAmountBoundary = MaxEnterAmountConverter().convert(secondaryStatus) + + if (secondaryStatus != null) { + secondaryMinimumAmountBoundary = EnterAmountBoundary( + amount = getMinimumTransactionAmountSyncUseCase + .invoke( + userWalletId = userWallet.walletId, + cryptoCurrencyStatus = secondaryStatus, + ).getOrNull().orZero(), + fiatRate = secondaryStatus.value.fiatRate, + fiatAmount = secondaryStatus.value.fiatAmount, + ) + secondaryMaximumAmountBoundary = MaxEnterAmountConverter().convert(secondaryStatus) + } } private fun loadQuotes() { val state = uiState.value as? SwapAmountUM.Content ?: return + if (state.secondaryCryptoCurrencyStatus == null) return val (fromCryptoCurrency, toCryptoCurrency) = when (state.swapDirection) { SwapDirection.Direct -> { @@ -376,11 +394,17 @@ internal class SwapAmountModel @Inject constructor( ).takeIf { error is ExpressError.AmountError } }, ifRight = { quote: SwapQuoteModel -> - convertToSwapProviderUM( - quote = quote, - provider = provider, - differencePercent = DifferencePercent.Empty, + SwapQuoteUMConverter( + primaryCurrency = fromCryptoCurrency, + secondaryCurrency = toCryptoCurrency, swapDirection = swapDirection, + allowanceContract = quote.allowanceContract, + isApprovalNeeded = checkAllowance(state, quote), + ).convert( + SwapQuoteUMConverter.Data( + quote = quote, + provider = provider, + ), ) }, ) @@ -397,61 +421,19 @@ internal class SwapAmountModel @Inject constructor( } } - private suspend fun convertToSwapProviderUM( - quote: SwapQuoteModel, - provider: ExpressProvider, - differencePercent: DifferencePercent, - swapDirection: SwapDirection, - ): SwapQuoteUM { - // todo swap allowance + private suspend fun checkAllowance(state: SwapAmountUM.Content, quote: SwapQuoteModel): Boolean { val allowanceContract = quote.allowanceContract - return if (allowanceContract != null) { - val allowance = getAllowanceUseCase( + val allowance = if (allowanceContract != null) { + getAllowanceUseCase( userWalletId = userWallet.walletId, - cryptoCurrency = primaryCryptoCurrencyStatus.currency, + cryptoCurrency = state.primaryCryptoCurrencyStatus.currency, spenderAddress = allowanceContract, - ).getOrNull().orZero() - val isApprovalNeeded = allowance < primaryCryptoCurrencyStatus.value.amount.orZero() - - if (isApprovalNeeded) { - SwapQuoteUM.Allowance( - provider = provider, - allowanceContract = allowanceContract, - ) - } else { - SwapQuoteUM.Content( - provider = provider, - quoteAmount = quote.toTokenAmount, - diffPercent = differencePercent, - quoteAmountValue = stringReference( - quote.toTokenAmount.format { - crypto( - when (swapDirection) { - SwapDirection.Direct -> secondaryCryptoCurrencyStatus.currency - SwapDirection.Reverse -> primaryCryptoCurrencyStatus.currency - }, - ) - }, - ), - ) - } + ).getOrNull() } else { - SwapQuoteUM.Content( - provider = provider, - quoteAmount = quote.toTokenAmount, - diffPercent = differencePercent, - quoteAmountValue = stringReference( - quote.toTokenAmount.format { - crypto( - when (swapDirection) { - SwapDirection.Direct -> secondaryCryptoCurrencyStatus.currency - SwapDirection.Reverse -> primaryCryptoCurrencyStatus.currency - }, - ) - }, - ), - ) + BigDecimal.ZERO } + + return allowance.orZero() < state.primaryCryptoCurrencyStatus.value.amount.orZero() } private fun saveResult() { @@ -475,20 +457,7 @@ internal class SwapAmountModel @Inject constructor( } else { R.drawable.ic_close_24 }, - backIconClick = { - // if (!route.isEditMode) { - // todo analytics - // analyticsEventHandler.send( - // CommonSendAnalyticEvents.CloseButtonClicked( - // categoryName = params.analyticsCategoryName, - // source = SendScreenSource.Address, - // isFromSummary = false, - // isValid = state.isPrimaryButtonEnabled, - // ), - // ) - // } - params.callback.onBackClick() - }, + backIconClick = params.callback::onBackClick, primaryButton = NavigationButton( textReference = if (route.isEditMode) { resourceReference(R.string.common_continue) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountQuoteUtils.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountQuoteUtils.kt index bef7ed2108..b2b853bf9d 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountQuoteUtils.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountQuoteUtils.kt @@ -42,8 +42,8 @@ internal object SwapAmountQuoteUtils { } fun SwapAmountUM.updateAmount( - onPrimaryAmount: SwapAmountFieldUM.Content.() -> SwapAmountFieldUM, - onSecondaryAmount: SwapAmountFieldUM.Content.() -> SwapAmountFieldUM, + onPrimaryAmount: SwapAmountFieldUM.Content.(CryptoCurrencyStatus) -> SwapAmountFieldUM, + onSecondaryAmount: SwapAmountFieldUM.Content.(CryptoCurrencyStatus) -> SwapAmountFieldUM, ): SwapAmountUM { if (this !is SwapAmountUM.Content) return this @@ -51,10 +51,11 @@ internal object SwapAmountQuoteUtils { selectedAmountType == SwapAmountType.From && swapDirection == SwapDirection.Direct ) { val amountFieldUM = primaryAmount as? SwapAmountFieldUM.Content ?: return this - copy(primaryAmount = amountFieldUM.onPrimaryAmount()) + copy(primaryAmount = amountFieldUM.onPrimaryAmount(primaryCryptoCurrencyStatus)) } else { + if (secondaryCryptoCurrencyStatus == null) return this val amountFieldUM = secondaryAmount as? SwapAmountFieldUM.Content ?: return this - copy(secondaryAmount = amountFieldUM.onSecondaryAmount()) + copy(secondaryAmount = amountFieldUM.onSecondaryAmount(secondaryCryptoCurrencyStatus)) } } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapQuoteUMConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapQuoteUMConverter.kt new file mode 100644 index 0000000000..8abbd44af1 --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapQuoteUMConverter.kt @@ -0,0 +1,66 @@ +package com.tangem.features.swap.v2.impl.amount.model.converter + +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.format.bigdecimal.crypto +import com.tangem.core.ui.format.bigdecimal.format +import com.tangem.domain.express.models.ExpressProvider +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.swap.models.SwapDirection +import com.tangem.domain.swap.models.SwapQuoteModel +import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM +import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM.Content.DifferencePercent +import com.tangem.utils.converter.Converter +import java.math.BigDecimal + +internal class SwapQuoteUMConverter( + private val swapDirection: SwapDirection, + private val allowanceContract: String?, + private val isApprovalNeeded: Boolean, + private val primaryCurrency: CryptoCurrency, + private val secondaryCurrency: CryptoCurrency, +) : Converter { + override fun convert(value: Data): SwapQuoteUM { + val (quote, provider) = value + + return if (allowanceContract != null) { + if (isApprovalNeeded) { + SwapQuoteUM.Allowance( + provider = provider, + allowanceContract = allowanceContract, + ) + } else { + SwapQuoteUM.Content( + provider = provider, + quoteAmount = quote.toTokenAmount, + diffPercent = DifferencePercent.Empty, + quoteAmountValue = stringReference( + quote.toTokenAmount.toQuoteValue(), + ), + ) + } + } else { + SwapQuoteUM.Content( + provider = provider, + quoteAmount = quote.toTokenAmount, + diffPercent = DifferencePercent.Empty, + quoteAmountValue = stringReference( + quote.toTokenAmount.toQuoteValue(), + ), + ) + } + } + + private fun BigDecimal.toQuoteValue() = format { + crypto( + when (swapDirection) { + SwapDirection.Direct -> secondaryCurrency + SwapDirection.Reverse -> primaryCurrency + }, + ) + } + + data class Data( + val quote: SwapQuoteModel, + val provider: ExpressProvider, + ) +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountChangeCurrencyTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountChangeCurrencyTransformer.kt index e1a149555d..21938524aa 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountChangeCurrencyTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountChangeCurrencyTransformer.kt @@ -11,18 +11,18 @@ internal class SwapAmountChangeCurrencyTransformer( override fun transform(prevState: SwapAmountUM): SwapAmountUM { if (prevState !is SwapAmountUM.Content) return prevState return prevState.updateAmount( - onPrimaryAmount = { + onPrimaryAmount = { primaryStatus -> copy( amountField = AmountCurrencyTransformer( - cryptoCurrencyStatus = prevState.primaryCryptoCurrencyStatus, + cryptoCurrencyStatus = primaryStatus, value = isFiatSelected, ).transform(prevState.primaryAmount.amountField), ) }, - onSecondaryAmount = { + onSecondaryAmount = { secondaryStatus -> copy( amountField = AmountCurrencyTransformer( - cryptoCurrencyStatus = prevState.secondaryCryptoCurrencyStatus, + cryptoCurrencyStatus = secondaryStatus, value = isFiatSelected, ).transform(prevState.secondaryAmount.amountField), ) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt new file mode 100644 index 0000000000..78ba1cd720 --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountPrimaryReadyStateTransformer.kt @@ -0,0 +1,57 @@ +package com.tangem.features.swap.v2.impl.amount.model.transformers + +import com.tangem.common.ui.amountScreen.AmountScreenClickIntents +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.express.models.ExpressRateType +import com.tangem.domain.swap.models.SwapCurrencies +import com.tangem.domain.swap.models.SwapDirection +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.wallets.models.UserWallet +import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM +import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType +import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM +import com.tangem.features.swap.v2.impl.amount.model.converter.SwapAmountFieldConverter +import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM +import com.tangem.utils.transformer.Transformer +import kotlinx.collections.immutable.persistentListOf + +@Suppress("LongParameterList") +internal class SwapAmountPrimaryReadyStateTransformer( + private val userWallet: UserWallet, + private val primaryCryptoCurrencyStatus: CryptoCurrencyStatus, + private val appCurrency: AppCurrency, + private val clickIntents: AmountScreenClickIntents, + private val swapDirection: SwapDirection, + private val isBalanceHidden: Boolean, +) : Transformer { + + private val amountFieldConverter = SwapAmountFieldConverter( + swapDirection = swapDirection, + isBalanceHidden = isBalanceHidden, + userWallet = userWallet, + appCurrency = appCurrency, + clickIntents = clickIntents, + ) + + override fun transform(prevState: SwapAmountUM): SwapAmountUM { + return SwapAmountUM.Content( + isPrimaryButtonEnabled = false, + primaryCryptoCurrencyStatus = primaryCryptoCurrencyStatus, + primaryAmount = amountFieldConverter.convert( + selectedType = SwapAmountType.From, + cryptoCurrencyStatus = primaryCryptoCurrencyStatus, + ), + secondaryCryptoCurrencyStatus = null, + secondaryAmount = SwapAmountFieldUM.Empty( + SwapAmountType.To, + ), + swapDirection = swapDirection, + swapCurrencies = SwapCurrencies.EMPTY, + selectedAmountType = prevState.selectedAmountType, + swapRateType = ExpressRateType.Float, + swapQuotes = persistentListOf(), + selectedQuote = SwapQuoteUM.Empty, + appCurrency = appCurrency, + ) + } +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountReadyStateConverter.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt similarity index 80% rename from features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountReadyStateConverter.kt rename to features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt index 7de7bab116..8b4c3989ba 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/converter/SwapAmountReadyStateConverter.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSecondaryReadyStateTransformer.kt @@ -1,4 +1,4 @@ -package com.tangem.features.swap.v2.impl.amount.model.converter +package com.tangem.features.swap.v2.impl.amount.model.transformers import com.tangem.common.ui.amountScreen.AmountScreenClickIntents import com.tangem.domain.appcurrency.model.AppCurrency @@ -9,21 +9,22 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM +import com.tangem.features.swap.v2.impl.amount.model.converter.SwapAmountFieldConverter import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM -import com.tangem.utils.converter.Converter +import com.tangem.utils.transformer.Transformer import kotlinx.collections.immutable.persistentListOf @Suppress("LongParameterList") -internal class SwapAmountReadyStateConverter( +internal class SwapAmountSecondaryReadyStateTransformer( private val userWallet: UserWallet, - private val swapCurrencies: SwapCurrencies, private val primaryCryptoCurrencyStatus: CryptoCurrencyStatus, private val secondaryCryptoCurrencyStatus: CryptoCurrencyStatus, private val appCurrency: AppCurrency, + private val swapCurrencies: SwapCurrencies, private val clickIntents: AmountScreenClickIntents, private val swapDirection: SwapDirection, private val isBalanceHidden: Boolean, -) : Converter { +) : Transformer { private val amountFieldConverter = SwapAmountFieldConverter( swapDirection = swapDirection, @@ -33,26 +34,23 @@ internal class SwapAmountReadyStateConverter( clickIntents = clickIntents, ) - override fun convert(value: Unit): SwapAmountUM { + override fun transform(prevState: SwapAmountUM): SwapAmountUM { return SwapAmountUM.Content( + isPrimaryButtonEnabled = false, + primaryAmount = prevState.primaryAmount, primaryCryptoCurrencyStatus = primaryCryptoCurrencyStatus, - secondaryCryptoCurrencyStatus = secondaryCryptoCurrencyStatus, - primaryAmount = amountFieldConverter.convert( - selectedType = SwapAmountType.From, - cryptoCurrencyStatus = primaryCryptoCurrencyStatus, - ), secondaryAmount = amountFieldConverter.convert( selectedType = SwapAmountType.To, cryptoCurrencyStatus = secondaryCryptoCurrencyStatus, ), + secondaryCryptoCurrencyStatus = secondaryCryptoCurrencyStatus, swapCurrencies = swapCurrencies, + selectedAmountType = prevState.selectedAmountType, swapDirection = swapDirection, + swapRateType = ExpressRateType.Float, swapQuotes = persistentListOf(), selectedQuote = SwapQuoteUM.Empty, - selectedAmountType = SwapAmountType.From, appCurrency = appCurrency, - swapRateType = ExpressRateType.Float, - isPrimaryButtonEnabled = false, ) } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSelectQuoteTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSelectQuoteTransformer.kt index b11d0238e1..7213bcb3c5 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSelectQuoteTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSelectQuoteTransformer.kt @@ -16,8 +16,8 @@ import com.tangem.utils.transformer.Transformer internal class SwapAmountSelectQuoteTransformer( private val quoteUM: SwapQuoteUM, - private val secondaryMaximumAmountBoundary: EnterAmountBoundary, - private val secondaryMinimumAmountBoundary: EnterAmountBoundary, + private val secondaryMaximumAmountBoundary: EnterAmountBoundary?, + private val secondaryMinimumAmountBoundary: EnterAmountBoundary?, ) : Transformer { override fun transform(prevState: SwapAmountUM): SwapAmountUM { if (prevState !is SwapAmountUM.Content) return prevState @@ -46,7 +46,9 @@ internal class SwapAmountSelectQuoteTransformer( } else { prevState.primaryAmount }, - secondaryAmount = if (prevState.selectedAmountType == SwapAmountType.From) { + secondaryAmount = if (prevState.selectedAmountType == SwapAmountType.From && + prevState.secondaryCryptoCurrencyStatus != null && secondaryMaximumAmountBoundary != null + ) { val secondaryAmountField = prevState.secondaryAmount as? SwapAmountFieldUM.Content val fromAmount = (prevState.primaryAmount.amountField as? AmountState.Data) ?.amountTextField?.cryptoAmount?.value.orZero() diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt index d890417658..05064354de 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountSetQuotesTransformer.kt @@ -17,8 +17,8 @@ import java.math.BigDecimal internal class SwapAmountSetQuotesTransformer( private val quotes: List, - private val secondaryMaximumAmountBoundary: EnterAmountBoundary, - private val secondaryMinimumAmountBoundary: EnterAmountBoundary, + private val secondaryMaximumAmountBoundary: EnterAmountBoundary?, + private val secondaryMinimumAmountBoundary: EnterAmountBoundary?, ) : Transformer { override fun transform(prevState: SwapAmountUM): SwapAmountUM { if (prevState !is SwapAmountUM.Content) return prevState diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountUpdateBalanceTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountUpdateBalanceTransformer.kt new file mode 100644 index 0000000000..dde5dd9c4c --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountUpdateBalanceTransformer.kt @@ -0,0 +1,38 @@ +package com.tangem.features.swap.v2.impl.amount.model.transformers + +import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer +import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM +import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM +import com.tangem.utils.transformer.Transformer + +internal class SwapAmountUpdateBalanceTransformer( + private val cryptoCurrencyStatus: CryptoCurrencyStatus, + private val primaryMaximumAmountBoundary: EnterAmountBoundary, + private val primaryMinimumAmountBoundary: EnterAmountBoundary, +) : Transformer { + + override fun transform(prevState: SwapAmountUM): SwapAmountUM { + val state = prevState as? SwapAmountUM.Content ?: return prevState + val primaryAmountUM = state.primaryAmount as? SwapAmountFieldUM.Content ?: return prevState + val amountField = primaryAmountUM.amountField as? AmountState.Data ?: return prevState + val amountValue = if (amountField.amountTextField.isFiatValue) { + amountField.amountTextField.fiatValue + } else { + amountField.amountTextField.value + } + return state.copy( + primaryCryptoCurrencyStatus = cryptoCurrencyStatus, + primaryAmount = primaryAmountUM.copy( + amountField = AmountFieldChangeTransformer( + cryptoCurrencyStatus = cryptoCurrencyStatus, + maxEnterAmount = primaryMaximumAmountBoundary, + minimumTransactionAmount = primaryMinimumAmountBoundary, + value = amountValue, + ).transform(prevState.primaryAmount.amountField), + ), + ) + } +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt index 1c0dbd8086..5a7e9eccb3 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueChangeTransformer.kt @@ -4,39 +4,45 @@ import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTrans import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM import com.tangem.features.swap.v2.impl.amount.model.SwapAmountQuoteUtils.updateAmount +import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM import com.tangem.utils.transformer.Transformer internal class SwapAmountValueChangeTransformer( private val primaryMaximumAmountBoundary: EnterAmountBoundary, - private val secondaryMaximumAmountBoundary: EnterAmountBoundary, private val primaryMinimumAmountBoundary: EnterAmountBoundary, - private val secondaryMinimumAmountBoundary: EnterAmountBoundary, + private val secondaryMaximumAmountBoundary: EnterAmountBoundary?, + private val secondaryMinimumAmountBoundary: EnterAmountBoundary?, private val value: String, ) : Transformer { override fun transform(prevState: SwapAmountUM): SwapAmountUM { if (prevState !is SwapAmountUM.Content) return prevState return prevState + .copy(selectedQuote = SwapQuoteUM.Loading) .updateAmount( - onPrimaryAmount = { + onPrimaryAmount = { primaryStatus -> copy( amountField = AmountFieldChangeTransformer( - cryptoCurrencyStatus = prevState.primaryCryptoCurrencyStatus, + cryptoCurrencyStatus = primaryStatus, maxEnterAmount = primaryMaximumAmountBoundary, minimumTransactionAmount = primaryMinimumAmountBoundary, value = value, ).transform(prevState.primaryAmount.amountField), ) }, - onSecondaryAmount = { - copy( - amountField = AmountFieldChangeTransformer( - cryptoCurrencyStatus = prevState.secondaryCryptoCurrencyStatus, - maxEnterAmount = secondaryMaximumAmountBoundary, - minimumTransactionAmount = secondaryMinimumAmountBoundary, - value = value, - ).transform(prevState.secondaryAmount.amountField), - ) + onSecondaryAmount = { secondaryStatus -> + if (secondaryMaximumAmountBoundary != null) { + copy( + amountField = AmountFieldChangeTransformer( + cryptoCurrencyStatus = secondaryStatus, + maxEnterAmount = secondaryMaximumAmountBoundary, + minimumTransactionAmount = secondaryMinimumAmountBoundary, + value = value, + ).transform(prevState.secondaryAmount.amountField), + ) + } else { + this + } }, ) } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt index 980216e2a3..59c38f1c94 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/transformers/SwapAmountValueMaxTransformer.kt @@ -9,9 +9,9 @@ import com.tangem.utils.transformer.Transformer internal class SwapAmountValueMaxTransformer( private val primaryMaximumAmountBoundary: EnterAmountBoundary, - private val secondaryMaximumAmountBoundary: EnterAmountBoundary, private val primaryMinimumAmountBoundary: EnterAmountBoundary, - private val secondaryMinimumAmountBoundary: EnterAmountBoundary, + private val secondaryMaximumAmountBoundary: EnterAmountBoundary?, + private val secondaryMinimumAmountBoundary: EnterAmountBoundary?, ) : Transformer { override fun transform(prevState: SwapAmountUM): SwapAmountUM { if (prevState !is SwapAmountUM.Content) return prevState @@ -19,25 +19,28 @@ internal class SwapAmountValueMaxTransformer( return prevState .copy(selectedQuote = SwapQuoteUM.Loading) .updateAmount( - onPrimaryAmount = { + onPrimaryAmount = { primaryStatus -> copy( amountField = AmountFieldSetMaxAmountTransformer( - cryptoCurrencyStatus = prevState.primaryCryptoCurrencyStatus, + cryptoCurrencyStatus = primaryStatus, maxAmount = primaryMaximumAmountBoundary, minAmount = primaryMinimumAmountBoundary, ).transform(prevState.primaryAmount.amountField), ) }, - onSecondaryAmount = { - copy( - amountField = AmountFieldSetMaxAmountTransformer( - cryptoCurrencyStatus = prevState.secondaryCryptoCurrencyStatus, - maxAmount = secondaryMaximumAmountBoundary, - minAmount = secondaryMinimumAmountBoundary, - ).transform(prevState.secondaryAmount.amountField), - ) + onSecondaryAmount = { secondaryStatus -> + if (secondaryMaximumAmountBoundary != null) { + copy( + amountField = AmountFieldSetMaxAmountTransformer( + cryptoCurrencyStatus = secondaryStatus, + maxAmount = secondaryMaximumAmountBoundary, + minAmount = secondaryMinimumAmountBoundary, + ).transform(prevState.secondaryAmount.amountField), + ) + } else { + this + } }, - ) } } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt index 212a0ca592..1ac34a19e2 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/SwapAmountContent.kt @@ -354,12 +354,12 @@ private fun AmountMaxButton(onMaxAmountClick: () -> Unit) { .padding(end = 16.dp) .clip(RoundedCornerShape(16.dp)) .background(TangemTheme.colors.background.secondary) - .padding(horizontal = 12.dp, vertical = 4.dp) .clickable( interactionSource = remember { MutableInteractionSource() }, indication = ripple(), onClick = onMaxAmountClick, - ), + ) + .padding(horizontal = 12.dp, vertical = 4.dp), ) }