From adaa72f359d491ea0e7c659bdffa5178be6f8c64 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 25 Apr 2024 14:12:17 +0500 Subject: [PATCH 1/4] Updated on 2026-08-14 --- .../impl/presentation/state/SendUiState.kt | 56 +++++++++- .../impl/presentation/state/StateRouter.kt | 47 ++++++-- .../state/common/SendSyncEditConverter.kt | 26 +++++ .../state/previewdata/SendClickIntentsStub.kt | 2 +- .../send/impl/presentation/ui/SendScreen.kt | 103 +++++++++++------- .../viewmodel/SendClickIntents.kt | 2 +- 6 files changed, 184 insertions(+), 52 deletions(-) create mode 100644 features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/common/SendSyncEditConverter.kt diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt index 043f45837f..23453c4b4d 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt @@ -28,9 +28,58 @@ internal data class SendUiState( val recipientState: SendStates.RecipientState? = null, val feeState: SendStates.FeeState? = null, val sendState: SendStates.SendState? = null, + val editAmountState: SendStates.AmountState? = null, + val editRecipientState: SendStates.RecipientState? = null, + val editFeeState: SendStates.FeeState? = null, val isBalanceHidden: Boolean, val event: StateEvent, -) +) { + + fun getAmountState(isEditState: Boolean): SendStates.AmountState? { + return if (isEditState) { + editAmountState + } else { + amountState + } + } + + fun getRecipientState(isEditState: Boolean): SendStates.RecipientState? { + return if (isEditState) { + editRecipientState + } else { + recipientState + } + } + + fun getFeeState(isEditState: Boolean): SendStates.FeeState? { + return if (isEditState) { + editFeeState + } else { + feeState + } + } + + fun copyWrapped( + isEditState: Boolean, + amountState: SendStates.AmountState? = this.amountState, + feeState: SendStates.FeeState? = this.feeState, + recipientState: SendStates.RecipientState? = this.recipientState, + sendState: SendStates.SendState? = this.sendState, + ): SendUiState = if (isEditState) { + copy( + editAmountState = amountState, + editFeeState = feeState, + editRecipientState = recipientState, + ) + } else { + copy( + amountState = amountState, + feeState = feeState, + recipientState = recipientState, + sendState = sendState, + ) + } +} @Stable internal sealed class SendStates { @@ -105,6 +154,9 @@ enum class SendUiStateType { None, Recipient, Amount, - Send, Fee, + Send, + EditAmount, + EditRecipient, + EditFee, } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt index 079dbfdb6f..5f2201052b 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt @@ -18,6 +18,9 @@ internal class StateRouter( val currentState: StateFlow get() = mutableCurrentState + val isEditState: Boolean + get() = currentState.value.isFromConfirmation + fun clear() { mutableCurrentState.update { getInitState() } } @@ -35,21 +38,29 @@ internal class StateRouter( else -> popBackStack() } else -> when (type) { - SendUiStateType.Amount -> continueToSend(::showRecipient) + SendUiStateType.Amount -> showRecipient() SendUiStateType.Fee -> showSend() - SendUiStateType.Send -> continueToSend(::showAmount) - else -> continueToSend(::popBackStack) + SendUiStateType.Send -> showAmount() + SendUiStateType.Recipient -> popBackStack() + SendUiStateType.EditAmount -> showSend() + SendUiStateType.EditRecipient -> showSend() + SendUiStateType.EditFee -> showSend() + else -> popBackStack() } } } fun onNextClick() { when (currentState.value.type) { - SendUiStateType.Recipient -> continueToSend(::showAmount) + SendUiStateType.Recipient -> showAmount() SendUiStateType.Amount, SendUiStateType.Fee, + SendUiStateType.EditAmount, + SendUiStateType.EditRecipient, + SendUiStateType.EditFee, -> showSend() SendUiStateType.Send -> onBackClick() + else -> popBackStack() } } @@ -67,17 +78,35 @@ internal class StateRouter( fun showAmount(isFromConfirmation: Boolean = false) { analyticsEventsHandler.send(SendAnalyticEvents.AmountScreenOpened) - mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Amount, isFromConfirmation) } + mutableCurrentState.update { + if (isFromConfirmation) { + SendUiCurrentScreen(SendUiStateType.EditAmount, true) + } else { + SendUiCurrentScreen(SendUiStateType.Amount, false) + } + } } fun showRecipient(isFromConfirmation: Boolean = false) { analyticsEventsHandler.send(SendAnalyticEvents.AddressScreenOpened) - mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Recipient, isFromConfirmation) } + mutableCurrentState.update { + if (isFromConfirmation) { + SendUiCurrentScreen(SendUiStateType.EditRecipient, true) + } else { + SendUiCurrentScreen(SendUiStateType.Recipient, false) + } + } } fun showFee(isFromConfirmation: Boolean = false) { analyticsEventsHandler.send(SendAnalyticEvents.FeeScreenOpened) - mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Fee, isFromConfirmation) } + mutableCurrentState.update { + if (isFromConfirmation) { + SendUiCurrentScreen(SendUiStateType.EditFee, true) + } else { + SendUiCurrentScreen(SendUiStateType.Fee, false) + } + } } fun showSend() { @@ -85,10 +114,6 @@ internal class StateRouter( mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Send, isFromConfirmation = false) } } - private fun continueToSend(show: () -> Unit) { - if (currentState.value.isFromConfirmation) showSend() else show() - } - private fun getInitState() = if (isEditingDisabled) { SendUiCurrentScreen( type = SendUiStateType.None, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/common/SendSyncEditConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/common/SendSyncEditConverter.kt new file mode 100644 index 0000000000..f0abbba036 --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/common/SendSyncEditConverter.kt @@ -0,0 +1,26 @@ +package com.tangem.features.send.impl.presentation.state.common + +import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.utils.Provider +import com.tangem.utils.converter.Converter + +internal class SendSyncEditConverter( + private val currentStateProvider: Provider, +) : Converter { + override fun convert(value: Boolean): SendUiState { + val state = currentStateProvider() + return if (value) { + state.copy( + amountState = state.editAmountState, + feeState = state.editFeeState, + recipientState = state.editRecipientState, + ) + } else { + state.copy( + editAmountState = state.amountState, + editRecipientState = state.recipientState, + editFeeState = state.feeState, + ) + } + } +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/SendClickIntentsStub.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/SendClickIntentsStub.kt index 5235588e51..0a113ea474 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/SendClickIntentsStub.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/SendClickIntentsStub.kt @@ -14,7 +14,7 @@ internal object SendClickIntentsStub : SendClickIntents { override fun onBackClick() {} - override fun onNextClick() {} + override fun onNextClick(isFromEdit: Boolean) {} override fun onPrevClick() {} diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt index ffa3bf5d06..b289867885 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt @@ -1,8 +1,11 @@ package com.tangem.features.send.impl.presentation.ui import androidx.activity.compose.BackHandler -import androidx.compose.animation.* +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.core.tween +import androidx.compose.animation.togetherWith import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.material3.SnackbarHostState @@ -23,12 +26,13 @@ import com.tangem.features.send.impl.presentation.ui.fee.SendSpeedAndFeeContent import com.tangem.features.send.impl.presentation.ui.recipient.SendRecipientContent import com.tangem.features.send.impl.presentation.ui.send.SendContent import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.withIndex @Composable internal fun SendScreen(uiState: SendUiState, currentState: SendUiCurrentScreen) { val snackbarHostState = remember { SnackbarHostState() } - val sendState = uiState.sendState ?: return BackHandler { uiState.clickIntents.onBackClick() } Column( modifier = Modifier @@ -38,38 +42,9 @@ internal fun SendScreen(uiState: SendUiState, currentState: SendUiCurrentScreen) .background(color = TangemTheme.colors.background.tertiary), horizontalAlignment = Alignment.CenterHorizontally, ) { - val titleRes = when (currentState.type) { - SendUiStateType.Amount -> resourceReference(R.string.send_amount_label) - SendUiStateType.Recipient -> resourceReference(R.string.send_recipient_label) - SendUiStateType.Fee -> resourceReference(R.string.common_fee_selector_title) - SendUiStateType.Send -> if (!sendState.isSuccess) { - resourceReference(R.string.send_summary_title, wrappedList(uiState.cryptoCurrencyName)) - } else { - null - } - else -> null - } - val isSending = currentState.type == SendUiStateType.Send && !uiState.sendState.isSuccess - val subtitleRes = if (isSending) { - uiState.amountState?.walletName - } else { - null - } - val iconRes = if (currentState.type == SendUiStateType.Recipient) { - R.drawable.ic_qrcode_scan_24 - } else { - null - } - - AppBarWithBackButtonAndIcon( - text = titleRes?.resolveReference(), - subtitle = subtitleRes, - onBackClick = uiState.clickIntents::popBackStack, - onIconClick = uiState.clickIntents::onQrCodeScanClick, - backIconRes = R.drawable.ic_close_24, - iconRes = iconRes, - backgroundColor = TangemTheme.colors.background.tertiary, - modifier = Modifier.height(TangemTheme.dimens.size56), + SendAppBar( + uiState = uiState, + currentState = currentState, ) SendScreenContent( uiState = uiState, @@ -88,6 +63,50 @@ internal fun SendScreen(uiState: SendUiState, currentState: SendUiCurrentScreen) ) } +@Composable +private fun SendAppBar(uiState: SendUiState, currentState: SendUiCurrentScreen) { + val (titleRes, subtitleRes) = when (currentState.type) { + SendUiStateType.Amount, + SendUiStateType.EditAmount, + -> resourceReference(R.string.send_amount_label) to null + SendUiStateType.Recipient, + SendUiStateType.EditRecipient, + -> resourceReference(R.string.send_recipient_label) to null + SendUiStateType.Fee, + SendUiStateType.EditFee, + -> resourceReference(R.string.common_fee_selector_title) to null + SendUiStateType.Send -> if (uiState.sendState?.isSuccess == false) { + resourceReference(R.string.send_summary_title, wrappedList(uiState.cryptoCurrencyName)) to + uiState.amountState?.walletName + } else { + null to null + } + else -> null to null + } + val iconRes = if (currentState.type == SendUiStateType.Recipient) { + R.drawable.ic_qrcode_scan_24 + } else { + null + } + val (backIcon, backClick) = when (currentState.type) { + SendUiStateType.EditAmount, + SendUiStateType.EditFee, + SendUiStateType.EditRecipient, + -> R.drawable.ic_back_24 to uiState.clickIntents::onBackClick + else -> R.drawable.ic_close_24 to uiState.clickIntents::popBackStack + } + AppBarWithBackButtonAndIcon( + text = titleRes?.resolveReference(), + subtitle = subtitleRes, + onBackClick = backClick, + onIconClick = uiState.clickIntents::onQrCodeScanClick, + backIconRes = backIcon, + iconRes = iconRes, + backgroundColor = TangemTheme.colors.background.tertiary, + modifier = Modifier.height(TangemTheme.dimens.size56), + ) +} + @OptIn(ExperimentalAnimationApi::class) @Composable private fun SendScreenContent(uiState: SendUiState, currentState: SendUiCurrentScreen, modifier: Modifier = Modifier) { @@ -135,13 +154,23 @@ private fun SendScreenContent(uiState: SendUiState, currentState: SendUiCurrentS isBalanceHiding = uiState.isBalanceHidden, clickIntents = uiState.clickIntents, ) + SendUiStateType.EditAmount -> SendAmountContent( + amountState = uiState.editAmountState, + isBalanceHiding = uiState.isBalanceHidden, + clickIntents = uiState.clickIntents, + ) SendUiStateType.Recipient -> SendRecipientContent( uiState = uiState.recipientState, clickIntents = uiState.clickIntents, isBalanceHidden = uiState.isBalanceHidden, ) - SendUiStateType.Fee -> SendSpeedAndFeeContent( - state = uiState.feeState, + SendUiStateType.EditRecipient -> SendRecipientContent( + uiState = uiState.editRecipientState, + clickIntents = uiState.clickIntents, + isBalanceHidden = uiState.isBalanceHidden, + ) + SendUiStateType.EditFee -> SendSpeedAndFeeContent( + state = uiState.editFeeState, clickIntents = uiState.clickIntents, ) SendUiStateType.Send -> SendContent(uiState) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt index a353059047..f0715cd50a 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt @@ -14,7 +14,7 @@ internal interface SendClickIntents { fun onBackClick() - fun onNextClick() + fun onNextClick(isFromEdit: Boolean = false) fun onPrevClick() From 94623dd8eb529e89b54a17ec581e2cb337af5473 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 25 Apr 2024 14:12:43 +0500 Subject: [PATCH 2/4] Updated on 2026-08-14 --- .../utils/SendOnNextScreenAnalyticSender.kt | 10 ++-- .../state/SendEventStateFactory.kt | 4 +- .../presentation/state/SendStateFactory.kt | 52 +++++++++++++------ .../state/amount/AmountStateFactory.kt | 6 +++ .../amount/SendAmountCurrencyConverter.kt | 8 ++- ...SendAmountPastedTriggerDismissConverter.kt | 8 ++- .../state/confirm/SendNotificationFactory.kt | 9 ++-- .../presentation/state/fee/FeeCalculation.kt | 4 +- .../state/fee/FeeNotificationFactory.kt | 9 ++-- .../presentation/state/fee/FeeStateFactory.kt | 44 ++++++++++------ .../fields/SendAmountFieldChangeConverter.kt | 14 +++-- .../SendAmountFieldMaxAmountConverter.kt | 8 ++- .../presentation/ui/SendNavigationButtons.kt | 45 ++++++++++------ .../presentation/viewmodel/SendViewModel.kt | 41 ++++++++++----- 14 files changed, 176 insertions(+), 86 deletions(-) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt index 6ca1759174..6fefbc87b8 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt @@ -5,18 +5,21 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.features.send.impl.presentation.analytics.SelectedCurrencyType import com.tangem.features.send.impl.presentation.analytics.SelectedFeeType import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents +import com.tangem.features.send.impl.presentation.state.* import com.tangem.features.send.impl.presentation.state.SendUiState -import com.tangem.features.send.impl.presentation.state.SendUiStateType +import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.FeeType +import com.tangem.utils.Provider internal class SendOnNextScreenAnalyticSender( + private val stateRouterProvider: Provider, private val analyticsEventHandler: AnalyticsEventHandler, ) { fun send(prevScreen: SendUiStateType, state: SendUiState) { when (prevScreen) { SendUiStateType.Fee -> { - val feeState = state.feeState ?: return + val feeState = state.getFeeState(stateRouterProvider().isEditState) ?: return val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content feeSelectorState?.selectedFee?.let { selectedFee -> val isCustomFeeEdited = feeState.fee?.amount?.value != feeSelectorState.fees.normal.amount.value @@ -27,7 +30,8 @@ internal class SendOnNextScreenAnalyticSender( } } SendUiStateType.Amount -> { - val isFiatSelected = state.amountState?.amountTextField?.isFiatValue ?: return + val amountState = state.getAmountState(stateRouterProvider().isEditState) ?: return + val isFiatSelected = amountState.amountTextField.isFiatValue val selectedCurrency = if (!isFiatSelected) { SelectedCurrencyType.Token } else { diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt index 616fad69f8..852339a7ed 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt @@ -20,6 +20,7 @@ import java.math.BigDecimal * @param feeStateFactory [FeeStateFactory] */ internal class SendEventStateFactory( + private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, private val clickIntents: SendClickIntents, @@ -50,7 +51,8 @@ internal class SendEventStateFactory( fun getFeeUpdatedAlert(fee: TransactionFee, onConsume: () -> Unit, onFeeNotIncreased: () -> Unit): SendUiState { val state = currentStateProvider() - val feeSelector = state.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return state + val feeState = state.getFeeState(stateRouterProvider().isEditState) + val feeSelector = feeState?.feeSelectorState as? FeeSelectorState.Content ?: return state val newFee = when (fee) { is TransactionFee.Single -> fee.normal is TransactionFee.Choosable -> { diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt index c8d7369fbd..a2b722b614 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt @@ -14,6 +14,7 @@ import com.tangem.domain.wallets.usecase.ValidateWalletMemoUseCase import com.tangem.features.send.impl.R import com.tangem.features.send.impl.presentation.domain.AvailableWallet import com.tangem.features.send.impl.presentation.state.amount.SendAmountStateConverter +import com.tangem.features.send.impl.presentation.state.common.SendSyncEditConverter import com.tangem.features.send.impl.presentation.state.confirm.SendConfirmStateConverter import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.SendFeeStateConverter @@ -31,6 +32,7 @@ import timber.log.Timber @Suppress("LongParameterList") internal class SendStateFactory( private val clickIntents: SendClickIntents, + private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val userWalletProvider: Provider, private val appCurrencyProvider: Provider, @@ -82,7 +84,9 @@ internal class SendStateFactory( cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, ) } - + private val sendSyncEditConverter by lazy(LazyThreadSafetyMode.NONE) { + SendSyncEditConverter(currentStateProvider = currentStateProvider) + } // region UI states fun getInitialState(): SendUiState = SendUiState( clickIntents = clickIntents, @@ -117,6 +121,8 @@ internal class SendStateFactory( ) } + fun syncEditStates(isFromEdit: Boolean) = sendSyncEditConverter.convert(isFromEdit) + fun getOnHideBalanceState(isBalanceHidden: Boolean): SendUiState { return currentStateProvider().copy(isBalanceHidden = isBalanceHidden) } @@ -143,8 +149,10 @@ internal class SendStateFactory( fun onRecipientAddressValueChange(value: String, isXAddress: Boolean = false): SendUiState { val state = currentStateProvider() - val recipientState = state.recipientState ?: return state - return state.copy( + val isEditState = stateRouterProvider().isEditState + val recipientState = state.getRecipientState(isEditState) ?: return state + return state.copyWrapped( + isEditState = isEditState, recipientState = recipientState.copy( addressTextField = recipientState.addressTextField.copy(value = value), memoTextField = recipientState.memoTextField?.copy(isEnabled = !isXAddress), @@ -155,7 +163,8 @@ internal class SendStateFactory( fun getOnRecipientAddressValidState(value: String, isValidAddress: Boolean): SendUiState { val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() val state = currentStateProvider() - val recipientState = state.recipientState ?: return state + val isEditState = stateRouterProvider().isEditState + val recipientState = state.getRecipientState(isEditState) ?: return state val isValidMemo = validateWalletMemoUseCase( memo = recipientState.memoTextField?.value.orEmpty(), @@ -167,7 +176,8 @@ internal class SendStateFactory( val isAddressInWallet = cryptoCurrencyStatus.value.networkAddress?.availableAddresses ?.any { it.value == value } ?: true - return state.copy( + return state.copyWrapped( + isEditState = isEditState, recipientState = recipientState.copy( isPrimaryButtonEnabled = isValidMemo && isValidAddress && !isAddressInWallet, isValidating = false, @@ -185,16 +195,20 @@ internal class SendStateFactory( fun getOnRecipientAddressValidationStarted(): SendUiState { val state = currentStateProvider() - val recipientState = state.recipientState ?: return state - return state.copy( + val isEditState = stateRouterProvider().isEditState + val recipientState = state.getRecipientState(isEditState) ?: return state + return state.copyWrapped( + isEditState = isEditState, recipientState = recipientState.copy(isValidating = true), ) } fun getOnRecipientMemoValueChange(value: String): SendUiState { val state = currentStateProvider() - val recipientState = state.recipientState ?: return state - return state.copy( + val isEditState = stateRouterProvider().isEditState + val recipientState = state.getRecipientState(isEditState) ?: return state + return state.copyWrapped( + isEditState = isEditState, recipientState = recipientState.copy( memoTextField = recipientState.memoTextField?.copy(value = value), ), @@ -204,7 +218,8 @@ internal class SendStateFactory( fun getOnRecipientMemoValidState(value: String, isValidAddress: Boolean): SendUiState { val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() val state = currentStateProvider() - val recipientState = state.recipientState ?: return state + val isEditState = stateRouterProvider().isEditState + val recipientState = state.getRecipientState(isEditState) ?: return state val isValidMemo = validateWalletMemoUseCase( memo = value, @@ -216,7 +231,8 @@ internal class SendStateFactory( val isAddressInWallet = cryptoCurrencyStatus.value.networkAddress?.availableAddresses ?.any { it.value == value } ?: true - return state.copy( + return state.copyWrapped( + isEditState = isEditState, recipientState = recipientState.copy( isPrimaryButtonEnabled = isValidMemo && isValidAddress && !isAddressInWallet, isValidating = false, @@ -230,8 +246,10 @@ internal class SendStateFactory( fun getOnXAddressMemoState(): SendUiState { val state = currentStateProvider() - val recipientState = state.recipientState ?: return state - return state.copy( + val isEditState = stateRouterProvider().isEditState + val recipientState = state.getRecipientState(isEditState) ?: return state + return state.copyWrapped( + isEditState = isEditState, recipientState = recipientState.copy( memoTextField = recipientState.memoTextField?.copy( value = "", @@ -243,9 +261,11 @@ internal class SendStateFactory( fun getHiddenRecentListState(isAddressInWallet: Boolean, isValidAddress: Boolean): SendUiState { val state = currentStateProvider() - val recipientState = state.recipientState ?: return state + val isEditState = stateRouterProvider().isEditState + val recipientState = state.getRecipientState(isEditState) ?: return state val isNotValid = isAddressInWallet || !isValidAddress - return state.copy( + return state.copyWrapped( + isEditState = isEditState, recipientState = recipientState.copy( recent = recipientState.recent.map { recent -> recent.copy(isVisible = isNotValid && (recent.isLoading || recent.title != TextReference.EMPTY)) @@ -305,7 +325,7 @@ internal class SendStateFactory( private fun isPrimaryButtonEnabled(state: SendUiState, notifications: ImmutableList): Boolean { val sendState = state.sendState ?: return false - val feeState = state.feeState ?: return false + val feeState = state.getFeeState(stateRouterProvider().isEditState) ?: return false val hasErrorNotifications = notifications.any { it is SendNotification.Error } return !hasErrorNotifications && !sendState.isSending && feeState.feeSelectorState is FeeSelectorState.Content } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt index 3321568099..e91f30356f 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt @@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.amount import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldChangeConverter import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldMaxAmountConverter import com.tangem.utils.Provider @@ -10,18 +11,21 @@ import com.tangem.utils.Provider * Factory to produce amount state for [SendUiState] */ internal class AmountStateFactory( + private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, ) { private val amountFieldChangeConverter by lazy(LazyThreadSafetyMode.NONE) { SendAmountFieldChangeConverter( + stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, ) } private val amountFieldMaxAmountConverter by lazy(LazyThreadSafetyMode.NONE) { SendAmountFieldMaxAmountConverter( + stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, ) @@ -29,12 +33,14 @@ internal class AmountStateFactory( private val amountCurrencyConverter by lazy(LazyThreadSafetyMode.NONE) { SendAmountCurrencyConverter( + stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, ) } private val amountPasteConverter by lazy(LazyThreadSafetyMode.NONE) { SendAmountPastedTriggerDismissConverter( + stateRouterProvider = stateRouterProvider, currentStateProvider = currentStateProvider, ) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountCurrencyConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountCurrencyConverter.kt index f6ff524e9f..4817557ac6 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountCurrencyConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountCurrencyConverter.kt @@ -2,17 +2,20 @@ package com.tangem.features.send.impl.presentation.state.amount import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import com.tangem.utils.isNullOrZero internal class SendAmountCurrencyConverter( + private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, ) : Converter { override fun convert(value: Boolean): SendUiState { val state = currentStateProvider() - val amountState = state.amountState ?: return state + val isEditState = stateRouterProvider().isEditState + val amountState = state.getAmountState(isEditState) ?: return state val amountTextField = amountState.amountTextField val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() @@ -20,7 +23,8 @@ internal class SendAmountCurrencyConverter( return if (amountTextField.isFiatValue == value && !isValidFiatRate) { state } else { - return state.copy( + return state.copyWrapped( + isEditState = isEditState, amountState = amountState.copy( amountTextField = amountTextField.copy( isFiatValue = value, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountPastedTriggerDismissConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountPastedTriggerDismissConverter.kt index 497629bdd5..093b1b6a8e 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountPastedTriggerDismissConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountPastedTriggerDismissConverter.kt @@ -1,16 +1,20 @@ package com.tangem.features.send.impl.presentation.state.amount import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.utils.Provider import com.tangem.utils.converter.Converter internal class SendAmountPastedTriggerDismissConverter( + private val stateRouterProvider: Provider, private val currentStateProvider: Provider, ) : Converter { override fun convert(value: Boolean): SendUiState { val state = currentStateProvider() - val amountState = state.amountState ?: return state - return state.copy( + val isEditState = stateRouterProvider().isEditState + val amountState = state.getAmountState(isEditState) ?: return state + return state.copyWrapped( + isEditState = isEditState, amountState = amountState.copy( amountTextField = amountState.amountTextField.copy( isValuePasted = false, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt index 3b01354982..cd1e28de72 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt @@ -49,10 +49,13 @@ internal class SendNotificationFactory( .filter { it.type == SendUiStateType.Send } .map { val state = currentStateProvider() - val sendState = state.sendState ?: return@map persistentListOf() - val feeState = state.feeState ?: return@map persistentListOf() + val isEditState = stateRouterProvider().isEditState val balance = cryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO - val amountValue = state.amountState?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO + val sendState = state.sendState ?: return@map persistentListOf() + val feeState = state.getFeeState(isEditState) ?: return@map persistentListOf() + val amountState = state.getAmountState(isEditState) ?: return@map persistentListOf() + + val amountValue = amountState.amountTextField.cryptoAmount.value ?: BigDecimal.ZERO val feeValue = feeState.fee?.amount?.value ?: BigDecimal.ZERO val isFeeCoverage = checkFeeCoverage( isSubtractAvailable = isSubtractAvailableProvider(), diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt index 8ec4bfd4d6..43e7da9e42 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt @@ -6,7 +6,6 @@ import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.common.extensions.minimalAmount import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.lib.crypto.BlockchainUtils import java.math.BigDecimal import java.math.RoundingMode @@ -73,8 +72,7 @@ internal fun calculateSubtractedAmount( /** * Check if custom fee is too low */ -internal fun checkIfFeeTooLow(state: SendUiState): Boolean { - val feeSelectorState = state.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return false +internal fun checkIfFeeTooLow(feeSelectorState: FeeSelectorState.Content): Boolean { val multipleFees = feeSelectorState.fees as? TransactionFee.Choosable ?: return false val minimumValue = multipleFees.minimum.amount.value ?: return false val customAmount = feeSelectorState.customValues.firstOrNull() ?: return false diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt index 92189a24ef..229568a930 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt @@ -1,9 +1,6 @@ package com.tangem.features.send.impl.presentation.state.fee -import com.tangem.features.send.impl.presentation.state.SendNotification -import com.tangem.features.send.impl.presentation.state.SendUiState -import com.tangem.features.send.impl.presentation.state.SendUiStateType -import com.tangem.features.send.impl.presentation.state.StateRouter +import com.tangem.features.send.impl.presentation.state.* import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.utils.Provider import kotlinx.collections.immutable.persistentListOf @@ -19,10 +16,10 @@ internal class FeeNotificationFactory( ) { fun create() = stateRouterProvider().currentState - .filter { it.type == SendUiStateType.Fee } + .filter { it.type == SendUiStateType.Fee || it.type == SendUiStateType.EditFee } .map { val state = currentStateProvider() - val feeState = state.feeState ?: return@map persistentListOf() + val feeState = state.getFeeState(stateRouterProvider().isEditState) ?: return@map persistentListOf() buildList { addFeeUnreachableNotification(feeState.feeSelectorState) }.toImmutableList() diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt index b88d51c2a9..e5683fcdc7 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt @@ -7,9 +7,7 @@ import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase -import com.tangem.features.send.impl.presentation.state.SendNotification -import com.tangem.features.send.impl.presentation.state.SendStates -import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.features.send.impl.presentation.state.* import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.utils.Provider import kotlinx.collections.immutable.ImmutableList @@ -20,6 +18,7 @@ import kotlinx.collections.immutable.persistentListOf */ internal class FeeStateFactory( private val clickIntents: SendClickIntents, + private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val feeCryptoCurrencyStatusProvider: Provider, private val appCurrencyProvider: Provider, @@ -43,8 +42,10 @@ internal class FeeStateFactory( fun onFeeOnLoadingState(): SendUiState { val state = currentStateProvider() - val feeState = state.feeState ?: return state - return state.copy( + val isEditState = stateRouterProvider().isEditState + val feeState = state.getFeeState(isEditState) ?: return state + return state.copyWrapped( + isEditState = isEditState, sendState = state.sendState?.copy( isPrimaryButtonEnabled = false, ), @@ -62,7 +63,8 @@ internal class FeeStateFactory( fun onFeeOnLoadedState(fees: TransactionFee): SendUiState { val state = currentStateProvider() - val feeState = state.feeState ?: return state + val isEditState = stateRouterProvider().isEditState + val feeState = state.getFeeState(isEditState) ?: return state val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content val isCustomWasSelected = if (feeState.isCustomSelected) { @@ -79,7 +81,8 @@ internal class FeeStateFactory( ) val fee = feeConverter.convert(updatedFeeSelectorState) - return state.copy( + return state.copyWrapped( + isEditState = isEditState, sendState = state.sendState?.copy( isPrimaryButtonEnabled = true, ), @@ -93,8 +96,10 @@ internal class FeeStateFactory( fun onFeeOnErrorState(): SendUiState { val state = currentStateProvider() - return state.copy( - feeState = state.feeState?.copy( + val isEditState = stateRouterProvider().isEditState + return state.copyWrapped( + isEditState = isEditState, + feeState = state.getFeeState(isEditState)?.copy( feeSelectorState = FeeSelectorState.Error, ), sendState = state.sendState?.copy( @@ -105,13 +110,15 @@ internal class FeeStateFactory( fun onFeeSelectedState(feeType: FeeType): SendUiState { val state = currentStateProvider() - val feeState = state.feeState ?: return state + val isEditState = stateRouterProvider().isEditState + val feeState = state.getFeeState(isEditState) ?: return state val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state val updatedFeeSelectorState = feeSelectorState.copy(selectedFee = feeType) val fee = feeConverter.convert(updatedFeeSelectorState) val isCustomFeeWasSelected = feeState.isCustomSelected || updatedFeeSelectorState.selectedFee == FeeType.Custom - return state.copy( + return state.copyWrapped( + isEditState = isEditState, feeState = feeState.copy( fee = fee, isCustomSelected = isCustomFeeWasSelected, @@ -122,12 +129,14 @@ internal class FeeStateFactory( fun onCustomFeeValueChange(index: Int, value: String): SendUiState { val state = currentStateProvider() - val feeState = state.feeState ?: return state + val isEditState = stateRouterProvider().isEditState + val feeState = state.getFeeState(isEditState) ?: return state val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state val updatedFeeSelectorState = customFeeFieldConverter.onValueChange(feeSelectorState, index, value) val fee = feeConverter.convert(updatedFeeSelectorState) - return state.copy( + return state.copyWrapped( + isEditState = isEditState, feeState = feeState.copy( feeSelectorState = updatedFeeSelectorState, fee = fee, @@ -137,10 +146,13 @@ internal class FeeStateFactory( fun getFeeNotificationState(notifications: ImmutableList): SendUiState { val state = currentStateProvider() - return state.copy( - feeState = state.feeState?.copy( + val isEditState = stateRouterProvider().isEditState + val feeState = state.getFeeState(isEditState) ?: return state + return state.copyWrapped( + isEditState = isEditState, + feeState = feeState.copy( notifications = notifications, - isPrimaryButtonEnabled = isPrimaryButtonEnabled(state.feeState, notifications), + isPrimaryButtonEnabled = isPrimaryButtonEnabled(feeState, notifications), ), ) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt index a1fc8fbe25..04c375714e 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt @@ -8,18 +8,21 @@ import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import java.math.BigDecimal import java.math.RoundingMode internal class SendAmountFieldChangeConverter( + private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, ) : Converter { override fun convert(value: String): SendUiState { val state = currentStateProvider() - val amountState = state.amountState ?: return state + val isEditState = stateRouterProvider().isEditState + val amountState = state.getAmountState(isEditState) ?: return state val amountTextField = amountState.amountTextField if (value.isEmpty()) return state.emptyState() @@ -35,7 +38,8 @@ internal class SendAmountFieldChangeConverter( val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue val isExceedBalance = checkValue.checkExceedBalance(amountTextField) val isZero = if (amountTextField.isFiatValue) decimalFiatValue.isZero() else decimalCryptoValue.isZero() - return state.copy( + return state.copyWrapped( + isEditState = isEditState, amountState = amountState.copy( isPrimaryButtonEnabled = !isExceedBalance && !isZero, amountTextField = amountTextField.copy( @@ -75,9 +79,11 @@ internal class SendAmountFieldChangeConverter( } private fun SendUiState.emptyState(): SendUiState { - if (amountState == null) return this + val isEditState = stateRouterProvider().isEditState + val amountState = getAmountState(isEditState) ?: return this val amountTextField = amountState.amountTextField - return copy( + return copyWrapped( + isEditState = isEditState, amountState = amountState.copy( isPrimaryButtonEnabled = false, amountTextField = amountTextField.copy( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt index bb69b15be3..7d4dfe3b45 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt @@ -6,11 +6,13 @@ import androidx.compose.ui.text.input.KeyboardType import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import com.tangem.utils.isNullOrZero internal class SendAmountFieldMaxAmountConverter( + private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, ) : Converter { @@ -18,7 +20,8 @@ internal class SendAmountFieldMaxAmountConverter( override fun convert(value: Unit): SendUiState { val state = currentStateProvider() val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - val amountState = state.amountState ?: return state + val isEditState = stateRouterProvider().isEditState + val amountState = state.getAmountState(isEditState) ?: return state val amountTextField = amountState.amountTextField val cryptoDecimals = amountTextField.cryptoAmount.decimals @@ -31,7 +34,8 @@ internal class SendAmountFieldMaxAmountConverter( val isDoneActionEnabled = !decimalCryptoValue.isNullOrZero() val cryptoValue = decimalCryptoValue?.parseBigDecimal(cryptoDecimals).orEmpty() val fiatValue = decimalFiatValue?.parseBigDecimal(fiatDecimals).orEmpty() - return state.copy( + return state.copyWrapped( + isEditState = isEditState, amountState = amountState.copy( isPrimaryButtonEnabled = true, amountTextField = amountTextField.copy( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt index bf72bb774a..ace8e220f8 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt @@ -3,7 +3,10 @@ package com.tangem.features.send.impl.presentation.ui import androidx.compose.animation.* import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -50,7 +53,11 @@ internal fun SendNavigationButtons( bottom = TangemTheme.dimens.spacing16, ), ) { - SendingText(uiState = uiState, isVisible = isSendingState) + SendingText( + uiState = uiState, + isEditState = currentState.isFromConfirmation, + isVisible = isSendingState, + ) SendDoneButtons( txUrl = sendState.txUrl, onExploreClick = uiState.clickIntents::onExploreClick, @@ -130,7 +137,12 @@ private fun SendNavigationButton( } @Composable -private fun SendingText(uiState: SendUiState, isVisible: Boolean, modifier: Modifier = Modifier) { +private fun SendingText( + uiState: SendUiState, + isEditState: Boolean, + isVisible: Boolean, + modifier: Modifier = Modifier, +) { var isVisibleProxy by remember { mutableStateOf(isVisible) } // text appearance delay for smooth screen transitions @@ -148,8 +160,8 @@ private fun SendingText(uiState: SendUiState, isVisible: Boolean, modifier: Modi exit = slideOutVertically().plus(fadeOut()), label = "Animate show sending state text", ) { - val amountState = uiState.amountState - val feeState = uiState.feeState + val amountState = uiState.getAmountState(isEditState) + val feeState = uiState.getFeeState(isEditState) val fiatRate = feeState?.rate val fiatAmount = amountState?.amountTextField?.fiatAmount val feeFiat = fiatRate?.let { feeState.fee?.amount?.value?.multiply(it) } @@ -230,11 +242,11 @@ private fun getButtonData( SendUiStateType.Amount, SendUiStateType.Recipient, SendUiStateType.Fee, - -> if (currentState.isFromConfirmation) { - R.string.common_continue to uiState.clickIntents::onNextClick - } else { - R.string.common_next to uiState.clickIntents::onNextClick - } + -> R.string.common_next to { uiState.clickIntents.onNextClick() } + SendUiStateType.EditFee, + SendUiStateType.EditAmount, + SendUiStateType.EditRecipient, + -> R.string.common_continue to { uiState.clickIntents.onNextClick(isFromEdit = true) } SendUiStateType.Send -> when { isSuccess -> R.string.common_close isSending -> R.string.send_sending @@ -245,10 +257,13 @@ private fun getButtonData( private fun isButtonEnabled(currentState: SendUiCurrentScreen, uiState: SendUiState): Boolean { return when (currentState.type) { - SendUiStateType.Amount -> uiState.amountState?.isPrimaryButtonEnabled ?: false - SendUiStateType.Recipient -> uiState.recipientState?.isPrimaryButtonEnabled ?: false - SendUiStateType.Fee -> uiState.feeState?.isPrimaryButtonEnabled ?: false - SendUiStateType.Send -> uiState.sendState?.isPrimaryButtonEnabled ?: false + SendUiStateType.Amount -> uiState.amountState?.isPrimaryButtonEnabled + SendUiStateType.Recipient -> uiState.recipientState?.isPrimaryButtonEnabled + SendUiStateType.Fee -> uiState.feeState?.isPrimaryButtonEnabled + SendUiStateType.Send -> uiState.sendState?.isPrimaryButtonEnabled + SendUiStateType.EditAmount -> uiState.editAmountState?.isPrimaryButtonEnabled + SendUiStateType.EditRecipient -> uiState.editRecipientState?.isPrimaryButtonEnabled + SendUiStateType.EditFee -> uiState.editFeeState?.isPrimaryButtonEnabled else -> true - } + } ?: false } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt index 94620f127c..1d678b777e 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt @@ -122,6 +122,7 @@ internal class SendViewModel @Inject constructor( private val stateFactory = SendStateFactory( clickIntents = this, + stateRouterProvider = Provider { stateRouter }, currentStateProvider = Provider { uiState }, userWalletProvider = Provider { userWallet }, appCurrencyProvider = Provider(selectedAppCurrencyFlow::value), @@ -132,12 +133,14 @@ internal class SendViewModel @Inject constructor( ) private val amountStateFactory = AmountStateFactory( + stateRouterProvider = Provider { stateRouter }, currentStateProvider = Provider { uiState }, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, ) private val feeStateFactory = FeeStateFactory( clickIntents = this, + stateRouterProvider = Provider { stateRouter }, currentStateProvider = Provider { uiState }, feeCryptoCurrencyStatusProvider = Provider { feeCryptoCurrencyStatus }, appCurrencyProvider = Provider(selectedAppCurrencyFlow::value), @@ -146,6 +149,7 @@ internal class SendViewModel @Inject constructor( private val eventStateFactory = SendEventStateFactory( clickIntents = this, + stateRouterProvider = Provider { stateRouter }, currentStateProvider = Provider { uiState }, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, feeStateFactory = feeStateFactory, @@ -171,7 +175,10 @@ internal class SendViewModel @Inject constructor( ) private val sendOnNextScreenAnalyticSender by lazy(LazyThreadSafetyMode.NONE) { - SendOnNextScreenAnalyticSender(analyticsEventHandler) + SendOnNextScreenAnalyticSender( + stateRouterProvider = Provider { stateRouter }, + analyticsEventHandler = analyticsEventHandler, + ) } // todo convert to StateFlow @@ -454,7 +461,9 @@ internal class SendViewModel @Inject constructor( stateRouter.currentState .onEach { when (it.type) { - SendUiStateType.Fee -> loadFee() + SendUiStateType.Fee, + SendUiStateType.EditFee, + -> loadFee() SendUiStateType.Send -> sendIdleTimer = SystemClock.elapsedRealtime() else -> Unit } @@ -489,16 +498,17 @@ internal class SendViewModel @Inject constructor( stateRouter.onBackClick(isSuccess = uiState.sendState?.isSuccess == true) } - override fun onNextClick() { + override fun onNextClick(isFromEdit: Boolean) { val currentState = stateRouter.currentState.value + uiState = stateFactory.syncEditStates(isFromEdit = isFromEdit) sendOnNextScreenAnalyticSender.send(currentState.type, uiState) when (currentState.type) { - SendUiStateType.Fee -> { - if (onFeeNext()) return - } - SendUiStateType.Amount -> { - loadFee() - } + SendUiStateType.Fee, + SendUiStateType.EditFee, + -> if (onFeeNext()) return + SendUiStateType.Amount, + SendUiStateType.EditAmount, + -> loadFee() else -> Unit } @@ -523,13 +533,14 @@ internal class SendViewModel @Inject constructor( innerRouter.openTokenDetails(userWalletId, currency) private fun onFeeNext(): Boolean { - if (checkIfFeeTooLow(uiState)) { + val feeState = uiState.getFeeState(stateRouter.isEditState) + val feeSelectorState = feeState?.feeSelectorState as? FeeSelectorState.Content ?: return false + if (checkIfFeeTooLow(feeSelectorState)) { uiState = eventStateFactory.getFeeTooLowAlert( onConsume = { uiState = eventStateFactory.onConsumeEventState() }, ) return true } - val feeSelectorState = uiState.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return false return checkIfFeeTooHigh( feeSelectorState = feeSelectorState, onShow = { diff -> @@ -706,8 +717,9 @@ internal class SendViewModel @Inject constructor( } private suspend fun callFeeUseCase(): Either? { - val amountState = uiState.amountState ?: return null - val recipientState = uiState.recipientState ?: return null + val isFromConfirmation = stateRouter.currentState.value.isFromConfirmation + val amountState = uiState.getAmountState(isFromConfirmation) ?: return null + val recipientState = uiState.getRecipientState(isFromConfirmation) ?: return null val amount = amountState.amountTextField.cryptoAmount.value ?: return null return feeCryptoCurrencyStatus?.let { feeCurrencyStatus -> @@ -736,12 +748,14 @@ internal class SendViewModel @Inject constructor( } override fun showAmount() { + uiState = stateFactory.syncEditStates(isFromEdit = false) stateRouter.showAmount(isFromConfirmation = true) setNeverToShowTapHelp() analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Amount)) } override fun showRecipient() { + uiState = stateFactory.syncEditStates(isFromEdit = false) stateRouter.showRecipient(isFromConfirmation = true) uiState = stateFactory.getHiddenTapHelpState() setNeverToShowTapHelp() @@ -749,6 +763,7 @@ internal class SendViewModel @Inject constructor( } override fun showFee() { + uiState = stateFactory.syncEditStates(isFromEdit = false) stateRouter.showFee(isFromConfirmation = true) setNeverToShowTapHelp() analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Fee)) From 66168b3177a2ee91d34cf9aec591c11822ac73d5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 25 Apr 2024 15:38:02 +0100 Subject: [PATCH 3/4] Updated on 2026-08-14 --- .../wallet/redux/middlewares/TradeCryptoMiddleware.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index aa15f116e8..39a0168df7 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -223,6 +223,10 @@ object TradeCryptoMiddleware { } private fun handleSendCoin(action: TradeCryptoAction.SendCoin) { + if (action.transactionInfo?.tag != null) { + // avoid open old send if memo exists + return + } val cryptoStatus = action.coinStatus val currency = cryptoStatus.currency val blockchain = Blockchain.fromId(currency.network.id.value) From 429f9ea3efb27116b8be650632fa4eb691bb6ec6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 25 Apr 2024 15:54:42 +0100 Subject: [PATCH 4/4] Updated on 2026-08-14 --- .../com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt index eded0146fc..91430442e1 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt @@ -197,12 +197,6 @@ class GetCurrencyWarningsUseCase( coinCurrency = coinStatus.currency, ) } - feePaidCurrency is FeePaidCurrency.SameCurrency && tokenStatus.value.amount.isZero() -> { - CryptoCurrencyWarning.BalanceNotEnoughForFee( - tokenCurrency = tokenStatus.currency, - coinCurrency = coinStatus.currency, - ) - } feePaidCurrency is FeePaidCurrency.Token -> { val feePaidTokenBalance = feePaidCurrency.balance val amount = tokenStatus.value.amount ?: return null