Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-24 18:31:37 +05:00
parent 110ff5c745
commit e7b7c086bb
27 changed files with 373 additions and 102 deletions

View file

@ -14,7 +14,7 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.models.errors.GetUserWalletError
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.utils.coroutines.AppCoroutineScope import com.tangem.utils.coroutines.AppCoroutineScope

View file

@ -9,7 +9,7 @@ import com.tangem.domain.account.status.model.AccountCryptoCurrency
import com.tangem.domain.card.IsWalletBackupProblematicUseCase import com.tangem.domain.card.IsWalletBackupProblematicUseCase
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.models.errors.GetUserWalletError
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import io.mockk.clearMocks import io.mockk.clearMocks
import io.mockk.coEvery import io.mockk.coEvery

View file

@ -1,4 +1,4 @@
package com.tangem.domain.wallets.models package com.tangem.domain.wallets.models.errors
sealed class GetUserWalletError { sealed class GetUserWalletError {

View file

@ -4,7 +4,7 @@ import arrow.core.Either
import arrow.core.raise.either import arrow.core.raise.either
import com.tangem.domain.common.wallets.UserWalletsListRepository import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.models.errors.GetUserWalletError
/** /**
* Use case for getting selected wallet. * Use case for getting selected wallet.

View file

@ -4,7 +4,7 @@ import arrow.core.Either
import arrow.core.raise.either import arrow.core.raise.either
import com.tangem.domain.common.wallets.UserWalletsListRepository import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.models.errors.GetUserWalletError
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull

View file

@ -10,7 +10,7 @@ import com.tangem.domain.common.wallets.requireUserWalletsSync
import com.tangem.domain.core.utils.EitherFlow import com.tangem.domain.core.utils.EitherFlow
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.models.errors.GetUserWalletError
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.flow.transformLatest

View file

@ -14,7 +14,7 @@ import com.tangem.core.ui.message.DialogMessage
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.models.errors.GetUserWalletError
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.UnlockHotWalletContextualUseCase import com.tangem.domain.wallets.usecase.UnlockHotWalletContextualUseCase
import com.tangem.features.hotwallet.WalletBackupComponent import com.tangem.features.hotwallet.WalletBackupComponent

View file

@ -0,0 +1,8 @@
package com.tangem.features.send.api.subcomponents.amount
/**
* Common route for amount
*/
interface AmountRoute {
val isEditMode: Boolean
}

View file

@ -0,0 +1,19 @@
package com.tangem.features.send.api.subcomponents.amount
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableContentComponent
interface SendAmountBlockComponent : ComposableContentComponent {
fun updateState(amountUM: AmountState)
interface Factory {
fun create(
context: AppComponentContext,
params: SendAmountComponentParams.AmountBlockParams,
onClick: () -> Unit,
onResult: (AmountState) -> Unit,
): SendAmountBlockComponent
}
}

View file

@ -0,0 +1,21 @@
package com.tangem.features.send.api.subcomponents.amount
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.navigationButtons.NavigationModelCallback
import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.wallets.models.errors.GetUserWalletError
interface SendAmountComponent : ComposableContentComponent {
fun updateState(amountUM: AmountState)
interface ModelCallback : NavigationModelCallback {
fun onAmountResult(amountUM: AmountState, isResetPredefined: Boolean)
fun onConvertToAnotherToken(lastAmount: String, isEnterInFiatSelected: Boolean)
fun resetSendNavigation()
fun onError(error: GetUserWalletError)
}
interface Factory : ComponentFactory<SendAmountComponentParams.AmountParams, SendAmountComponent>
}

View file

@ -1,4 +1,4 @@
package com.tangem.features.send.subcomponents.amount package com.tangem.features.send.api.subcomponents.amount
import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
@ -9,10 +9,10 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.entity.PredefinedValues import com.tangem.features.send.api.entity.PredefinedValues
import com.tangem.features.send.common.CommonSendRoute import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
internal sealed class SendAmountComponentParams { sealed class SendAmountComponentParams {
abstract val state: AmountState abstract val state: AmountState
abstract val analyticsCategoryName: String abstract val analyticsCategoryName: String
@ -39,7 +39,7 @@ internal sealed class SendAmountComponentParams {
override val accountFlow: StateFlow<Account?>, override val accountFlow: StateFlow<Account?>,
override val isAccountModeFlow: StateFlow<Boolean>, override val isAccountModeFlow: StateFlow<Boolean>,
val callback: SendAmountComponent.ModelCallback, val callback: SendAmountComponent.ModelCallback,
val currentRoute: StateFlow<CommonSendRoute>, val currentRoute: Flow<AmountRoute>,
) : SendAmountComponentParams() ) : SendAmountComponentParams()
data class AmountBlockParams( data class AmountBlockParams(

View file

@ -0,0 +1,39 @@
package com.tangem.features.send.api.subcomponents.amount
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
import kotlinx.coroutines.flow.Flow
import java.math.BigDecimal
/**
* Trigger for reducing amount from another component
*/
interface SendAmountReduceTrigger {
suspend fun triggerReduceBy(reduceBy: AmountReduceByTransformer.ReduceByData)
suspend fun triggerReduceTo(reduceTo: BigDecimal)
suspend fun triggerIgnoreReduce()
}
/**
* Trigger for reducing amount from another component
*/
interface SendAmountReduceListener {
val reduceToTriggerFlow: Flow<BigDecimal>
val reduceByTriggerFlow: Flow<AmountReduceByTransformer.ReduceByData>
val ignoreReduceTriggerFlow: Flow<Unit>
}
/**
* Trigger amount change from another component.
* Different from another triggers because it takes raw string instead of BigDecimal
*/
interface SendAmountUpdateTrigger {
suspend fun triggerUpdateAmount(amountValue: String, isEnterInFiatSelected: Boolean?)
}
/**
* Trigger amount change from another component.
* Different from another triggers because it takes raw string instead of BigDecimal
*/
interface SendAmountUpdateListener {
val updateAmountTriggerFlow: Flow<Pair<String, Boolean?>>
}

View file

@ -1,6 +1,7 @@
package com.tangem.features.send.common package com.tangem.features.send.common
import com.tangem.core.decompose.navigation.Route import com.tangem.core.decompose.navigation.Route
import com.tangem.features.send.api.subcomponents.amount.AmountRoute
import com.tangem.features.send.api.subcomponents.destination.DestinationRoute import com.tangem.features.send.api.subcomponents.destination.DestinationRoute
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -31,5 +32,5 @@ internal sealed class CommonSendRoute : Route {
@Serializable @Serializable
data class Amount( data class Amount(
override val isEditMode: Boolean, override val isEditMode: Boolean,
) : CommonSendRoute() ) : CommonSendRoute(), AmountRoute
} }

View file

@ -10,7 +10,7 @@ import com.tangem.features.managetokens.component.ChooseManagedTokensComponent
import com.tangem.features.send.api.SendComponent import com.tangem.features.send.api.SendComponent
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.entry.SendEntryRoute import com.tangem.features.send.api.entry.SendEntryRoute
import com.tangem.features.send.subcomponents.amount.SendAmountUpdateTrigger import com.tangem.features.send.api.subcomponents.amount.SendAmountUpdateTrigger
import com.tangem.features.swap.v2.api.SendWithSwapComponent import com.tangem.features.swap.v2.api.SendWithSwapComponent
import com.tangem.features.swap.v2.api.subcomponents.SwapAmountUpdateTrigger import com.tangem.features.swap.v2.api.subcomponents.SwapAmountUpdateTrigger
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider

View file

@ -30,6 +30,8 @@ import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.models.account.derivationIndex import com.tangem.domain.models.account.derivationIndex
import com.tangem.features.send.api.SendComponent import com.tangem.features.send.api.SendComponent
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.subcomponents.amount.SendAmountComponent
import com.tangem.features.send.api.subcomponents.amount.SendAmountComponentParams
import com.tangem.features.send.api.subcomponents.destination.SendDestinationComponent import com.tangem.features.send.api.subcomponents.destination.SendDestinationComponent
import com.tangem.features.send.api.subcomponents.destination.SendDestinationComponentParams import com.tangem.features.send.api.subcomponents.destination.SendDestinationComponentParams
import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM
@ -41,14 +43,12 @@ import com.tangem.features.send.impl.R
import com.tangem.features.send.send.confirm.SendConfirmComponent import com.tangem.features.send.send.confirm.SendConfirmComponent
import com.tangem.features.send.send.model.SendModel import com.tangem.features.send.send.model.SendModel
import com.tangem.features.send.send.success.SendConfirmSuccessComponent import com.tangem.features.send.send.success.SendConfirmSuccessComponent
import com.tangem.features.send.subcomponents.amount.SendAmountComponent import com.tangem.features.send.subcomponents.amount.DefaultSendAmountComponent
import com.tangem.features.send.subcomponents.amount.SendAmountComponentParams
import com.tangem.features.send.subcomponents.destination.DefaultSendDestinationBlockComponent import com.tangem.features.send.subcomponents.destination.DefaultSendDestinationBlockComponent
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -57,6 +57,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
@Assisted appComponentContext: AppComponentContext, @Assisted appComponentContext: AppComponentContext,
@Assisted private val params: SendComponent.Params, @Assisted private val params: SendComponent.Params,
private val analyticsEventHandler: AnalyticsEventHandler, private val analyticsEventHandler: AnalyticsEventHandler,
private val amountComponentFactory: SendAmountComponent.Factory,
private val feeSelectorComponentFactory: FeeSelectorBlockComponent.Factory, private val feeSelectorComponentFactory: FeeSelectorBlockComponent.Factory,
private val sendDestinationComponentFactory: SendDestinationComponent.Factory, private val sendDestinationComponentFactory: SendDestinationComponent.Factory,
) : SendComponent, AppComponentContext by appComponentContext { ) : SendComponent, AppComponentContext by appComponentContext {
@ -113,7 +114,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
activeComponent.updateState(model.uiState.value) activeComponent.updateState(model.uiState.value)
} }
} }
is SendAmountComponent -> { is DefaultSendAmountComponent -> {
analyticsEventHandler.send( analyticsEventHandler.send(
CommonSendAnalyticEvents.AmountScreenOpened( CommonSendAnalyticEvents.AmountScreenOpened(
categoryName = model.analyticCategoryName, categoryName = model.analyticCategoryName,
@ -180,11 +181,11 @@ internal class DefaultSendComponent @AssistedInject constructor(
) )
private fun getAmountComponent(factoryContext: AppComponentContext): ComposableContentComponent { private fun getAmountComponent(factoryContext: AppComponentContext): ComposableContentComponent {
return SendAmountComponent( return amountComponentFactory.create(
appComponentContext = factoryContext, context = factoryContext,
params = SendAmountComponentParams.AmountParams( params = SendAmountComponentParams.AmountParams(
state = model.uiState.value.amountUM, state = model.uiState.value.amountUM,
currentRoute = model.currentRoute.asStateFlow(), currentRoute = model.currentRoute.filterIsInstance<CommonSendRoute.Amount>(),
isBalanceHidingFlow = model.isBalanceHiddenFlow, isBalanceHidingFlow = model.isBalanceHiddenFlow,
analyticsCategoryName = model.analyticCategoryName, analyticsCategoryName = model.analyticCategoryName,
appCurrency = model.appCurrency, appCurrency = model.appCurrency,

View file

@ -27,8 +27,8 @@ import com.tangem.features.send.common.ui.state.ConfirmUM
import com.tangem.features.send.send.confirm.model.SendConfirmModel import com.tangem.features.send.send.confirm.model.SendConfirmModel
import com.tangem.features.send.send.confirm.ui.SendConfirmContent import com.tangem.features.send.send.confirm.ui.SendConfirmContent
import com.tangem.features.send.send.ui.state.SendUM import com.tangem.features.send.send.ui.state.SendUM
import com.tangem.features.send.subcomponents.amount.SendAmountBlockComponent import com.tangem.features.send.subcomponents.amount.DefaultSendAmountBlockComponent
import com.tangem.features.send.subcomponents.amount.SendAmountComponentParams import com.tangem.features.send.api.subcomponents.amount.SendAmountComponentParams
import com.tangem.features.send.subcomponents.destination.DefaultSendDestinationBlockComponent import com.tangem.features.send.subcomponents.destination.DefaultSendDestinationBlockComponent
import com.tangem.features.send.subcomponents.notifications.DefaultSendNotificationsComponent import com.tangem.features.send.subcomponents.notifications.DefaultSendNotificationsComponent
import com.tangem.utils.extensions.orZero import com.tangem.utils.extensions.orZero
@ -60,7 +60,7 @@ internal class SendConfirmComponent(
onClick = model::showEditDestination, onClick = model::showEditDestination,
) )
private val amountBlockComponent = SendAmountBlockComponent( private val amountBlockComponent = DefaultSendAmountBlockComponent(
appComponentContext = child("sendConfirmAmountBlock"), appComponentContext = child("sendConfirmAmountBlock"),
params = SendAmountComponentParams.AmountBlockParams( params = SendAmountComponentParams.AmountBlockParams(
state = model.uiState.value.amountUM, state = model.uiState.value.amountUM,

View file

@ -45,6 +45,7 @@ import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.utils.convertToSdkAmount import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents.SendScreenSource
import com.tangem.features.send.api.subcomponents.amount.SendAmountReduceTrigger
import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorCheckReloadListener import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorCheckReloadListener
import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorCheckReloadTrigger import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorCheckReloadTrigger
@ -69,7 +70,6 @@ import com.tangem.features.send.send.confirm.model.transformers.SendConfirmSendi
import com.tangem.features.send.send.confirm.model.transformers.SendConfirmSentStateTransformer import com.tangem.features.send.send.confirm.model.transformers.SendConfirmSentStateTransformer
import com.tangem.features.send.send.confirm.model.transformers.SendConfirmationNotificationsTransformerV2 import com.tangem.features.send.send.confirm.model.transformers.SendConfirmationNotificationsTransformerV2
import com.tangem.features.send.send.ui.state.SendUM import com.tangem.features.send.send.ui.state.SendUM
import com.tangem.features.send.subcomponents.amount.SendAmountReduceTrigger
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.orZero import com.tangem.utils.extensions.orZero
import com.tangem.utils.extensions.stripZeroPlainString import com.tangem.utils.extensions.stripZeroPlainString

View file

@ -18,7 +18,7 @@ import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorBlockCo
import com.tangem.features.send.common.ui.state.ConfirmUM import com.tangem.features.send.common.ui.state.ConfirmUM
import com.tangem.features.send.common.ui.tapHelp import com.tangem.features.send.common.ui.tapHelp
import com.tangem.features.send.send.ui.state.SendUM import com.tangem.features.send.send.ui.state.SendUM
import com.tangem.features.send.subcomponents.amount.SendAmountBlockComponent import com.tangem.features.send.subcomponents.amount.DefaultSendAmountBlockComponent
import com.tangem.features.send.subcomponents.destination.DefaultSendDestinationBlockComponent import com.tangem.features.send.subcomponents.destination.DefaultSendDestinationBlockComponent
import com.tangem.features.send.subcomponents.notifications import com.tangem.features.send.subcomponents.notifications
import com.tangem.features.send.subcomponents.notifications.DefaultSendNotificationsComponent import com.tangem.features.send.subcomponents.notifications.DefaultSendNotificationsComponent
@ -31,7 +31,7 @@ private const val BLOCKS_KEY = "BLOCKS_KEY"
internal fun SendConfirmContent( internal fun SendConfirmContent(
sendUM: SendUM, sendUM: SendUM,
destinationBlockComponent: DefaultSendDestinationBlockComponent, destinationBlockComponent: DefaultSendDestinationBlockComponent,
amountBlockComponent: SendAmountBlockComponent, amountBlockComponent: DefaultSendAmountBlockComponent,
feeSelectorBlockComponent: FeeSelectorBlockComponent, feeSelectorBlockComponent: FeeSelectorBlockComponent,
notificationsComponent: DefaultSendNotificationsComponent, notificationsComponent: DefaultSendNotificationsComponent,
notificationsUM: ImmutableList<NotificationUM>, notificationsUM: ImmutableList<NotificationUM>,
@ -73,7 +73,7 @@ internal fun SendConfirmContent(
private fun LazyListScope.blocks( private fun LazyListScope.blocks(
destinationBlockComponent: DefaultSendDestinationBlockComponent, destinationBlockComponent: DefaultSendDestinationBlockComponent,
amountBlockComponent: SendAmountBlockComponent, amountBlockComponent: DefaultSendAmountBlockComponent,
feeSelectorBlockComponent: FeeSelectorBlockComponent, feeSelectorBlockComponent: FeeSelectorBlockComponent,
) { ) {
item(key = BLOCKS_KEY) { item(key = BLOCKS_KEY) {

View file

@ -41,13 +41,15 @@ import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.transaction.usecase.gasless.GetFeeForGaslessUseCase import com.tangem.domain.transaction.usecase.gasless.GetFeeForGaslessUseCase
import com.tangem.domain.transaction.usecase.gasless.GetFeeForTokenUseCase import com.tangem.domain.transaction.usecase.gasless.GetFeeForTokenUseCase
import com.tangem.domain.utils.convertToSdkAmount import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.models.errors.GetUserWalletError
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.api.SendComponent import com.tangem.features.send.api.SendComponent
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents.SendScreenSource
import com.tangem.features.send.api.entity.PredefinedValues import com.tangem.features.send.api.entity.PredefinedValues
import com.tangem.features.send.api.entity.isFromMainScreenQr import com.tangem.features.send.api.entity.isFromMainScreenQr
import com.tangem.features.send.api.subcomponents.amount.SendAmountComponent
import com.tangem.features.send.api.subcomponents.amount.SendAmountUpdateTrigger
import com.tangem.features.send.api.subcomponents.destination.SendDestinationComponent import com.tangem.features.send.api.subcomponents.destination.SendDestinationComponent
import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.api.subcomponents.destination.entity.DestinationUM
import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeSelectorUM import com.tangem.features.send.api.subcomponents.feeSelector.entity.FeeSelectorUM
@ -58,8 +60,6 @@ import com.tangem.features.send.send.analytics.SendAnalyticEvents
import com.tangem.features.send.send.confirm.SendConfirmComponent import com.tangem.features.send.send.confirm.SendConfirmComponent
import com.tangem.features.send.send.success.SendConfirmSuccessComponent import com.tangem.features.send.send.success.SendConfirmSuccessComponent
import com.tangem.features.send.send.ui.state.SendUM import com.tangem.features.send.send.ui.state.SendUM
import com.tangem.features.send.subcomponents.amount.SendAmountComponent
import com.tangem.features.send.subcomponents.amount.SendAmountUpdateTrigger
import com.tangem.features.send.subcomponents.destination.model.transformers.SendDestinationInitialStateTransformer import com.tangem.features.send.subcomponents.destination.model.transformers.SendDestinationInitialStateTransformer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.JobHolder

View file

@ -8,18 +8,22 @@ import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.ui.AmountBlockV2 import com.tangem.common.ui.amountScreen.ui.AmountBlockV2
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.send.api.entity.PredefinedValues import com.tangem.features.send.api.entity.PredefinedValues
import com.tangem.features.send.api.subcomponents.amount.SendAmountBlockComponent
import com.tangem.features.send.api.subcomponents.amount.SendAmountComponentParams
import com.tangem.features.send.subcomponents.amount.model.SendAmountModel import com.tangem.features.send.subcomponents.amount.model.SendAmountModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
internal class SendAmountBlockComponent( internal class DefaultSendAmountBlockComponent @AssistedInject constructor(
appComponentContext: AppComponentContext, @Assisted appComponentContext: AppComponentContext,
private val params: SendAmountComponentParams.AmountBlockParams, @Assisted private val params: SendAmountComponentParams.AmountBlockParams,
val onResult: (AmountState) -> Unit, @Assisted val onResult: (AmountState) -> Unit,
val onClick: () -> Unit, @Assisted val onClick: () -> Unit,
) : ComposableContentComponent, AppComponentContext by appComponentContext { ) : SendAmountBlockComponent, AppComponentContext by appComponentContext {
private val model: SendAmountModel = getOrCreateModel(params = params) private val model: SendAmountModel = getOrCreateModel(params = params)
@ -29,7 +33,7 @@ internal class SendAmountBlockComponent(
}.launchIn(componentScope) }.launchIn(componentScope)
} }
fun updateState(amountUM: AmountState) = model.updateState(amountUM) override fun updateState(amountUM: AmountState) = model.updateState(amountUM)
@Composable @Composable
override fun Content(modifier: Modifier) { override fun Content(modifier: Modifier) {
@ -44,4 +48,14 @@ internal class SendAmountBlockComponent(
modifier = modifier, modifier = modifier,
) )
} }
@AssistedFactory
interface Factory : SendAmountBlockComponent.Factory {
override fun create(
context: AppComponentContext,
params: SendAmountComponentParams.AmountBlockParams,
onClick: () -> Unit,
onResult: (AmountState) -> Unit,
): DefaultSendAmountBlockComponent
}
} }

View file

@ -5,22 +5,24 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.navigationButtons.NavigationModelCallback
import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.features.send.api.subcomponents.amount.SendAmountComponent
import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.features.send.api.subcomponents.amount.SendAmountComponentParams
import com.tangem.features.send.subcomponents.amount.model.SendAmountModel import com.tangem.features.send.subcomponents.amount.model.SendAmountModel
import com.tangem.features.send.subcomponents.amount.ui.SendAmountContent import com.tangem.features.send.subcomponents.amount.ui.SendAmountContent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
internal class SendAmountComponent( internal class DefaultSendAmountComponent @AssistedInject constructor(
appComponentContext: AppComponentContext, @Assisted appComponentContext: AppComponentContext,
private val params: SendAmountComponentParams.AmountParams, @Assisted private val params: SendAmountComponentParams.AmountParams,
) : ComposableContentComponent, AppComponentContext by appComponentContext { ) : SendAmountComponent, AppComponentContext by appComponentContext {
private val model: SendAmountModel = getOrCreateModel(params = params) private val model: SendAmountModel = getOrCreateModel(params = params)
fun updateState(amountUM: AmountState) = model.updateState(amountUM) override fun updateState(amountUM: AmountState) = model.updateState(amountUM)
@Composable @Composable
override fun Content(modifier: Modifier) { override fun Content(modifier: Modifier) {
@ -35,10 +37,11 @@ internal class SendAmountComponent(
) )
} }
interface ModelCallback : NavigationModelCallback { @AssistedFactory
fun onAmountResult(amountUM: AmountState, isResetPredefined: Boolean) interface Factory : SendAmountComponent.Factory {
fun onConvertToAnotherToken(lastAmount: String, isEnterInFiatSelected: Boolean) override fun create(
fun resetSendNavigation() context: AppComponentContext,
fun onError(error: GetUserWalletError) params: SendAmountComponentParams.AmountParams,
): DefaultSendAmountComponent
} }
} }

View file

@ -1,46 +1,15 @@
package com.tangem.features.send.subcomponents.amount package com.tangem.features.send.subcomponents.amount
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData
import kotlinx.coroutines.flow.Flow import com.tangem.features.send.api.subcomponents.amount.SendAmountReduceListener
import com.tangem.features.send.api.subcomponents.amount.SendAmountReduceTrigger
import com.tangem.features.send.api.subcomponents.amount.SendAmountUpdateListener
import com.tangem.features.send.api.subcomponents.amount.SendAmountUpdateTrigger
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import java.math.BigDecimal import java.math.BigDecimal
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
/**
* Trigger for reducing amount from another component
*/
interface SendAmountReduceTrigger {
suspend fun triggerReduceBy(reduceBy: ReduceByData)
suspend fun triggerReduceTo(reduceTo: BigDecimal)
suspend fun triggerIgnoreReduce()
}
/**
* Trigger for reducing amount from another component
*/
interface SendAmountReduceListener {
val reduceToTriggerFlow: Flow<BigDecimal>
val reduceByTriggerFlow: Flow<ReduceByData>
val ignoreReduceTriggerFlow: Flow<Unit>
}
/**
* Trigger amount change from another component.
* Different from another triggers because it takes raw string instead of BigDecimal
*/
interface SendAmountUpdateTrigger {
suspend fun triggerUpdateAmount(amountValue: String, isEnterInFiatSelected: Boolean?)
}
/**
* Trigger amount change from another component.
* Different from another triggers because it takes raw string instead of BigDecimal
*/
interface SendAmountUpdateListener {
val updateAmountTriggerFlow: Flow<Pair<String, Boolean?>>
}
@Singleton @Singleton
internal class DefaultSendAmountReduceTrigger @Inject constructor() : internal class DefaultSendAmountReduceTrigger @Inject constructor() :
SendAmountReduceTrigger, SendAmountReduceTrigger,

View file

@ -1,10 +1,9 @@
package com.tangem.features.send.subcomponents.amount.di package com.tangem.features.send.subcomponents.amount.di
import com.tangem.features.send.api.subcomponents.amount.*
import com.tangem.features.send.subcomponents.amount.DefaultSendAmountBlockComponent
import com.tangem.features.send.subcomponents.amount.DefaultSendAmountComponent
import com.tangem.features.send.subcomponents.amount.DefaultSendAmountReduceTrigger import com.tangem.features.send.subcomponents.amount.DefaultSendAmountReduceTrigger
import com.tangem.features.send.subcomponents.amount.SendAmountReduceListener
import com.tangem.features.send.subcomponents.amount.SendAmountReduceTrigger
import com.tangem.features.send.subcomponents.amount.SendAmountUpdateListener
import com.tangem.features.send.subcomponents.amount.SendAmountUpdateTrigger
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -15,6 +14,16 @@ import javax.inject.Singleton
@Module @Module
internal interface SendAmountModule { internal interface SendAmountModule {
@Singleton
@Binds
fun provideSendAmountComponentFactory(impl: DefaultSendAmountComponent.Factory): SendAmountComponent.Factory
@Singleton
@Binds
fun provideSendAmountBlockComponentFactory(
impl: DefaultSendAmountBlockComponent.Factory,
): SendAmountBlockComponent.Factory
@Singleton @Singleton
@Binds @Binds
fun provideSendAmountReduceTrigger(impl: DefaultSendAmountReduceTrigger): SendAmountReduceTrigger fun provideSendAmountReduceTrigger(impl: DefaultSendAmountReduceTrigger): SendAmountReduceTrigger

View file

@ -9,13 +9,13 @@ import com.tangem.common.ui.amountScreen.converters.field.AmountFieldSetMaxAmoun
import com.tangem.common.ui.amountScreen.models.AmountParameters import com.tangem.common.ui.amountScreen.models.AmountParameters
import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.common.ui.navigationButtons.NavigationButton import com.tangem.common.ui.navigationButtons.NavigationButton
import com.tangem.common.ui.navigationButtons.NavigationUM import com.tangem.common.ui.navigationButtons.NavigationUM
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.WrappedList import com.tangem.core.ui.extensions.WrappedList
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
@ -29,19 +29,21 @@ import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.send.api.entity.PredefinedValues import com.tangem.features.send.api.entity.PredefinedValues
import com.tangem.features.send.api.entity.isFromMainScreenQr import com.tangem.features.send.api.entity.isFromMainScreenQr
import com.tangem.features.send.api.subcomponents.amount.AmountRoute
import com.tangem.features.send.api.subcomponents.amount.SendAmountComponentParams
import com.tangem.features.send.api.subcomponents.amount.SendAmountReduceListener
import com.tangem.features.send.api.subcomponents.amount.SendAmountUpdateListener
import com.tangem.features.send.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents import com.tangem.features.send.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents
import com.tangem.features.send.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents.SelectedCurrencyType import com.tangem.features.send.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents.SelectedCurrencyType
import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorReloadTrigger import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.send.common.CommonSendRoute import com.tangem.features.send.common.CommonSendRoute
import com.tangem.features.send.subcomponents.amount.SendAmountComponentParams
import com.tangem.features.send.subcomponents.amount.SendAmountReduceListener
import com.tangem.features.send.subcomponents.amount.SendAmountUpdateListener
import com.tangem.features.send.impl.R import com.tangem.features.send.impl.R
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.extensions.orZero import com.tangem.utils.extensions.orZero
import com.tangem.utils.isNullOrZero import com.tangem.utils.isNullOrZero
import com.tangem.utils.transformer.update import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.* import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
import kotlin.properties.Delegates import kotlin.properties.Delegates
@ -271,13 +273,17 @@ internal class SendAmountModel @Inject constructor(
override fun onConvertToAnotherToken() { override fun onConvertToAnotherToken() {
val amountParams = params as? SendAmountComponentParams.AmountParams ?: return val amountParams = params as? SendAmountComponentParams.AmountParams ?: return
if (amountParams.currentRoute.value.isEditMode) { modelScope.launch {
sendAmountAlertFactory.showResetSendingAlert { var isEditMode = false
params.callback.resetSendNavigation() amountParams.currentRoute.collect { route -> isEditMode = route.isEditMode }
if (isEditMode) {
sendAmountAlertFactory.showResetSendingAlert {
params.callback.resetSendNavigation()
confirmConvertToToken()
}
} else {
confirmConvertToToken() confirmConvertToToken()
} }
} else {
confirmConvertToToken()
} }
} }
@ -361,7 +367,9 @@ internal class SendAmountModel @Inject constructor(
val params = params as? SendAmountComponentParams.AmountParams ?: return val params = params as? SendAmountComponentParams.AmountParams ?: return
combine( combine(
flow = uiState, flow = uiState,
flow2 = params.currentRoute.filterIsInstance<CommonSendRoute.Amount>(), // Filter on the public AmountRoute interface (not the internal CommonSendRoute.Amount) so an
// external host (e.g. staking) that supplies its own AmountRoute is not silently dropped here.
flow2 = params.currentRoute.filterIsInstance<AmountRoute>(),
transform = { state, route -> state to route }, transform = { state, route -> state to route },
).onEach { (state, route) -> ).onEach { (state, route) ->
setSendWithSwapAvailability() setSendWithSwapAvailability()

View file

@ -0,0 +1,179 @@
package com.tangem.features.send.subcomponents.amount.model
import arrow.core.right
import com.google.common.truth.Truth.assertThat
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.navigationButtons.NavigationUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.model.MutableParamsContainer
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.api.entity.PredefinedValues
import com.tangem.features.send.api.subcomponents.amount.AmountRoute
import com.tangem.features.send.api.subcomponents.amount.SendAmountComponent
import com.tangem.features.send.api.subcomponents.amount.SendAmountComponentParams
import com.tangem.features.send.api.subcomponents.amount.SendAmountReduceListener
import com.tangem.features.send.api.subcomponents.amount.SendAmountUpdateListener
import com.tangem.features.send.api.subcomponents.feeSelector.FeeSelectorReloadTrigger
import com.tangem.features.send.impl.R
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.clearMocks
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.math.BigDecimal
/**
* Guards the route-decoupling fix: `SendAmountModel.configAmountNavigation()` filters its route flow on
* the public `AmountRoute` interface, not the `internal CommonSendRoute.Amount`. The test feeds a
* foreign `AmountRoute` (which is NOT a `CommonSendRoute.Amount`) and asserts the navigation result is
* still produced before the fix the `combine`'s `filterIsInstance<CommonSendRoute.Amount>()` dropped
* it and `onNavigationResult` never fired, leaving an external host (e.g. staking) with a dead Next
* button. Also checks the `isEditMode` back-icon / primary-button mapping is unaffected.
*/
@OptIn(ExperimentalCoroutinesApi::class)
internal class SendAmountNavigationTest {
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase = mockk()
private val sendAmountReduceListener: SendAmountReduceListener = mockk()
private val sendAmountUpdateListener: SendAmountUpdateListener = mockk()
private val analyticsEventHandler: AnalyticsEventHandler = mockk(relaxed = true)
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase = mockk()
private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger = mockk(relaxed = true)
private val getUserWalletUseCase: GetUserWalletUseCase = mockk()
private val sendAmountAlertFactory: SendAmountAlertFactory = mockk(relaxed = true)
private val getWalletsUseCase: GetWalletsUseCase = mockk(relaxed = true)
private val callback: SendAmountComponent.ModelCallback = mockk(relaxed = true)
private val cryptoCurrency = MockCryptoCurrencyFactory().createCoin(Blockchain.Ethereum)
private var model: SendAmountModel? = null
@BeforeEach
fun setup() {
clearMocks(
getMinimumTransactionAmountSyncUseCase,
sendAmountReduceListener,
sendAmountUpdateListener,
getSelectedAppCurrencyUseCase,
getUserWalletUseCase,
callback,
)
// No wallet → the model stays on AmountState.Empty (the heavy AmountStateConverter path is skipped),
// which is all the navigation block needs to emit.
every { getUserWalletUseCase.invokeFlow(any()) } returns emptyFlow()
every { sendAmountReduceListener.reduceToTriggerFlow } returns emptyFlow()
every { sendAmountReduceListener.reduceByTriggerFlow } returns emptyFlow()
every { sendAmountReduceListener.ignoreReduceTriggerFlow } returns emptyFlow()
every { sendAmountUpdateListener.updateAmountTriggerFlow } returns emptyFlow()
coEvery { getSelectedAppCurrencyUseCase.invokeSync() } returns AppCurrency.Default.right()
coEvery { getMinimumTransactionAmountSyncUseCase(any(), any()) } returns BigDecimal.ZERO.right()
}
@AfterEach
fun tearDown() {
// Cancel modelScope so the long-lived navigation/status collectors stop between tests.
model?.onDestroy()
model = null
}
@Test
fun `GIVEN a foreign AmountRoute WHEN model created THEN navigation produced with close icon and next button`() =
runTest {
// Arrange — a route that is NOT CommonSendRoute.Amount (the impl type the model used to filter on).
val navSlot = slot<NavigationUM>()
// Act
createModel(testScope = this, route = TestAmountRoute(isEditMode = false))
advanceUntilIdle()
// Assert — before the fix this never fired for a non-CommonSendRoute.Amount route.
verify(atLeast = 1) { callback.onNavigationResult(capture(navSlot)) }
val content = navSlot.captured as NavigationUM.Content
assertThat(content.backIconRes).isEqualTo(R.drawable.ic_close_24)
assertThat(content.primaryButton.textReference).isEqualTo(resourceReference(R.string.common_next))
}
@Test
fun `GIVEN a foreign AmountRoute in edit mode WHEN model created THEN navigation has back icon and continue button`() =
runTest {
// Arrange
val navSlot = slot<NavigationUM>()
// Act
createModel(testScope = this, route = TestAmountRoute(isEditMode = true))
advanceUntilIdle()
// Assert
verify(atLeast = 1) { callback.onNavigationResult(capture(navSlot)) }
val content = navSlot.captured as NavigationUM.Content
assertThat(content.backIconRes).isEqualTo(R.drawable.ic_back_24)
assertThat(content.primaryButton.textReference).isEqualTo(resourceReference(R.string.common_continue))
}
private fun createModel(testScope: TestScope, route: AmountRoute): SendAmountModel {
val params = SendAmountComponentParams.AmountParams(
state = AmountState.Empty,
analyticsCategoryName = "test",
userWalletId = UserWalletId(stringValue = "0123456789"),
appCurrency = AppCurrency.Default,
predefinedValues = PredefinedValues.Empty,
cryptoCurrency = cryptoCurrency,
cryptoCurrencyStatusFlow = MutableStateFlow(mockk<CryptoCurrencyStatus>(relaxed = true)),
isBalanceHidingFlow = MutableStateFlow(false),
analyticsSendSource = CommonSendAnalyticEvents.CommonSendSource.Send,
accountFlow = MutableStateFlow<Account?>(null),
isAccountModeFlow = MutableStateFlow(false),
callback = callback,
currentRoute = MutableStateFlow(route),
)
return SendAmountModel(
paramsContainer = MutableParamsContainer(value = params),
dispatchers = testScope.createTestingCoroutineDispatcherProvider(),
getMinimumTransactionAmountSyncUseCase = getMinimumTransactionAmountSyncUseCase,
sendAmountReduceListener = sendAmountReduceListener,
sendAmountUpdateListener = sendAmountUpdateListener,
analyticsEventHandler = analyticsEventHandler,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
feeSelectorReloadTrigger = feeSelectorReloadTrigger,
getUserWalletUseCase = getUserWalletUseCase,
sendAmountAlertFactory = sendAmountAlertFactory,
getWalletsUseCase = getWalletsUseCase,
).also { model = it }
}
private fun TestScope.createTestingCoroutineDispatcherProvider(): TestingCoroutineDispatcherProvider {
val testDispatcher = StandardTestDispatcher(testScheduler)
return TestingCoroutineDispatcherProvider(
main = testDispatcher,
mainImmediate = testDispatcher,
io = testDispatcher,
default = testDispatcher,
single = testDispatcher,
)
}
private data class TestAmountRoute(override val isEditMode: Boolean) : AmountRoute
}

View file

@ -22,7 +22,7 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.tokens.wallet.WalletBalanceFetcher import com.tangem.domain.tokens.wallet.WalletBalanceFetcher
import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.models.errors.GetUserWalletError
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.SelectWalletUseCase import com.tangem.domain.wallets.usecase.SelectWalletUseCase

View file

@ -24,8 +24,8 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier import com.tangem.domain.networks.multi.MultiNetworkStatusSupplier
import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier
import com.tangem.domain.wallets.PromoCodeActivationResult import com.tangem.domain.wallets.PromoCodeActivationResult
import com.tangem.domain.wallets.models.GetUserWalletError
import com.tangem.domain.wallets.models.errors.ActivatePromoCodeError import com.tangem.domain.wallets.models.errors.ActivatePromoCodeError
import com.tangem.domain.wallets.models.errors.GetUserWalletError
import com.tangem.domain.wallets.usecase.ActivateBitcoinPromocodeUseCase import com.tangem.domain.wallets.usecase.ActivateBitcoinPromocodeUseCase
import com.tangem.domain.wallets.usecase.BindRefcodeWithWalletUseCase import com.tangem.domain.wallets.usecase.BindRefcodeWithWalletUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase