Updated on 2026-08-14
This commit is contained in:
parent
110ff5c745
commit
e7b7c086bb
27 changed files with 373 additions and 102 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.tangem.domain.wallets.models
|
package com.tangem.domain.wallets.models.errors
|
||||||
|
|
||||||
sealed class GetUserWalletError {
|
sealed class GetUserWalletError {
|
||||||
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.tangem.features.send.api.subcomponents.amount
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common route for amount
|
||||||
|
*/
|
||||||
|
interface AmountRoute {
|
||||||
|
val isEditMode: Boolean
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
}
|
||||||
|
|
@ -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(
|
||||||
|
|
@ -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?>>
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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,
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue