From fd5ed580e4396174da43323603b5f5fbd5068d1b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 24 Jul 2025 19:22:32 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../NavigationModelCallback.kt | 2 + .../SendDestinationComponentParams.kt | 2 - .../send/v2/send/DefaultSendComponent.kt | 151 +++++------------- .../features/send/v2/send/model/SendModel.kt | 101 ++++++++++-- .../v2/sendnft/DefaultNFTSendComponent.kt | 61 +++---- .../send/v2/sendnft/model/NFTSendModel.kt | 42 +++-- .../amount/SendAmountComponentParams.kt | 5 +- .../amount/model/SendAmountModel.kt | 11 +- .../destination/model/SendDestinationModel.kt | 26 +-- .../fee/SendFeeComponentParams.kt | 1 - .../subcomponents/fee/model/SendFeeModel.kt | 4 +- .../v2/impl/amount/SwapAmountComponent.kt | 4 +- .../common/SwapNavigationModelCallback.kt | 9 -- .../DefaultSendWithSwapComponent.kt | 2 - 14 files changed, 201 insertions(+), 220 deletions(-) delete mode 100644 features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapNavigationModelCallback.kt diff --git a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationModelCallback.kt b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationModelCallback.kt index 1e2401e1e8..2e84e796a8 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationModelCallback.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationModelCallback.kt @@ -2,4 +2,6 @@ package com.tangem.common.ui.navigationButtons interface NavigationModelCallback { fun onNavigationResult(navigationUM: NavigationUM) + fun onBackClick() + fun onNextClick() } \ No newline at end of file diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/destination/SendDestinationComponentParams.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/destination/SendDestinationComponentParams.kt index b0dbdc1aad..d87ee8379f 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/destination/SendDestinationComponentParams.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/subcomponents/destination/SendDestinationComponentParams.kt @@ -24,8 +24,6 @@ sealed class SendDestinationComponentParams { val isBalanceHidingFlow: StateFlow, val currentRoute: Flow, val callback: SendDestinationComponent.ModelCallback, - val onBackClick: () -> Unit, - val onNextClick: () -> Unit, ) : SendDestinationComponentParams() data class DestinationBlockParams( diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt index 532911f4f6..c0d9b98bd2 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt @@ -23,29 +23,28 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.v2.api.FeeSelectorBlockComponent import com.tangem.features.send.v2.api.SendComponent +import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams +import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents -import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.common.ui.SendContent import com.tangem.features.send.v2.common.ui.state.ConfirmUM -import com.tangem.features.send.v2.common.utils.safeNextClick import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.send.confirm.SendConfirmComponent import com.tangem.features.send.v2.send.model.SendModel import com.tangem.features.send.v2.send.success.SendConfirmSuccessComponent import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams -import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent -import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationComponent -import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams -import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationBlockComponent +import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationComponent +import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponent import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.launch @@ -58,7 +57,6 @@ internal class DefaultSendComponent @AssistedInject constructor( ) : SendComponent, AppComponentContext by appComponentContext { private val stackNavigation = StackNavigation() - private val analyticCategoryName = CommonSendAnalyticEvents.SEND_CATEGORY private val innerRouter = InnerRouter( stackNavigation = stackNavigation, @@ -67,23 +65,11 @@ internal class DefaultSendComponent @AssistedInject constructor( private val model: SendModel = getOrCreateModel(params = params, router = innerRouter) - private val initialRoute = if (params.amount == null) { - if (model.uiState.value.isRedesignEnabled) { - CommonSendRoute.Amount(isEditMode = false) - } else { - CommonSendRoute.Destination(isEditMode = false) - } - } else { - CommonSendRoute.Empty - } - - private val currentRoute = MutableStateFlow(initialRoute) - private val childStack = childStack( key = "sendInnerStack", source = stackNavigation, serializer = null, - initialConfiguration = initialRoute, + initialConfiguration = model.initialRoute, handleBackButton = true, childFactory = { configuration, factoryContext -> createChild( @@ -103,31 +89,33 @@ internal class DefaultSendComponent @AssistedInject constructor( ) { stack -> componentScope.launch { when (val activeComponent = stack.active.instance) { - is SendConfirmComponent -> if (currentRoute.value.isEditMode) { + is SendConfirmComponent -> if (model.currentRoute.value.isEditMode) { analyticsEventHandler.send( - CommonSendAnalyticEvents.ConfirmationScreenOpened(categoryName = analyticCategoryName), + CommonSendAnalyticEvents.ConfirmationScreenOpened( + categoryName = model.analyticCategoryName, + ), ) activeComponent.updateState(model.uiState.value) } is SendAmountComponent -> { analyticsEventHandler.send( - CommonSendAnalyticEvents.AmountScreenOpened(categoryName = analyticCategoryName), + CommonSendAnalyticEvents.AmountScreenOpened(categoryName = model.analyticCategoryName), ) activeComponent.updateState(model.uiState.value.amountUM) } is DefaultSendDestinationComponent -> { analyticsEventHandler.send( - CommonSendAnalyticEvents.AddressScreenOpened(categoryName = analyticCategoryName), + CommonSendAnalyticEvents.AddressScreenOpened(categoryName = model.analyticCategoryName), ) activeComponent.updateState(model.uiState.value.destinationUM) } is SendFeeComponent -> { analyticsEventHandler.send( - CommonSendAnalyticEvents.FeeScreenOpened(categoryName = analyticCategoryName), + CommonSendAnalyticEvents.FeeScreenOpened(categoryName = model.analyticCategoryName), ) } } - currentRoute.emit(stack.active.configuration) + model.currentRoute.emit(stack.active.configuration) } } } @@ -146,93 +134,42 @@ internal class DefaultSendComponent @AssistedInject constructor( private fun createChild(route: CommonSendRoute, factoryContext: AppComponentContext) = when (route) { CommonSendRoute.Empty -> getStubComponent() - is CommonSendRoute.Destination -> getDestinationComponent(factoryContext, route) - is CommonSendRoute.Amount -> getAmountComponent(factoryContext, route) + is CommonSendRoute.Destination -> getDestinationComponent(factoryContext) + is CommonSendRoute.Amount -> getAmountComponent(factoryContext) is CommonSendRoute.Fee -> getFeeComponent(factoryContext) is CommonSendRoute.Confirm -> getConfirmComponent(factoryContext) is CommonSendRoute.ConfirmSuccess -> getConfirmSuccessComponent(factoryContext) } - private fun getDestinationComponent( - factoryContext: AppComponentContext, - route: CommonSendRoute, - ): DefaultSendDestinationComponent = DefaultSendDestinationComponent( - appComponentContext = factoryContext, - params = SendDestinationComponentParams.DestinationParams( - state = model.uiState.value.destinationUM, - currentRoute = currentRoute.filterIsInstance(), - isBalanceHidingFlow = model.isBalanceHiddenFlow, - analyticsCategoryName = analyticCategoryName, - title = resourceReference(R.string.send_recipient_label), - userWalletId = params.userWalletId, - cryptoCurrency = params.currency, - callback = model, - onBackClick = ::onChildBack, - onNextClick = { - val nextRoute = if (model.uiState.value.isRedesignEnabled) { - CommonSendRoute.Confirm - } else { - CommonSendRoute.Amount(isEditMode = false) - } - innerRouter.safeNextClick( - currentRoute = route, - nextRoute = nextRoute, - popBack = ::onChildBack, - childStack = childStack, - ) - }, - ), - ) + private fun getDestinationComponent(factoryContext: AppComponentContext): DefaultSendDestinationComponent = + DefaultSendDestinationComponent( + appComponentContext = factoryContext, + params = SendDestinationComponentParams.DestinationParams( + state = model.uiState.value.destinationUM, + currentRoute = model.currentRoute.filterIsInstance(), + isBalanceHidingFlow = model.isBalanceHiddenFlow, + analyticsCategoryName = model.analyticCategoryName, + title = resourceReference(R.string.send_recipient_label), + userWalletId = params.userWalletId, + cryptoCurrency = params.currency, + callback = model, + ), + ) - private fun getAmountComponent( - factoryContext: AppComponentContext, - route: CommonSendRoute, - ): ComposableContentComponent { + private fun getAmountComponent(factoryContext: AppComponentContext): ComposableContentComponent { return SendAmountComponent( appComponentContext = factoryContext, params = SendAmountComponentParams.AmountParams( state = model.uiState.value.amountUM, - currentRoute = currentRoute.filterIsInstance(), + currentRoute = model.currentRoute.asStateFlow(), isBalanceHidingFlow = model.isBalanceHiddenFlow, - analyticsCategoryName = analyticCategoryName, + analyticsCategoryName = model.analyticCategoryName, appCurrency = model.appCurrency, userWalletId = params.userWalletId, cryptoCurrency = params.currency, cryptoCurrencyStatusFlow = model.cryptoCurrencyStatusFlow, callback = model, predefinedValues = model.predefinedValues, - onBackClick = { - if (route.isEditMode) { - onChildBack() - } else { - analyticsEventHandler.send( - CommonSendAnalyticEvents.CloseButtonClicked( - categoryName = analyticCategoryName, - source = SendScreenSource.Amount, - isFromSummary = false, - isValid = model.uiState.value.amountUM.isPrimaryButtonEnabled, - ), - ) - if (model.uiState.value.isRedesignEnabled) { - onChildBack() - } else { - router.pop() - } - } - }, - onNextClick = { - val nextRoute = if (model.uiState.value.isRedesignEnabled) { - CommonSendRoute.Destination(isEditMode = false) - } else { - CommonSendRoute.Confirm - } - innerRouter.safeNextClick( - currentRoute = route, - nextRoute = nextRoute, - popBack = ::onChildBack, - childStack = childStack, - ) - }, isRedesignEnabled = model.uiState.value.isRedesignEnabled, ), ) @@ -246,16 +183,13 @@ internal class DefaultSendComponent @AssistedInject constructor( val cryptoCurrencyStatus = model.cryptoCurrencyStatusFlow.value val feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatusFlow.value - return if (sendAmount != null && destinationAddress != null && - feeCryptoCurrencyStatus != null && cryptoCurrencyStatus != null - ) { - // TODO Apply new component [REDACTED_TASK_KEY] + return if (sendAmount != null && destinationAddress != null) { SendFeeComponent( appComponentContext = factoryContext, params = SendFeeComponentParams.FeeParams( state = model.uiState.value.feeUM, - currentRoute = currentRoute.filterIsInstance(), - analyticsCategoryName = analyticCategoryName, + currentRoute = model.currentRoute.filterIsInstance(), + analyticsCategoryName = model.analyticCategoryName, userWallet = model.userWallet, cryptoCurrencyStatus = cryptoCurrencyStatus, feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, @@ -264,7 +198,6 @@ internal class DefaultSendComponent @AssistedInject constructor( sendAmount = sendAmount, destinationAddress = destinationAddress, callback = model, - onNextClick = ::onChildBack, ), ) } else { @@ -285,9 +218,9 @@ internal class DefaultSendComponent @AssistedInject constructor( params = SendConfirmComponent.Params( state = model.uiState.value, userWallet = model.userWallet, - currentRoute = currentRoute, + currentRoute = model.currentRoute, isBalanceHidingFlow = model.isBalanceHiddenFlow, - analyticsCategoryName = analyticCategoryName, + analyticsCategoryName = model.analyticCategoryName, cryptoCurrencyStatus = cryptoCurrencyStatus, feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, cryptoCurrencyStatusFlow = model.cryptoCurrencyStatusFlow, @@ -329,7 +262,7 @@ internal class DefaultSendComponent @AssistedInject constructor( appComponentContext = child("sendConfirmDestinationBlock"), params = SendDestinationComponentParams.DestinationBlockParams( state = model.uiState.value.destinationUM, - analyticsCategoryName = analyticCategoryName, + analyticsCategoryName = model.analyticCategoryName, userWalletId = model.userWallet.walletId, cryptoCurrency = cryptoCurrencyStatus.currency, blockClickEnableFlow = MutableStateFlow(true), @@ -343,7 +276,7 @@ internal class DefaultSendComponent @AssistedInject constructor( appComponentContext = child("sendConfirmFeeBlock"), params = SendFeeComponentParams.FeeBlockParams( state = model.uiState.value.feeUM, - analyticsCategoryName = analyticCategoryName, + analyticsCategoryName = model.analyticCategoryName, userWallet = model.userWallet, cryptoCurrencyStatus = cryptoCurrencyStatus, feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, @@ -363,8 +296,8 @@ internal class DefaultSendComponent @AssistedInject constructor( sendUMFlow = model.uiState, feeBlockComponent = feeBlockComponent, destinationBlockComponent = destinationBlockComponent, - analyticsCategoryName = analyticCategoryName, - currentRoute = currentRoute, + analyticsCategoryName = model.analyticCategoryName, + currentRoute = model.currentRoute, txUrl = txUrl, callback = model, ), diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt index a0e2d3dec6..fbb7d77387 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt @@ -7,6 +7,7 @@ import arrow.core.left import com.tangem.blockchain.common.transaction.TransactionFee 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.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -22,6 +23,9 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.isMultiCurrency +import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.qrscanning.models.SourceType import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase @@ -33,9 +37,7 @@ import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase import com.tangem.domain.utils.convertToSdkAmount -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.isMultiCurrency -import com.tangem.domain.models.wallet.requireColdWallet +import com.tangem.domain.wallets.models.GetUserWalletError import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.send.v2.api.SendComponent import com.tangem.features.send.v2.api.SendFeatureToggles @@ -44,8 +46,10 @@ import com.tangem.features.send.v2.api.entity.PredefinedValues import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponent import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.v2.common.CommonSendRoute -import com.tangem.domain.wallets.models.GetUserWalletError +import com.tangem.features.send.v2.common.CommonSendRoute.* import com.tangem.features.send.v2.common.SendConfirmAlertFactory +import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents +import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.send.confirm.SendConfirmComponent import com.tangem.features.send.v2.send.success.SendConfirmSuccessComponent @@ -73,7 +77,7 @@ internal interface SendComponentCallback : @Stable @ModelScoped -@Suppress("LongParameterList") +@Suppress("LongParameterList", "LargeClass") internal class SendModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, @@ -93,16 +97,31 @@ internal class SendModel @Inject constructor( private val getFeeUseCase: GetFeeUseCase, private val sendAmountUpdateTrigger: SendAmountUpdateTrigger, private val sendFeatureToggles: SendFeatureToggles, + private val analyticsEventHandler: AnalyticsEventHandler, ) : Model(), SendComponentCallback { private val params: SendComponent.Params = paramsContainer.require() private val cryptoCurrency = params.currency - private val _uiState = MutableStateFlow(initialState()) - val uiState = _uiState.asStateFlow() + val analyticCategoryName = CommonSendAnalyticEvents.SEND_CATEGORY - private val _isBalanceHiddenFlow = MutableStateFlow(false) - val isBalanceHiddenFlow = _isBalanceHiddenFlow.asStateFlow() + val uiState: StateFlow + field = MutableStateFlow(initialState()) + + val isBalanceHiddenFlow: StateFlow + field = MutableStateFlow(false) + + val initialRoute = if (params.amount == null) { + if (uiState.value.isRedesignEnabled) { + Amount(isEditMode = false) + } else { + Destination(isEditMode = false) + } + } else { + Empty + } + + val currentRoute = MutableStateFlow(initialRoute) private val _cryptoCurrencyStatusFlow = MutableStateFlow( CryptoCurrencyStatus( @@ -135,24 +154,76 @@ internal class SendModel @Inject constructor( } override fun onNavigationResult(navigationUM: NavigationUM) { - _uiState.update { it.copy(navigationUM = navigationUM) } + uiState.update { it.copy(navigationUM = navigationUM) } } override fun onDestinationResult(destinationUM: DestinationUM) { - _uiState.update { it.copy(destinationUM = destinationUM) } + uiState.update { it.copy(destinationUM = destinationUM) } } override fun onAmountResult(amountUM: AmountState, isResetPredefined: Boolean) { if (isResetPredefined) resetPredefinedAmount() - _uiState.update { it.copy(amountUM = amountUM) } + uiState.update { it.copy(amountUM = amountUM) } } override fun onFeeResult(feeUM: FeeUM) { - _uiState.update { it.copy(feeUM = feeUM) } + uiState.update { it.copy(feeUM = feeUM) } } override fun onResult(sendUM: SendUM) { - _uiState.update { sendUM } + uiState.update { sendUM } + } + + override fun onBackClick() { + val isRedesignEnabled = uiState.value.isRedesignEnabled + + when (val route = currentRoute.value) { + is Amount -> if (isRedesignEnabled && !route.isEditMode) { + analyticsEventHandler.send( + CommonSendAnalyticEvents.CloseButtonClicked( + categoryName = analyticCategoryName, + source = SendScreenSource.Amount, + isFromSummary = false, + isValid = uiState.value.amountUM.isPrimaryButtonEnabled, + ), + ) + } + is Destination -> if (isRedesignEnabled && !route.isEditMode) { + analyticsEventHandler.send( + CommonSendAnalyticEvents.CloseButtonClicked( + categoryName = analyticCategoryName, + source = SendScreenSource.Address, + isFromSummary = false, + isValid = uiState.value.amountUM.isPrimaryButtonEnabled, + ), + ) + } + else -> Unit + } + + router.pop() + } + + override fun onNextClick() { + val isRedesignEnabled = uiState.value.isRedesignEnabled + if (currentRoute.value.isEditMode) { + onBackClick() + } else { + when (currentRoute.value) { + is Amount -> if (isRedesignEnabled) { + router.push(Destination(isEditMode = false)) + } else { + router.push(Confirm) + } + is Destination -> if (isRedesignEnabled) { + router.push(Confirm) + } else { + router.push(Amount(isEditMode = false)) + } + Confirm -> router.push(ConfirmSuccess) + else -> onBackClick() + } + } } override fun onConvertToAnotherToken(lastAmount: String) { @@ -269,7 +340,7 @@ internal class SendModel @Inject constructor( .conflate() .distinctUntilChanged() .onEach { - _isBalanceHiddenFlow.value = it.isBalanceHidden + isBalanceHiddenFlow.value = it.isBalanceHidden } .launchIn(modelScope) .saveIn(balanceHidingJobHolder) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt index 05ca4dddfb..60fe6511a2 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/DefaultNFTSendComponent.kt @@ -20,23 +20,21 @@ import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.extensions.resourceReference import com.tangem.features.nft.component.NFTDetailsBlockComponent import com.tangem.features.send.v2.api.NFTSendComponent +import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams +import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.common.ui.SendContent import com.tangem.features.send.v2.common.ui.state.ConfirmUM -import com.tangem.features.send.v2.common.utils.safeNextClick import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.sendnft.confirm.NFTSendConfirmComponent import com.tangem.features.send.v2.sendnft.model.NFTSendModel import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationComponent -import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams -import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponent import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.launch import java.math.BigDecimal @@ -56,16 +54,13 @@ internal class DefaultNFTSendComponent @AssistedInject constructor( popCallback = { onChildBack() }, ) - private val initialRoute = CommonSendRoute.Empty - private val currentRouteFlow = MutableStateFlow(initialRoute) - private val model: NFTSendModel = getOrCreateModel(params = params, router = innerRouter) private val childStack = childStack( key = "NFTSendInnerStack", source = stackNavigation, serializer = null, - initialConfiguration = initialRoute, + initialConfiguration = model.initialRoute, handleBackButton = true, childFactory = { configuration, factoryContext -> createChild( @@ -89,7 +84,7 @@ internal class DefaultNFTSendComponent @AssistedInject constructor( analyticsEventHandler.send( CommonSendAnalyticEvents.ConfirmationScreenOpened(categoryName = analyticsCategoryName), ) - if (currentRouteFlow.value.isEditMode) { + if (model.currentRouteFlow.value.isEditMode) { activeComponent.updateState(model.uiState.value) } } @@ -105,7 +100,7 @@ internal class DefaultNFTSendComponent @AssistedInject constructor( ) } } - currentRouteFlow.emit(stack.active.configuration) + model.currentRouteFlow.emit(stack.active.configuration) } } } @@ -123,37 +118,26 @@ internal class DefaultNFTSendComponent @AssistedInject constructor( } private fun createChild(route: CommonSendRoute, factoryContext: AppComponentContext) = when (route) { - is CommonSendRoute.Destination -> getDestinationComponent(factoryContext, route) + is CommonSendRoute.Destination -> getDestinationComponent(factoryContext) is CommonSendRoute.Fee -> getFeeComponent(factoryContext) CommonSendRoute.Confirm -> getConfirmComponent(factoryContext) else -> getStubComponent() } - private fun getDestinationComponent( - factoryContext: AppComponentContext, - route: CommonSendRoute, - ): DefaultSendDestinationComponent = DefaultSendDestinationComponent( - appComponentContext = factoryContext, - params = SendDestinationComponentParams.DestinationParams( - state = model.uiState.value.destinationUM, - currentRoute = currentRouteFlow.filterIsInstance(), - isBalanceHidingFlow = model.isBalanceHiddenFlow, - title = resourceReference(R.string.nft_send), - analyticsCategoryName = analyticsCategoryName, - userWalletId = params.userWalletId, - cryptoCurrency = model.cryptoCurrency, - callback = model, - onBackClick = ::onChildBack, - onNextClick = { - innerRouter.safeNextClick( - currentRoute = route, - nextRoute = CommonSendRoute.Confirm, - popBack = ::onChildBack, - childStack = childStack, - ) - }, - ), - ) + private fun getDestinationComponent(factoryContext: AppComponentContext): DefaultSendDestinationComponent = + DefaultSendDestinationComponent( + appComponentContext = factoryContext, + params = SendDestinationComponentParams.DestinationParams( + state = model.uiState.value.destinationUM, + currentRoute = model.currentRouteFlow.filterIsInstance(), + isBalanceHidingFlow = model.isBalanceHiddenFlow, + title = resourceReference(R.string.nft_send), + analyticsCategoryName = analyticsCategoryName, + userWalletId = params.userWalletId, + cryptoCurrency = model.cryptoCurrency, + callback = model, + ), + ) private fun getFeeComponent(factoryContext: AppComponentContext): ComposableContentComponent { val state = model.uiState.value @@ -163,7 +147,7 @@ internal class DefaultNFTSendComponent @AssistedInject constructor( appComponentContext = factoryContext, params = SendFeeComponentParams.FeeParams( state = model.uiState.value.feeUM, - currentRoute = currentRouteFlow.filterIsInstance(), + currentRoute = model.currentRouteFlow.filterIsInstance(), analyticsCategoryName = analyticsCategoryName, userWallet = model.userWallet, cryptoCurrencyStatus = model.cryptoCurrencyStatus, @@ -173,7 +157,6 @@ internal class DefaultNFTSendComponent @AssistedInject constructor( onLoadFee = model::loadFee, destinationAddress = destinationAddress, callback = model, - onNextClick = ::onChildBack, ), ) } else { @@ -194,7 +177,7 @@ internal class DefaultNFTSendComponent @AssistedInject constructor( feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatus, appCurrency = model.appCurrency, callback = model, - currentRoute = currentRouteFlow.filterIsInstance(), + currentRoute = model.currentRouteFlow.filterIsInstance(), isBalanceHidingFlow = model.isBalanceHiddenFlow, onLoadFee = model::loadFee, ), diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt index 7bf5522ffc..06a09841fe 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/sendnft/model/NFTSendModel.kt @@ -20,18 +20,19 @@ import com.tangem.domain.feedback.SendFeedbackEmailUseCase import com.tangem.domain.feedback.models.BlockchainErrorInfo import com.tangem.domain.feedback.models.FeedbackEmailType import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.tokens.* import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.requireColdWallet import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.send.v2.api.NFTSendComponent import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponent import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.v2.common.CommonSendRoute +import com.tangem.features.send.v2.common.CommonSendRoute.* import com.tangem.features.send.v2.common.SendConfirmAlertFactory import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.sendnft.confirm.NFTSendConfirmComponent @@ -72,14 +73,19 @@ internal class NFTSendModel @Inject constructor( ) : Model(), SendNFTComponentCallback { val params: NFTSendComponent.Params = paramsContainer.require() + + val initialRoute = Empty + + val currentRouteFlow = MutableStateFlow(initialRoute) + private val userWalletId = params.userWalletId private val nftAsset = params.nftAsset - private val _uiState = MutableStateFlow(initialState()) - val uiState = _uiState.asStateFlow() + val uiState: StateFlow + field = MutableStateFlow(initialState()) - private val _isBalanceHiddenFlow = MutableStateFlow(false) - val isBalanceHiddenFlow = _isBalanceHiddenFlow.asStateFlow() + val isBalanceHiddenFlow: StateFlow + field = MutableStateFlow(false) var cryptoCurrency: CryptoCurrency by Delegates.notNull() var userWallet: UserWallet by Delegates.notNull() @@ -93,19 +99,35 @@ internal class NFTSendModel @Inject constructor( } override fun onNavigationResult(navigationUM: NavigationUM) { - _uiState.update { it.copy(navigationUM = navigationUM) } + uiState.update { it.copy(navigationUM = navigationUM) } } override fun onResult(nftSendUM: NFTSendUM) { - _uiState.value = nftSendUM + uiState.value = nftSendUM } override fun onDestinationResult(destinationUM: DestinationUM) { - _uiState.update { it.copy(destinationUM = destinationUM) } + uiState.update { it.copy(destinationUM = destinationUM) } } override fun onFeeResult(feeUM: FeeUM) { - _uiState.update { it.copy(feeUM = feeUM) } + uiState.update { it.copy(feeUM = feeUM) } + } + + override fun onBackClick() { + router.pop() + } + + override fun onNextClick() { + if (currentRouteFlow.value.isEditMode) { + onBackClick() + } else { + when (currentRouteFlow.value) { + is Destination -> router.push(Confirm) + Confirm -> router.push(ConfirmSuccess) + else -> onBackClick() + } + } } suspend fun loadFee(): Either { diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt index 4d51a47f1a..d212be79aa 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/SendAmountComponentParams.kt @@ -9,7 +9,6 @@ import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.api.entity.PredefinedValues import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent.ModelCallback -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.StateFlow internal sealed class SendAmountComponentParams { @@ -33,10 +32,8 @@ internal sealed class SendAmountComponentParams { override val cryptoCurrency: CryptoCurrency, override val cryptoCurrencyStatusFlow: StateFlow, val callback: ModelCallback, - val currentRoute: Flow, + val currentRoute: StateFlow, val isBalanceHidingFlow: StateFlow, - val onBackClick: () -> Unit, - val onNextClick: () -> Unit, ) : SendAmountComponentParams() data class AmountBlockParams( diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt index d991cd47db..fc6affca28 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/amount/model/SendAmountModel.kt @@ -31,6 +31,7 @@ import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.send.v2.api.SendFeatureToggles import com.tangem.features.send.v2.api.entity.PredefinedValues import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger +import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceListener @@ -48,7 +49,7 @@ import kotlinx.coroutines.launch import javax.inject.Inject import kotlin.properties.Delegates -@Suppress("LongParameterList") +@Suppress("LongParameterList", "LargeClass") @Stable @ModelScoped internal class SendAmountModel @Inject constructor( @@ -319,7 +320,7 @@ internal class SendAmountModel @Inject constructor( val params = params as? SendAmountComponentParams.AmountParams ?: return combine( flow = uiState, - flow2 = params.currentRoute, + flow2 = params.currentRoute.filterIsInstance(), transform = { state, route -> state to route }, ).onEach { (state, route) -> isSendWithSwapAvailable.update { @@ -335,7 +336,7 @@ internal class SendAmountModel @Inject constructor( } else { R.drawable.ic_close_24 }, - backIconClick = { params.onBackClick() }, + backIconClick = params.callback::onBackClick, primaryButton = NavigationButton( textReference = if (route.isEditMode) { resourceReference(R.string.common_continue) @@ -345,7 +346,7 @@ internal class SendAmountModel @Inject constructor( isEnabled = state.isPrimaryButtonEnabled, onClick = { onAmountNext() - params.onNextClick() + params.callback.onNextClick() }, ), prevButton = if (params.isRedesignEnabled) { @@ -357,7 +358,7 @@ internal class SendAmountModel @Inject constructor( isEnabled = true, onClick = { saveResult() - params.onBackClick() + params.callback.onBackClick() }, ).takeIf { route.isEditMode.not() } }, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt index 94f278f768..6fdf7e3b09 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt @@ -10,9 +10,11 @@ import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router -import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.models.network.CryptoCurrencyAddress +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.wallet.isLocked +import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.qrscanning.models.SourceType import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase @@ -22,9 +24,6 @@ import com.tangem.domain.transaction.usecase.IsUtxoConsolidationAvailableUseCase import com.tangem.domain.transaction.usecase.ValidateWalletAddressUseCase import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase import com.tangem.domain.txhistory.usecase.GetFixedTxHistoryItemsUseCase -import com.tangem.domain.models.wallet.UserWallet -import com.tangem.domain.models.wallet.isLocked -import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.features.send.v2.api.SendFeatureToggles import com.tangem.features.send.v2.api.entity.PredefinedValues @@ -285,7 +284,7 @@ internal class SendDestinationModel @Inject constructor( val isRecent = type == EnterAddressSource.RecentAddress if (isRecent && isValidAddress && isValidMemo) { saveResult() - (params as? SendDestinationComponentParams.DestinationParams)?.onNextClick?.invoke() + (params as? SendDestinationComponentParams.DestinationParams)?.callback?.onNextClick() } } @@ -323,7 +322,7 @@ internal class SendDestinationModel @Inject constructor( ), ) } - params.onBackClick() + params.callback.onBackClick() }, additionalIconRes = if (isRedesignEnabled) { null @@ -344,22 +343,9 @@ internal class SendDestinationModel @Inject constructor( isEnabled = state.isPrimaryButtonEnabled, onClick = { saveResult() - params.onNextClick() + params.callback.onNextClick() }, ), - prevButton = if (!route.isEditMode && !isRedesignEnabled) { - NavigationButton( - textReference = TextReference.EMPTY, - iconRes = R.drawable.ic_back_24, - isEnabled = true, - onClick = { - saveResult() - params.onBackClick() - }, - ) - } else { - null - }, secondaryPairButtonsUM = null, ), ) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/SendFeeComponentParams.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/SendFeeComponentParams.kt index fe2add6083..bdf1038ebe 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/SendFeeComponentParams.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/SendFeeComponentParams.kt @@ -36,7 +36,6 @@ internal sealed class SendFeeComponentParams { override val onLoadFee: suspend () -> Either, val currentRoute: Flow, val callback: SendFeeComponent.ModelCallback, - val onNextClick: () -> Unit, ) : SendFeeComponentParams() data class FeeBlockParams( diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/SendFeeModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/SendFeeModel.kt index a42481308d..e1f722720f 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/SendFeeModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/fee/model/SendFeeModel.kt @@ -213,7 +213,7 @@ internal class SendFeeModel @Inject constructor( ) val params = params as? SendFeeComponentParams.FeeParams ?: return params.callback.onFeeResult(uiState.value) - params.onNextClick() + params.callback.onNextClick() } private fun checkLoadFee() { @@ -284,7 +284,7 @@ internal class SendFeeModel @Inject constructor( title = resourceReference(R.string.common_fee_selector_title), subtitle = null, backIconRes = R.drawable.ic_back_24, - backIconClick = params.onNextClick, + backIconClick = params.callback::onNextClick, primaryButton = NavigationButton( textReference = resourceReference(R.string.common_continue), isEnabled = state.isPrimaryButtonEnabled, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponent.kt index b987f350eb..8105a965b0 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountComponent.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.common.ui.navigationButtons.NavigationModelCallback import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent @@ -12,7 +13,6 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM import com.tangem.features.swap.v2.impl.amount.model.SwapAmountModel import com.tangem.features.swap.v2.impl.amount.ui.SwapAmountContent -import com.tangem.features.swap.v2.impl.common.SwapNavigationModelCallback import dagger.assisted.Assisted import dagger.assisted.AssistedInject @@ -36,7 +36,7 @@ internal class SwapAmountComponent @AssistedInject constructor( ) } - interface ModelCallback : SwapNavigationModelCallback { + interface ModelCallback : NavigationModelCallback { fun onAmountResult(amountUM: SwapAmountUM) fun onSeparatorClick(lastAmount: String) } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapNavigationModelCallback.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapNavigationModelCallback.kt deleted file mode 100644 index 4462691230..0000000000 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/common/SwapNavigationModelCallback.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.features.swap.v2.impl.common - -import com.tangem.common.ui.navigationButtons.NavigationUM - -internal interface SwapNavigationModelCallback { - fun onNavigationResult(navigationUM: NavigationUM) - fun onBackClick() - fun onNextClick() -} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt index a400a6fd54..c3c1dbbe36 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt @@ -148,8 +148,6 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor( userWalletId = params.userWalletId, cryptoCurrency = secondaryCryptoCurrency, callback = model, - onBackClick = ::onChildBack, - onNextClick = model::onNextClick, ), ) }