Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-15 22:43:52 +03:00
commit d6f9f59866
1729 changed files with 67614 additions and 9361 deletions

View file

@ -22,7 +22,7 @@ dependencies {
/** Feature */
implementation(projects.features.swapV2.api)
implementation(projects.features.manageTokens.api)
implementation(projects.features.sendV2.api)
implementation(projects.features.send.api)
implementation(projects.features.commonFeatures.api)
/** Core */
@ -95,7 +95,7 @@ dependencies {
kapt(deps.hilt.kapt)
/** Test */
testImplementation(deps.test.junit)
testImplementation(deps.test.junit5)
testImplementation(deps.test.truth)
testImplementation(deps.test.mockk)
}

View file

@ -7,7 +7,7 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.swap.models.SwapDirection
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import com.tangem.features.swap.v2.impl.sendviaswap.SendWithSwapRoute
import kotlinx.coroutines.flow.Flow

View file

@ -37,8 +37,8 @@ import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
import com.tangem.domain.transaction.models.AllowanceInfo
import com.tangem.domain.transaction.usecase.GetAllowanceInfoUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.send.v2.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.send.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents
import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkListener
import com.tangem.features.swap.v2.impl.R
import com.tangem.features.swap.v2.impl.amount.SwapAmountBlockComponent.SwapChooseProviderConfig

View file

@ -13,7 +13,7 @@ import com.tangem.domain.swap.models.SwapTxType
import com.tangem.domain.swap.usecase.GetSwapSupportedPairsUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.managetokens.component.analytics.CommonManageTokensAnalyticEvents
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkComponent
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseTokenNetworkContentUM
import com.tangem.features.swap.v2.impl.choosetoken.fromSupported.entity.SwapChooseTokenNetworkUM

View file

@ -5,8 +5,8 @@ import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.express.models.ExpressProvider
import com.tangem.domain.swap.models.SwapDataModel
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import kotlinx.collections.immutable.ImmutableList

View file

@ -22,10 +22,10 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.swap.models.R
import com.tangem.domain.swap.models.SwapDirection
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.v2.api.subcomponents.destination.DestinationRoute
import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponent
import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.subcomponents.destination.DestinationRoute
import com.tangem.features.send.api.subcomponents.destination.SendDestinationComponent
import com.tangem.features.send.api.subcomponents.destination.SendDestinationComponentParams
import com.tangem.features.swap.v2.api.SendWithSwapComponent
import com.tangem.features.swap.v2.impl.amount.SwapAmountComponent
import com.tangem.features.swap.v2.impl.amount.SwapAmountComponentParams
@ -102,6 +102,8 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor(
if (model.currentRoute.value.isEditMode) {
activeComponent.updateState(model.uiState.value)
}
// Re-sync destination from parent on Confirm entry, bypassing the edit-mode gate ([REDACTED_TASK_KEY]).
activeComponent.updateDestinationState(model.uiState.value.destinationUM)
val fromCurrency = params.currency
val content = model.uiState.value.amountUM as? SwapAmountUM.Content ?: return@launch
val toCurrency = content.secondaryCryptoCurrencyStatus?.currency ?: return@launch

View file

@ -1,7 +1,7 @@
package com.tangem.features.swap.v2.impl.sendviaswap
import com.tangem.core.decompose.navigation.Route
import com.tangem.features.send.v2.api.subcomponents.destination.DestinationRoute
import com.tangem.features.send.api.subcomponents.destination.DestinationRoute
import com.tangem.features.swap.v2.impl.amount.SwapAmountRoute
import kotlinx.serialization.Serializable

View file

@ -15,7 +15,7 @@ import com.tangem.core.analytics.models.AnalyticsParam.Key.SEND_TOKEN
import com.tangem.core.analytics.models.AppsFlyerIncludedEvent
import com.tangem.domain.express.models.ExpressRateType
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
internal sealed class SendWithSwapAnalyticEvents(
event: String,

View file

@ -14,13 +14,14 @@ import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.swap.models.SwapDirection
import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.v2.api.entity.PredefinedValues
import com.tangem.features.send.v2.api.params.FeeSelectorParams.*
import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationBlockComponent
import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams
import com.tangem.features.send.api.FeeSelectorBlockComponent
import com.tangem.features.send.api.SendNotificationsComponent
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.entity.PredefinedValues
import com.tangem.features.send.api.params.FeeSelectorParams.*
import com.tangem.features.send.api.subcomponents.destination.SendDestinationBlockComponent
import com.tangem.features.send.api.subcomponents.destination.SendDestinationComponentParams
import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.swap.v2.impl.amount.SwapAmountBlockComponent
import com.tangem.features.swap.v2.impl.amount.SwapAmountComponentParams
import com.tangem.features.swap.v2.impl.common.SwapUtils.SEND_WITH_SWAP_PROVIDER_TYPES
@ -40,7 +41,7 @@ import kotlinx.coroutines.flow.*
internal class SendWithSwapConfirmComponent @AssistedInject constructor(
@Assisted private val appComponentContext: AppComponentContext,
@Assisted private val params: Params,
sendDestinationBlockComponent: SendDestinationBlockComponent.Factory,
sendDestinationBlockComponentFactory: SendDestinationBlockComponent.Factory,
feeSelectorBlockComponentFactory: FeeSelectorBlockComponent.Factory,
sendNotificationsComponentFactory: SendNotificationsComponent.Factory,
) : ComposableContentComponent, AppComponentContext by appComponentContext {
@ -69,7 +70,7 @@ internal class SendWithSwapConfirmComponent @AssistedInject constructor(
onClick = model::showEditAmount,
)
private val sendDestinationBlockComponent = sendDestinationBlockComponent.create(
private val sendDestinationBlockComponent = sendDestinationBlockComponentFactory.create(
context = child("sendWithSwapConfirmDestinationBlock"),
params = SendDestinationComponentParams.DestinationBlockParams(
state = model.uiState.value.destinationUM,
@ -81,7 +82,8 @@ internal class SendWithSwapConfirmComponent @AssistedInject constructor(
predefinedValues = PredefinedValues.Empty,
isAllowSelfSend = true,
),
onResult = model::onDestinationResult,
// No feedback: the read-only block is driven one-way by the model.uiState collector ([REDACTED_TASK_KEY]).
onResult = {},
onClick = model::showEditDestination,
)
@ -151,15 +153,29 @@ internal class SendWithSwapConfirmComponent @AssistedInject constructor(
val confirmUM = state.confirmUM as? ConfirmUM.Content
blockClickEnableFlow.value = confirmUM?.isTransactionInProcess == false
}.launchIn(componentScope)
// Single source of truth: the block always mirrors the model's authoritative destinationUM.
model.uiState
.map { it.destinationUM }
.distinctUntilChanged()
.onEach(sendDestinationBlockComponent::updateState)
.launchIn(componentScope)
}
fun updateState(sendWithSwapUM: SendWithSwapUM) {
amountBlockComponent.updateState(sendWithSwapUM.amountUM)
sendDestinationBlockComponent.updateState(sendWithSwapUM.destinationUM)
feeSelectorBlockComponent.updateState(sendWithSwapUM.feeSelectorUM)
model.updateState(sendWithSwapUM)
}
// Re-sync destination from parent on Confirm entry, bypassing the edit-mode gate; Empty only occurs on
// reset (which leaves Confirm), so only Content is applied ([REDACTED_TASK_KEY]).
fun updateDestinationState(destinationUM: DestinationUM) {
if (destinationUM is DestinationUM.Content && destinationUM != model.uiState.value.destinationUM) {
model.onDestinationResult(destinationUM)
}
}
@Composable
override fun Content(modifier: Modifier) {
val sendWithSwapUM by model.uiState.collectAsStateWithLifecycle()

View file

@ -37,16 +37,16 @@ import com.tangem.domain.transaction.usecase.EstimateFeeUseCase
import com.tangem.domain.transaction.usecase.gasless.EstimateFeeForGaslessTxUseCase
import com.tangem.domain.transaction.usecase.gasless.EstimateFeeForTokenUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.SendScreenSource
import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateListener
import com.tangem.features.send.v2.api.subcomponents.notifications.SendNotificationsUpdateTrigger
import com.tangem.features.send.api.SendNotificationsComponent
import com.tangem.features.send.api.SendNotificationsComponent.Params.NotificationData
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents.SendScreenSource
import com.tangem.features.send.api.callbacks.FeeSelectorModelCallback
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.send.api.subcomponents.notifications.SendNotificationsUpdateListener
import com.tangem.features.send.api.subcomponents.notifications.SendNotificationsUpdateTrigger
import com.tangem.features.swap.v2.api.subcomponents.SwapAmountUpdateTrigger
import com.tangem.features.swap.v2.impl.R
import com.tangem.features.swap.v2.impl.amount.SwapAmountReduceTrigger
@ -76,7 +76,7 @@ import jakarta.inject.Inject
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import java.math.BigDecimal
import com.tangem.features.send.v2.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
import com.tangem.features.send.api.entity.FeeSelectorUM as FeeSelectorUMRedesigned
import com.tangem.utils.transformer.update as transformerUpdate
@Suppress("LongParameterList", "LargeClass")

View file

@ -23,7 +23,7 @@ import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.transaction.usecase.gasless.CreateAndSendGaslessTransactionUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils
import com.tangem.features.send.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

View file

@ -10,11 +10,11 @@ import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.swap.models.SwapDirection.Companion.withSwapDirection
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkIfCustomFeeTooHigh
import com.tangem.features.send.v2.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkIfCustomFeeTooLow
import com.tangem.features.send.v2.api.utils.formatFooterFiatFee
import com.tangem.features.send.v2.api.utils.getTronTokenFeeSendingText
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkIfCustomFeeTooHigh
import com.tangem.features.send.api.subcomponents.feeSelector.utils.FeeCalculationUtils.checkIfCustomFeeTooLow
import com.tangem.features.send.api.utils.formatFooterFiatFee
import com.tangem.features.send.api.utils.getTronTokenFeeSendingText
import com.tangem.features.swap.v2.impl.R
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import com.tangem.features.swap.v2.impl.common.entity.ConfirmUM

View file

@ -12,9 +12,9 @@ import androidx.compose.ui.unit.dp
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.common.ui.notifications.notifications
import com.tangem.core.ui.components.SpacerH16
import com.tangem.features.send.v2.api.FeeSelectorBlockComponent
import com.tangem.features.send.v2.api.SendNotificationsComponent
import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationBlockComponent
import com.tangem.features.send.api.FeeSelectorBlockComponent
import com.tangem.features.send.api.SendNotificationsComponent
import com.tangem.features.send.api.subcomponents.destination.SendDestinationBlockComponent
import com.tangem.features.swap.v2.impl.amount.SwapAmountBlockComponent
import com.tangem.features.swap.v2.impl.common.entity.ConfirmUM
import com.tangem.features.swap.v2.impl.notifications.SwapNotificationsComponent

View file

@ -1,8 +1,8 @@
package com.tangem.features.swap.v2.impl.sendviaswap.entity
import com.tangem.common.ui.navigationButtons.NavigationUM
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import com.tangem.features.swap.v2.impl.common.entity.ConfirmUM

View file

@ -19,10 +19,10 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.swap.models.SwapDirection
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponent
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.destination.SendDestinationComponent
import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.swap.v2.api.SendWithSwapComponent
import com.tangem.features.swap.v2.impl.amount.SwapAmountComponent
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM

View file

@ -45,12 +45,12 @@ import com.tangem.domain.express.models.ExpressProviderType
import com.tangem.domain.swap.models.SwapDataModel
import com.tangem.domain.swap.models.SwapDataTransactionModel
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.features.send.v2.api.entity.FeeExtraInfo
import com.tangem.features.send.v2.api.entity.FeeItem
import com.tangem.features.send.v2.api.entity.FeeNonce
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationTextFieldUM
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.api.entity.FeeExtraInfo
import com.tangem.features.send.api.entity.FeeItem
import com.tangem.features.send.api.entity.FeeNonce
import com.tangem.features.send.api.entity.FeeSelectorUM
import com.tangem.features.send.api.subcomponents.destination.entity.DestinationTextFieldUM
import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.swap.v2.impl.R
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM

View file

@ -14,7 +14,7 @@ import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify
import org.junit.Test
import org.junit.jupiter.api.Test
import java.math.BigDecimal
class SwapAmountAnalyticsSenderTest {

View file

@ -5,7 +5,7 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import io.mockk.every
import io.mockk.mockk
import org.junit.Test
import org.junit.jupiter.api.Test
import java.math.BigDecimal
internal class SwapFromSubtitleConverterTest {

View file

@ -17,7 +17,7 @@ import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import io.mockk.every
import io.mockk.mockk
import kotlinx.collections.immutable.persistentListOf
import org.junit.Test
import org.junit.jupiter.api.Test
import java.math.BigDecimal
internal class SwapAmountSelectQuoteTransformerTest {

View file

@ -11,7 +11,7 @@ import com.tangem.domain.swap.models.SwapAmountType
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import io.mockk.every
import io.mockk.mockk
import org.junit.Test
import org.junit.jupiter.api.Test
import java.math.BigDecimal
internal class SwapProviderListItemConverterTest {

View file

@ -11,7 +11,7 @@ import com.tangem.features.swap.v2.impl.chooseprovider.entity.SwapProviderState
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import io.mockk.every
import io.mockk.mockk
import org.junit.Test
import org.junit.jupiter.api.Test
import java.math.BigDecimal
@Suppress("DEPRECATION")

View file

@ -4,7 +4,7 @@ import com.google.common.truth.Truth.assertThat
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.swap.models.SwapAmountType
import io.mockk.mockk
import org.junit.Test
import org.junit.jupiter.api.Test
internal class AmountErrorCurrencyResolverTest {