From bc707a571e590f5be8c1ebcca744763285b6eed1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 25 Sep 2024 19:52:49 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../ui/notifications/NotificationsFactory.kt | 2 +- .../errors/FeeErrorStateMapper.kt | 19 - .../presentation/state/SendNotification.kt | 250 -------- .../presentation/state/SendStateFactory.kt | 13 +- .../impl/presentation/state/SendUiState.kt | 5 +- .../state/confirm/SendNotificationFactory.kt | 582 +++++------------- .../state/fee/FeeNotificationFactory.kt | 22 +- .../state/fee/FeeSelectorState.kt | 8 +- .../presentation/state/fee/FeeStateFactory.kt | 13 +- .../state/previewdata/FeeStatePreviewData.kt | 2 +- .../state/previewdata/SendClickIntentsStub.kt | 22 +- .../presentation/ui/common/Notifications.kt | 17 +- .../viewmodel/SendClickIntents.kt | 18 +- .../presentation/viewmodel/SendViewModel.kt | 59 +- .../AddStakingNotificationsTransformer.kt | 12 +- 15 files changed, 248 insertions(+), 796 deletions(-) delete mode 100644 features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/errors/FeeErrorStateMapper.kt delete mode 100644 features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotification.kt diff --git a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt index 37e29e0335..36c15ab3c6 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/notifications/NotificationsFactory.kt @@ -21,7 +21,7 @@ import java.math.BigDecimal object NotificationsFactory { fun MutableList.addFeeUnreachableNotification( - feeError: GetFeeError, + feeError: GetFeeError?, tokenName: String, onReload: () -> Unit, ) { diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/errors/FeeErrorStateMapper.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/errors/FeeErrorStateMapper.kt deleted file mode 100644 index ab218f7fe0..0000000000 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/errors/FeeErrorStateMapper.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.features.send.impl.presentation.errors - -import com.tangem.domain.transaction.error.GetFeeError -import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState - -internal class FeeErrorStateMapper { - - fun getFeeError(loadFeeError: GetFeeError?, tokenName: String): FeeSelectorState.Error { - return when (loadFeeError) { - GetFeeError.BlockchainErrors.TronActivationError -> FeeSelectorState.Error.TronAccountActivationError( - tokenName, - ) - is GetFeeError.DataError, - GetFeeError.UnknownError, - null, - -> FeeSelectorState.Error.NetworkError - } - } -} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotification.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotification.kt deleted file mode 100644 index 187e4090fd..0000000000 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotification.kt +++ /dev/null @@ -1,250 +0,0 @@ -package com.tangem.features.send.impl.presentation.state - -import com.tangem.blockchain.common.Blockchain -import com.tangem.core.ui.components.notifications.NotificationConfig -import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.wrappedList -import com.tangem.core.ui.utils.BigDecimalFormatter -import com.tangem.features.send.impl.R -import java.math.BigDecimal - -internal sealed class SendNotification(val config: NotificationConfig) { - - sealed class Error( - title: TextReference, - subtitle: TextReference, - iconResId: Int = R.drawable.ic_alert_24, - buttonState: NotificationConfig.ButtonsState? = null, - onCloseClick: (() -> Unit)? = null, - ) : SendNotification( - config = NotificationConfig( - title = title, - subtitle = subtitle, - iconResId = iconResId, - buttonsState = buttonState, - onCloseClick = onCloseClick, - ), - ) { - - data object TotalExceedsBalance : Error( - title = resourceReference(R.string.send_notification_exceed_balance_title), - subtitle = resourceReference(R.string.send_notification_exceed_balance_text), - ) - - data object InvalidAmount : Error( - title = resourceReference(R.string.send_notification_invalid_amount_title), - subtitle = resourceReference(R.string.send_notification_invalid_amount_text), - ) - - data class MinimumAmountError(val amount: String) : Error( - title = resourceReference(R.string.send_notification_invalid_amount_title), - subtitle = resourceReference( - R.string.send_notification_invalid_minimum_amount_text, - wrappedList(amount, amount), - ), - ) - - data class TransactionLimitError( - val cryptoCurrency: String, - val utxoLimit: String, - val amountLimit: String, - val onConfirmClick: () -> Unit, - ) : Error( - title = resourceReference(R.string.send_notification_transaction_limit_title), - subtitle = resourceReference( - R.string.send_notification_transaction_limit_text, - wrappedList(cryptoCurrency, utxoLimit, amountLimit), - ), - buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig( - text = resourceReference(R.string.send_notification_leave_button, wrappedList(amountLimit)), - onClick = onConfirmClick, - ), - ) - - data class ExceedsBalance( - val networkIconId: Int, - val currencyName: String, - val feeName: String, - val feeSymbol: String, - val networkName: String, - val mergeFeeNetworkName: Boolean = false, - val onClick: (() -> Unit)? = null, - ) : Error( - title = resourceReference( - id = R.string.warning_send_blocked_funds_for_fee_title, - wrappedList(feeName), - ), - subtitle = resourceReference( - id = R.string.warning_send_blocked_funds_for_fee_message, - formatArgs = wrappedList(currencyName, networkName, currencyName, feeName, feeSymbol), - ), - iconResId = networkIconId, - buttonState = onClick?.let { - NotificationConfig.ButtonsState.SecondaryButtonConfig( - text = resourceReference( - R.string.common_buy_currency, - wrappedList( - if (mergeFeeNetworkName) { - "$currencyName ($feeSymbol)" - } else { - feeName - }, - ), - ), - onClick = onClick, - ) - }, - ) - - data class ExistentialDeposit(val deposit: String, val onConfirmClick: () -> Unit) : Error( - title = resourceReference(R.string.send_notification_existential_deposit_title), - subtitle = resourceReference(R.string.send_notification_existential_deposit_text, wrappedList(deposit)), - buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig( - text = resourceReference(R.string.common_ok), - onClick = onConfirmClick, - ), - ) - - data class ReserveAmount(val amount: String) : Error( - title = resourceReference( - id = R.string.send_notification_invalid_reserve_amount_title, - wrappedList(amount), - ), - subtitle = resourceReference(id = R.string.send_notification_invalid_reserve_amount_text), - ) - } - - sealed class Warning( - title: TextReference, - subtitle: TextReference, - buttonsState: NotificationConfig.ButtonsState? = null, - onCloseClick: (() -> Unit)? = null, - ) : SendNotification( - config = NotificationConfig( - title = title, - subtitle = subtitle, - iconResId = R.drawable.img_attention_20, - buttonsState = buttonsState, - onCloseClick = onCloseClick, - ), - ) { - data class HighFeeError( - val currencyName: String, - val amount: String, - val onConfirmClick: () -> Unit, - val onCloseClick: () -> Unit, - ) : Warning( - title = resourceReference(R.string.send_notification_high_fee_title), - subtitle = resourceReference(R.string.send_notification_high_fee_text, wrappedList(currencyName, amount)), - buttonsState = NotificationConfig.ButtonsState.PrimaryButtonConfig( - text = resourceReference(R.string.send_notification_reduce_by, wrappedList(amount)), - onClick = onConfirmClick, - ), - onCloseClick = onCloseClick, - ) - - data object FeeTooLow : Warning( - title = resourceReference(id = R.string.send_notification_transaction_delay_title), - subtitle = resourceReference(id = R.string.send_notification_transaction_delay_text), - ) - - data class TooHigh( - val value: String, - ) : Warning( - title = resourceReference(id = R.string.send_notification_fee_too_high_title), - subtitle = resourceReference(id = R.string.send_notification_fee_too_high_text, wrappedList(value)), - ) - - data class NetworkFeeUnreachable(val onRefresh: () -> Unit) : Warning( - title = resourceReference(R.string.send_fee_unreachable_error_title), - subtitle = resourceReference(R.string.send_fee_unreachable_error_text), - buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig( - text = resourceReference(R.string.warning_button_refresh), - onClick = onRefresh, - ), - ) - - data class TronAccountNotActivated(val tokenName: String) : Warning( - title = resourceReference(R.string.send_fee_unreachable_error_title), - subtitle = resourceReference( - R.string.send_tron_account_activation_error, - wrappedList(tokenName), - ), - ) - - data class FeeCoverageNotification(val cryptoAmount: String, val fiatAmount: String) : Warning( - title = resourceReference(R.string.send_network_fee_warning_title), - subtitle = resourceReference( - R.string.common_network_fee_warning_content, - wrappedList(cryptoAmount, fiatAmount), - ), - ) - } - - sealed interface Cardano { - - data class MinAdaValueCharged(val tokenName: String, val minAdaValue: String) : Warning( - title = resourceReference(id = R.string.cardano_coin_will_be_send_with_token_title), - subtitle = resourceReference( - id = R.string.cardano_coin_will_be_send_with_token_description, - formatArgs = wrappedList(minAdaValue, tokenName), - ), - ) - - data object InsufficientBalanceToTransferCoin : Error( - title = resourceReference(id = R.string.cardano_max_amount_has_token_title), - subtitle = resourceReference(id = R.string.cardano_max_amount_has_token_description), - ) - - data class InsufficientBalanceToTransferToken(val tokenName: String) : Error( - title = resourceReference(id = R.string.cardano_insufficient_balance_to_send_token_title), - subtitle = resourceReference( - id = R.string.cardano_insufficient_balance_to_send_token_description, - formatArgs = wrappedList(tokenName), - ), - ) - } - - sealed interface Koinos { - data class InsufficientRecoverableMana( - val mana: BigDecimal, - val maxMana: BigDecimal, - ) : Error( - title = resourceReference(R.string.koinos_insufficient_mana_to_send_koin_title), - subtitle = resourceReference( - R.string.koinos_insufficient_mana_to_send_koin_description, - formatArgs = wrappedList( - BigDecimalFormatter.formatCryptoAmountShorted(mana, "", Blockchain.Koinos.decimals()), - BigDecimalFormatter.formatCryptoAmountShorted(maxMana, "", Blockchain.Koinos.decimals()), - ), - ), - ) - - data object InsufficientBalance : Error( - title = resourceReference(R.string.koinos_insufficient_balance_to_send_koin_title), - subtitle = resourceReference(R.string.koinos_insufficient_balance_to_send_koin_description), - ) - - data class ManaExceedsBalance( - val availableKoinForTransfer: BigDecimal, - val onReduceClick: () -> Unit, - ) : Error( - title = resourceReference(R.string.koinos_mana_exceeds_koin_balance_title), - subtitle = resourceReference( - R.string.koinos_mana_exceeds_koin_balance_description, - formatArgs = wrappedList( - BigDecimalFormatter.formatCryptoAmount( - availableKoinForTransfer, - Blockchain.Koinos.currency, - Blockchain.Koinos.decimals(), - ), - ), - ), - buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig( - text = resourceReference(R.string.send_notification_reduce_to, wrappedList(availableKoinForTransfer)), - onClick = onReduceClick, - ), - ) - } -} \ No newline at end of file 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 e71731954d..ddcc7ab29a 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 @@ -3,6 +3,7 @@ package com.tangem.features.send.impl.presentation.state import com.tangem.blockchain.common.TransactionData import com.tangem.common.ui.amountScreen.converters.AmountStateConverter import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.event.consumedEvent import com.tangem.domain.appcurrency.model.AppCurrency @@ -164,14 +165,14 @@ internal class SendStateFactory( ) } - fun getSendNotificationState(notifications: ImmutableList): SendUiState { + fun getSendNotificationState(notifications: ImmutableList): SendUiState { val state = currentStateProvider() val sendState = state.sendState ?: return state val reducedBy = sendState.reduceAmountBy.takeIf { notifications.none { - it is SendNotification.Error.ExistentialDeposit || - it is SendNotification.Error.TransactionLimitError || - it is SendNotification.Warning.HighFeeError + it is NotificationUM.Error.ExistentialDeposit || + it is NotificationUM.Error.TransactionLimitError || + it is NotificationUM.Warning.HighFeeError } } return state.copy( @@ -199,10 +200,10 @@ internal class SendStateFactory( private fun isPrimaryButtonEnabled( state: SendUiState, isSending: Boolean, - notifications: ImmutableList, + notifications: ImmutableList, ): Boolean { val feeState = state.getFeeState(stateRouterProvider().isEditState) ?: return false - val hasErrorNotifications = notifications.any { it is SendNotification.Error } + val hasErrorNotifications = notifications.any { it is NotificationUM.Error } return !hasErrorNotifications && !isSending && feeState.feeSelectorState is FeeSelectorState.Content } //endregion 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 1282a69df2..ab01823bca 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 @@ -4,6 +4,7 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable import com.tangem.blockchain.common.transaction.Fee import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.event.StateEvent import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent @@ -112,7 +113,7 @@ internal sealed class SendStates { val appCurrency: AppCurrency, val isFeeApproximate: Boolean, val isCustomSelected: Boolean, - val notifications: ImmutableList, + val notifications: ImmutableList, val isTronToken: Boolean, ) : SendStates() @@ -129,7 +130,7 @@ internal sealed class SendStates { val reduceAmountBy: BigDecimal?, val isFromConfirmation: Boolean, val showTapHelp: Boolean, - val notifications: ImmutableList, + val notifications: ImmutableList, ) : SendStates() } 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 ae185f4c0c..1bf9b9ed01 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 @@ -1,31 +1,35 @@ package com.tangem.features.send.impl.presentation.state.confirm import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee -import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.utils.minimalAmount -import com.tangem.common.ui.amountScreen.models.AmountFieldModel import com.tangem.common.ui.amountScreen.models.AmountState -import com.tangem.common.ui.amountScreen.utils.getFiatString +import com.tangem.common.ui.notifications.NotificationUM +import com.tangem.common.ui.notifications.NotificationsFactory.addDustWarningNotification +import com.tangem.common.ui.notifications.NotificationsFactory.addExceedBalanceNotification +import com.tangem.common.ui.notifications.NotificationsFactory.addExceedsBalanceNotification +import com.tangem.common.ui.notifications.NotificationsFactory.addExistentialWarningNotification +import com.tangem.common.ui.notifications.NotificationsFactory.addFeeCoverageNotification +import com.tangem.common.ui.notifications.NotificationsFactory.addFeeUnreachableNotification +import com.tangem.common.ui.notifications.NotificationsFactory.addReserveAmountErrorNotification +import com.tangem.common.ui.notifications.NotificationsFactory.addTransactionLimitErrorNotification +import com.tangem.common.ui.notifications.NotificationsFactory.addValidateTransactionNotifications import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.core.ui.extensions.networkIconResId -import com.tangem.core.ui.utils.BigDecimalFormatter -import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.GetBalanceNotEnoughForFeeWarningUseCase -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.GetCurrencyCheckUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning -import com.tangem.domain.tokens.repository.CurrencyChecksRepository +import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck +import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase import com.tangem.domain.utils.convertToSdkAmount -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.features.send.impl.R +import com.tangem.domain.wallets.models.UserWalletId 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.SendStates +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.* import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.lib.crypto.BlockchainUtils @@ -41,32 +45,33 @@ import kotlinx.coroutines.flow.map import java.math.BigDecimal @Suppress("LongParameterList", "LargeClass") -internal class SendNotificationFactory( +internal class SendNotificationFactory constructor( + private val analyticsEventHandler: AnalyticsEventHandler, + private val validateTransactionUseCase: ValidateTransactionUseCase, + private val getCurrencyCheckUseCase: GetCurrencyCheckUseCase, + private val getBalanceNotEnoughForFeeWarningUseCase: GetBalanceNotEnoughForFeeWarningUseCase, private val cryptoCurrencyStatusProvider: Provider, private val feeCryptoCurrencyStatusProvider: Provider, private val currentStateProvider: Provider, - private val userWalletProvider: Provider, - private val currencyChecksRepository: CurrencyChecksRepository, private val stateRouterProvider: Provider, private val isSubtractAvailableProvider: Provider, private val appCurrencyProvider: Provider, private val clickIntents: SendClickIntents, - private val analyticsEventHandler: AnalyticsEventHandler, - private val getBalanceNotEnoughForFeeWarningUseCase: GetBalanceNotEnoughForFeeWarningUseCase, - private val validateTransactionUseCase: ValidateTransactionUseCase, + private val userWalletId: UserWalletId, ) { - fun create(): Flow> = stateRouterProvider().currentState + fun create(): Flow> = stateRouterProvider().currentState .filter { it.type == SendUiStateType.Send } .map { + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() + val state = currentStateProvider() val isEditState = stateRouterProvider().isEditState - val balance = cryptoCurrencyStatusProvider().value.amount.orZero() + val balance = cryptoCurrencyStatus.value.amount.orZero() val sendState = state.sendState ?: return@map persistentListOf() val feeState = state.getFeeState(isEditState) ?: return@map persistentListOf() val amountState = state.getAmountState(isEditState) as? AmountState.Data ?: return@map persistentListOf() - val recipientAddress = state.recipientState?.addressTextField?.value.orEmpty() val amountValue = amountState.amountTextField.cryptoAmount.value.orZero() val feeValue = feeState.fee?.amount?.value.orZero() val reduceAmountBy = sendState.reduceAmountBy.orZero() @@ -78,42 +83,38 @@ internal class SendNotificationFactory( reduceAmountBy = reduceAmountBy, ) val sendingAmount = checkAndCalculateSubtractedAmount( - isAmountSubtractAvailable = isFeeCoverage, - cryptoCurrencyStatus = cryptoCurrencyStatusProvider(), + isAmountSubtractAvailable = isSubtractAvailableProvider(), + cryptoCurrencyStatus = cryptoCurrencyStatus, amountValue = amountValue, feeValue = feeValue, reduceAmountBy = reduceAmountBy, ) + val feeError = (feeState.feeSelectorState as? FeeSelectorState.Error)?.error + val currencyCheck = getCurrencyCheckUseCase( + userWalletId = userWalletId, + currencyStatus = cryptoCurrencyStatus, + amount = amountValue, + fee = feeValue, + ) buildList { - // errors - addFeeUnreachableNotification(feeState.feeSelectorState) - addExceedBalanceNotification(feeValue, sendingAmount) - addExceedsBalanceNotification(feeState.fee) - addDustWarningNotificationForSpecificBlockchains(feeValue, sendingAmount) - addTransactionLimitErrorNotification(feeValue, sendingAmount) - addReserveAmountErrorNotification(recipientAddress, sendingAmount) - - // warnings - addExistentialWarningNotification(feeValue, amountValue) - addFeeCoverageNotification( - isFeeCoverage = isFeeCoverage, - amountField = amountState.amountTextField, - sendingValue = sendingAmount, - ) - addHighFeeWarningNotification(amountValue, sendState.ignoreAmountReduce) - addTooHighNotification(feeState.feeSelectorState) - addTooLowNotification(feeState) - - // blockchain specific - addValidateTransactionNotifications( + addErrorNotifications( + feeError = feeError, sendingAmount = sendingAmount, - fee = feeState.fee, - state = state, + feeValue = feeValue, + currencyCheck = currencyCheck, + ) + addWarningNotifications( + amountState = amountState, + feeState = feeState, + sendState = sendState, + sendingAmount = sendingAmount, + isFeeCoverage = isFeeCoverage, + currencyCheck = currencyCheck, ) }.toImmutableList() } - fun dismissNotificationState(clazz: Class, isIgnored: Boolean = false): SendUiState { + fun dismissNotificationState(clazz: Class, isIgnored: Boolean = false): SendUiState { val state = currentStateProvider() val sendState = state.sendState ?: return state val notificationsToRemove = sendState.notifications.filterIsInstance(clazz) @@ -128,161 +129,120 @@ internal class SendNotificationFactory( ) } - private fun MutableList.addFeeUnreachableNotification(feeSelectorState: FeeSelectorState) { - when (feeSelectorState) { - is FeeSelectorState.Error.TronAccountActivationError -> add( - SendNotification.Warning.TronAccountNotActivated( - feeSelectorState.tokenName, - ), - ) - is FeeSelectorState.Error.NetworkError -> add( - SendNotification.Warning.NetworkFeeUnreachable(clickIntents::feeReload), - ) - else -> { - /* do nothing */ - } - } - } - - private fun MutableList.addExceedBalanceNotification( - feeAmount: BigDecimal, - receivedAmount: BigDecimal, - ) { - val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - val balance = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO - - if (!isSubtractAvailableProvider()) return - - val showNotification = receivedAmount + feeAmount > balance - if (showNotification) { - add(SendNotification.Error.TotalExceedsBalance) - } - } - - private suspend fun MutableList.addReserveAmountErrorNotification( - recipientAddress: String, + private suspend fun MutableList.addErrorNotifications( + feeError: GetFeeError?, sendingAmount: BigDecimal, + feeValue: BigDecimal, + currencyCheck: CryptoCurrencyCheck, ) { - val userWalletId = userWalletProvider().walletId - val cryptoCurrency = cryptoCurrencyStatusProvider().currency - val isAccountFunded = currencyChecksRepository.checkIfAccountFunded( - userWalletId, - cryptoCurrency.network, - recipientAddress, - ) - val minimumAmount = currencyChecksRepository.getReserveAmount(userWalletId, cryptoCurrency.network) - if (!isAccountFunded && minimumAmount != null && minimumAmount > sendingAmount) { - add( - SendNotification.Error.ReserveAmount( - BigDecimalFormatter.formatCryptoAmount( - cryptoAmount = minimumAmount, - cryptoCurrency = cryptoCurrency, - ), - ), - ) - } - } - - private suspend fun MutableList.addTransactionLimitErrorNotification( - feeAmount: BigDecimal, - receivedAmount: BigDecimal, - ) { - val userWalletId = userWalletProvider().walletId - val cryptoCurrency = cryptoCurrencyStatusProvider().currency - val utxoLimit = currencyChecksRepository.checkUtxoAmountLimit( - userWalletId = userWalletId, - network = cryptoCurrency.network, - amount = receivedAmount, - fee = feeAmount, - ) - - if (utxoLimit != null) { - add( - SendNotification.Error.TransactionLimitError( - cryptoCurrency = cryptoCurrency.name, - utxoLimit = utxoLimit.maxLimit.toPlainString(), - amountLimit = BigDecimalFormatter.formatCryptoAmount( - cryptoAmount = utxoLimit.maxAmount, - cryptoCurrency = cryptoCurrency, - ), - onConfirmClick = { - clickIntents.onAmountReduceClick( - reduceAmountTo = utxoLimit.maxAmount, - clazz = SendNotification.Error.TransactionLimitError::class.java, - ) - }, - ), - ) - } - } - - private suspend fun MutableList.addExistentialWarningNotification( - feeAmount: BigDecimal, - receivedAmount: BigDecimal, - ) { - val userWalletId = userWalletProvider().walletId val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - val cryptoCurrency = cryptoCurrencyStatus.currency - val balance = cryptoCurrencyStatus.value.amount ?: return - val spendingAmount = if (cryptoCurrency is CryptoCurrency.Token) { - feeAmount - } else { - receivedAmount - } - val currencyDeposit = currencyChecksRepository.getExistentialDeposit( - userWalletId, - cryptoCurrency.network, + val currency = cryptoCurrencyStatusProvider().currency + val currencyWarning = getBalanceNotEnoughForFeeWarningUseCase( + fee = feeValue, + userWalletId = userWalletId, + tokenStatus = cryptoCurrencyStatus, + coinStatus = feeCryptoCurrencyStatusProvider() ?: cryptoCurrencyStatus, + ).getOrNull() + + addFeeUnreachableNotification( + feeError = feeError, + tokenName = currency.name, + onReload = clickIntents::feeReload, ) - val diff = balance.minus(spendingAmount) - if (currencyDeposit != null && diff >= BigDecimal.ZERO && currencyDeposit > diff) { - add( - SendNotification.Error.ExistentialDeposit( - deposit = BigDecimalFormatter.formatCryptoAmountUncapped( - cryptoAmount = currencyDeposit, - cryptoCurrency = cryptoCurrency, + addExceedBalanceNotification( + feeAmount = feeValue, + sendingAmount = sendingAmount, + isSubtractionAvailable = isSubtractAvailableProvider(), + cryptoCurrencyStatus = cryptoCurrencyStatus, + ) + addExceedsBalanceNotification( + cryptoCurrencyWarning = currencyWarning, + cryptoCurrencyStatus = cryptoCurrencyStatus, + shouldMergeFeeNetworkName = BlockchainUtils.isArbitrum(currency.network.backendId), + onClick = clickIntents::onTokenDetailsClick, + onAnalyticsEvent = { + analyticsEventHandler.send( + SendAnalyticEvents.NoticeNotEnoughFee( + token = cryptoCurrencyStatus.currency.symbol, + blockchain = cryptoCurrencyStatus.currency.network.name, ), - onConfirmClick = { - clickIntents.onAmountReduceClick( - reduceAmountByDiff = currencyDeposit.minus(diff), - reduceAmountBy = currencyDeposit, - clazz = SendNotification.Error.ExistentialDeposit::class.java, - ) - }, - ), + ) + }, + ) + if (!BlockchainUtils.isCardano(currency.id.value)) { + addDustWarningNotification( + dustValue = currencyCheck.dustValue, + feeValue = feeValue, + sendingAmount = sendingAmount, + cryptoCurrencyStatus = cryptoCurrencyStatus, + feeCurrencyStatus = feeCryptoCurrencyStatusProvider(), ) } + addTransactionLimitErrorNotification( + utxoLimit = currencyCheck.utxoAmountLimit, + cryptoCurrency = currency, + onReduceClick = clickIntents::onAmountReduceToClick, + ) + addReserveAmountErrorNotification( + reserveAmount = currencyCheck.reserveAmount, + sendingAmount = sendingAmount, + cryptoCurrency = currency, + isAccountFunded = false, + ) } - private fun MutableList.addFeeCoverageNotification( + private suspend fun MutableList.addWarningNotifications( + amountState: AmountState.Data, + feeState: SendStates.FeeState, + sendState: SendStates.SendState, + sendingAmount: BigDecimal, isFeeCoverage: Boolean, - amountField: AmountFieldModel, - sendingValue: BigDecimal, + currencyCheck: CryptoCurrencyCheck, ) { - if (isFeeCoverage) { - analyticsEventHandler.send(SendAnalyticEvents.NoticeFeeCoverage) - val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - val cryptoCurrency = cryptoCurrencyStatus.currency - val fiatRate = cryptoCurrencyStatus.value.fiatRate - val amountValue = amountField.cryptoAmount.value ?: return - - val cryptoDiff = amountValue.minus(sendingValue) - add( - SendNotification.Warning.FeeCoverageNotification( - cryptoAmount = BigDecimalFormatter.formatCryptoAmountUncapped( - cryptoAmount = cryptoDiff, - cryptoCurrency = cryptoCurrency, - ), - fiatAmount = getFiatString( - value = cryptoDiff, - rate = fiatRate, - appCurrency = appCurrencyProvider(), - ), - ), - ) + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() + val currency = cryptoCurrencyStatus.currency + val amountValue = amountState.amountTextField.cryptoAmount.value + val validationError = amountValue?.let { + validateTransactionUseCase( + userWalletId = userWalletId, + amount = amountValue.convertToSdkAmount(cryptoCurrencyStatus.currency), + fee = feeState.fee, + memo = null, + destination = "", + network = cryptoCurrencyStatus.currency.network, + ).leftOrNull() } + addExistentialWarningNotification( + existentialDeposit = currencyCheck.existentialDeposit, + feeAmount = feeState.fee?.amount?.value.orZero(), + receivedAmount = sendingAmount, + cryptoCurrencyStatus = cryptoCurrencyStatus, + onReduceClick = clickIntents::onAmountReduceByClick, + ) + addFeeCoverageNotification( + isFeeCoverage = isFeeCoverage, + amountField = amountState.amountTextField, + sendingValue = sendingAmount, + appCurrency = appCurrencyProvider(), + cryptoCurrencyStatus = cryptoCurrencyStatus, + ) + addValidateTransactionNotifications( + dustValue = currencyCheck.dustValue.orZero(), + fee = feeState.fee, + validationError = validationError, + cryptoCurrency = currency, + onReduceClick = clickIntents::onAmountReduceToClick, + ) + + addHighFeeWarningNotification( + amountState.amountTextField.cryptoAmount.value.orZero(), + sendState.ignoreAmountReduce, + ) + addTooHighNotification(feeState.feeSelectorState) + addTooLowNotification(feeState) } - private fun MutableList.addHighFeeWarningNotification( + private fun MutableList.addHighFeeWarningNotification( sendAmount: BigDecimal, ignoreAmountReduce: Boolean, ) { @@ -293,61 +253,32 @@ internal class SendNotificationFactory( val isTotalBalance = sendAmount >= balance && balance > threshold if (!ignoreAmountReduce && isTotalBalance && isTezos) { add( - SendNotification.Warning.HighFeeError( + NotificationUM.Warning.HighFeeError( currencyName = cryptoCurrencyStatus.currency.name, amount = threshold.toPlainString(), onConfirmClick = { - clickIntents.onAmountReduceClick( + clickIntents.onAmountReduceByClick( reduceAmountBy = threshold, - clazz = SendNotification.Warning.HighFeeError::class.java, + reduceAmountByDiff = threshold, + notification = NotificationUM.Warning.HighFeeError::class.java, ) }, onCloseClick = { - clickIntents.onNotificationCancel(SendNotification.Warning.HighFeeError::class.java) + clickIntents.onNotificationCancel(NotificationUM.Warning.HighFeeError::class.java) }, ), ) } } - private suspend fun MutableList.addDustWarningNotificationForSpecificBlockchains( - feeValue: BigDecimal, - sendingAmount: BigDecimal, - ) { - val isCardano = BlockchainUtils.isCardano(cryptoCurrencyStatusProvider().currency.network.id.value) - - if (!isCardano) { - addDustWarningNotification(feeValue, sendingAmount) - } - } - - private fun checkDustLimits(feeAmount: BigDecimal, receivedAmount: BigDecimal, dustValue: BigDecimal): Boolean { - val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - val feeCurrencyStatus = feeCryptoCurrencyStatusProvider() ?: return false - - val change = when (cryptoCurrencyStatus.currency) { - is CryptoCurrency.Coin -> { - val balance = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO - balance - (feeAmount + receivedAmount) - } - is CryptoCurrency.Token -> { - val balance = feeCurrencyStatus.value.amount ?: BigDecimal.ZERO - balance - feeAmount - } - } - - val isChangeLowerThanDust = change < dustValue && change > BigDecimal.ZERO - return receivedAmount < dustValue || isChangeLowerThanDust - } - - private fun MutableList.addTooLowNotification(feeState: SendStates.FeeState) { + private fun MutableList.addTooLowNotification(feeState: SendStates.FeeState) { val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return val multipleFees = feeSelectorState.fees as? TransactionFee.Choosable ?: return val minimumValue = multipleFees.minimum.amount.value ?: return val customAmount = feeSelectorState.customValues.firstOrNull() ?: return val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals) if (feeSelectorState.selectedFee == FeeType.Custom && minimumValue > customValue) { - add(SendNotification.Warning.FeeTooLow) + add(NotificationUM.Warning.FeeTooLow) analyticsEventHandler.send( SendAnalyticEvents.NoticeTransactionDelays( cryptoCurrencyStatusProvider().currency.symbol, @@ -356,206 +287,11 @@ internal class SendNotificationFactory( } } - private fun MutableList.addTooHighNotification(feeSelectorState: FeeSelectorState) { + private fun MutableList.addTooHighNotification(feeSelectorState: FeeSelectorState) { if (feeSelectorState !is FeeSelectorState.Content) return checkIfFeeTooHigh(feeSelectorState) { diff -> - add(SendNotification.Warning.TooHigh(diff)) - } - } - - private suspend fun MutableList.addExceedsBalanceNotification(fee: Fee?) { - val feeValue = fee?.amount?.value ?: BigDecimal.ZERO - val userWalletId = userWalletProvider().walletId - val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - val feeCurrencyStatus = feeCryptoCurrencyStatusProvider() ?: return - - val warning = getBalanceNotEnoughForFeeWarningUseCase( - fee = feeValue, - userWalletId = userWalletId, - tokenStatus = cryptoCurrencyStatus, - coinStatus = feeCurrencyStatus, - ).getOrNull() ?: return - - val mergeFeeNetworkName = cryptoCurrencyStatus.shouldMergeFeeNetworkName() - when (warning) { - is CryptoCurrencyWarning.BalanceNotEnoughForFee -> { - add( - SendNotification.Error.ExceedsBalance( - networkIconId = warning.coinCurrency.networkIconResId, - networkName = warning.coinCurrency.name, - currencyName = cryptoCurrencyStatus.currency.name, - feeName = warning.coinCurrency.name, - feeSymbol = warning.coinCurrency.symbol, - mergeFeeNetworkName = mergeFeeNetworkName, - onClick = { - clickIntents.onTokenDetailsClick( - userWalletId = userWalletId, - currency = warning.coinCurrency, - ) - }, - ), - ) - analyticsEventHandler.send( - SendAnalyticEvents.NoticeNotEnoughFee( - token = cryptoCurrencyStatus.currency.symbol, - blockchain = cryptoCurrencyStatus.currency.network.name, - ), - ) - } - is CryptoCurrencyWarning.CustomTokenNotEnoughForFee -> { - val currency = warning.feeCurrency - add( - SendNotification.Error.ExceedsBalance( - networkIconId = currency?.networkIconResId ?: R.drawable.ic_alert_24, - currencyName = warning.currency.name, - feeName = warning.feeCurrencyName, - feeSymbol = warning.feeCurrencySymbol, - networkName = warning.networkName, - mergeFeeNetworkName = mergeFeeNetworkName, - onClick = currency?.let { - { - clickIntents.onTokenDetailsClick( - userWalletId, - currency, - ) - } - }, - ), - ) - analyticsEventHandler.send( - SendAnalyticEvents.NoticeNotEnoughFee( - token = warning.currency.symbol, - blockchain = warning.networkName, - ), - ) - } - else -> Unit - } - } - - // workaround for networks that users have misunderstanding - private fun CryptoCurrencyStatus.shouldMergeFeeNetworkName(): Boolean { - return Blockchain.fromNetworkId(this.currency.network.backendId) == Blockchain.Arbitrum - } - - private suspend fun MutableList.addValidateTransactionNotifications( - sendingAmount: BigDecimal, - fee: Fee?, - state: SendUiState, - ) { - val sendingCurrency = cryptoCurrencyStatusProvider().currency - - validateTransactionUseCase( - amount = sendingAmount.convertToSdkAmount(sendingCurrency), - fee = fee ?: return, - memo = state.recipientState?.memoTextField?.value, - destination = requireNotNull(state.recipientState?.addressTextField?.value), - userWalletId = userWalletProvider().walletId, - network = sendingCurrency.network, - ).fold( - ifLeft = { - when (it) { - is BlockchainSdkError.Cardano -> addCardanoTransactionValidationError( - error = it, - sendingCurrency = sendingCurrency, - ) - is BlockchainSdkError.Koinos -> addKoinosTransactionValidationError(error = it) - else -> return - } - }, - ifRight = { - (fee as? Fee.CardanoToken)?.let { - add( - SendNotification.Cardano.MinAdaValueCharged( - tokenName = sendingCurrency.name, - minAdaValue = it.minAdaValue.parseBigDecimal(sendingCurrency.decimals), - ), - ) - } - }, - ) - } - - private suspend fun MutableList.addCardanoTransactionValidationError( - error: BlockchainSdkError.Cardano, - sendingCurrency: CryptoCurrency, - ) { - when (error) { - BlockchainSdkError.Cardano.InsufficientMinAdaBalanceToSendToken -> { - add(SendNotification.Cardano.InsufficientBalanceToTransferToken(sendingCurrency.name)) - } - BlockchainSdkError.Cardano.InsufficientRemainingBalanceToWithdrawTokens -> { - when (sendingCurrency) { - is CryptoCurrency.Coin -> SendNotification.Cardano.InsufficientBalanceToTransferCoin - is CryptoCurrency.Token -> { - SendNotification.Cardano.InsufficientBalanceToTransferToken(sendingCurrency.name) - } - }.let(::add) - } - BlockchainSdkError.Cardano.InsufficientRemainingBalance, - BlockchainSdkError.Cardano.InsufficientSendingAdaAmount, - -> { - val dustValue = currencyChecksRepository.getDustValue( - userWalletId = userWalletProvider().walletId, - network = sendingCurrency.network, - ) ?: return - - add( - SendNotification.Error.MinimumAmountError( - amount = dustValue.parseBigDecimal(sendingCurrency.decimals), - ), - ) - } - } - } - - private fun MutableList.addKoinosTransactionValidationError(error: BlockchainSdkError.Koinos) { - when (error) { - is BlockchainSdkError.Koinos.InsufficientBalance -> { - add(SendNotification.Koinos.InsufficientBalance) - } - is BlockchainSdkError.Koinos.InsufficientMana -> { - add( - SendNotification.Koinos.InsufficientRecoverableMana( - mana = error.manaBalance ?: BigDecimal.ZERO, - maxMana = error.maxMana ?: BigDecimal.ZERO, - ), - ) - } - is BlockchainSdkError.Koinos.ManaFeeExceedsBalance -> { - add( - SendNotification.Koinos.ManaExceedsBalance( - availableKoinForTransfer = error.availableKoinForTransfer, - onReduceClick = { - clickIntents.onAmountReduceClick( - reduceAmountTo = error.availableKoinForTransfer, - clazz = SendNotification.Koinos.InsufficientRecoverableMana::class.java, - ) - }, - ), - ) - } - else -> {} - } - } - - private suspend fun MutableList.addDustWarningNotification( - feeValue: BigDecimal, - sendingAmount: BigDecimal, - ) { - val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - val dustValue = currencyChecksRepository.getDustValue( - userWalletProvider().walletId, - cryptoCurrencyStatus.currency.network, - ) ?: return - - if (checkDustLimits(feeValue, sendingAmount, dustValue)) { - add( - SendNotification.Error.MinimumAmountError( - amount = dustValue.parseBigDecimal(cryptoCurrencyStatus.currency.decimals), - ), - ) + add(NotificationUM.Warning.TooHigh(diff)) } } } \ No newline at end of file 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 aba37ba2ea..96985ed40d 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,6 +1,6 @@ package com.tangem.features.send.impl.presentation.state.fee -import com.tangem.features.send.impl.presentation.state.SendNotification +import com.tangem.common.ui.notifications.NotificationsFactory.addFeeUnreachableNotification 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 @@ -25,24 +25,10 @@ internal class FeeNotificationFactory( val feeState = state.getFeeState(stateRouterProvider().isEditState) ?: return@map persistentListOf() buildList { addFeeUnreachableNotification( - feeState.feeSelectorState, + feeError = (feeState.feeSelectorState as? FeeSelectorState.Error)?.error, + tokenName = state.cryptoCurrencyName, + onReload = clickIntents::feeReload, ) }.toImmutableList() } - - private fun MutableList.addFeeUnreachableNotification(feeSelectorState: FeeSelectorState) { - when (feeSelectorState) { - is FeeSelectorState.Error.TronAccountActivationError -> add( - SendNotification.Warning.TronAccountNotActivated( - feeSelectorState.tokenName, - ), - ) - is FeeSelectorState.Error.NetworkError -> add( - SendNotification.Warning.NetworkFeeUnreachable(clickIntents::feeReload), - ) - else -> { - /* do nothing */ - } - } - } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt index 6ee78a7939..20c93c2458 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt @@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.fee import androidx.compose.runtime.Immutable import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.domain.transaction.error.GetFeeError import com.tangem.features.send.impl.presentation.state.fields.SendTextField import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -17,10 +18,9 @@ internal sealed class FeeSelectorState { data object Loading : FeeSelectorState() - sealed class Error : FeeSelectorState() { - data object NetworkError : Error() - data class TronAccountActivationError(val tokenName: String) : Error() - } + data class Error( + val error: GetFeeError?, + ) : FeeSelectorState() } enum class FeeType { 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 3ff511170b..bf4385fd78 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 @@ -4,12 +4,13 @@ import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.extensions.isZero import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.AmountType import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.transaction.error.GetFeeError 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.StateRouter @@ -102,13 +103,13 @@ internal class FeeStateFactory( ) } - fun onFeeOnErrorState(feeError: FeeSelectorState.Error): SendUiState { + fun onFeeOnErrorState(feeError: GetFeeError?): SendUiState { val state = currentStateProvider() val isEditState = stateRouterProvider().isEditState return state.copyWrapped( isEditState = isEditState, feeState = state.getFeeState(isEditState)?.copy( - feeSelectorState = feeError, + feeSelectorState = FeeSelectorState.Error(feeError), ), sendState = state.sendState?.copy( isPrimaryButtonEnabled = false, @@ -152,7 +153,7 @@ internal class FeeStateFactory( ) } - fun getFeeNotificationState(notifications: ImmutableList): SendUiState { + fun getFeeNotificationState(notifications: ImmutableList): SendUiState { val state = currentStateProvider() val isEditState = stateRouterProvider().isEditState val feeState = state.getFeeState(isEditState) ?: return state @@ -167,7 +168,7 @@ internal class FeeStateFactory( private fun isPrimaryButtonEnabled( feeState: SendStates.FeeState, - notifications: ImmutableList, + notifications: ImmutableList, ): Boolean { val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return false val customValue = feeSelectorState.customValues.firstOrNull() @@ -178,7 +179,7 @@ internal class FeeStateFactory( } else { false } - val noErrors = notifications.none { it is SendNotification.Error } + val noErrors = notifications.none { it is NotificationUM.Error } return noErrors && (isNotEmptyCustom || isNotCustom) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/FeeStatePreviewData.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/FeeStatePreviewData.kt index 1256218851..02cb054cd8 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/FeeStatePreviewData.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/FeeStatePreviewData.kt @@ -103,6 +103,6 @@ internal object FeeStatePreviewData { ) val errorFeeState = feeState.copy( - feeSelectorState = FeeSelectorState.Error.NetworkError, + feeSelectorState = FeeSelectorState.Error(null), ) } \ 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 8eab2cbac3..7e17a6e883 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 @@ -1,9 +1,8 @@ package com.tangem.features.send.impl.presentation.state.previewdata +import com.tangem.common.ui.notifications.NotificationUM import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource -import com.tangem.features.send.impl.presentation.state.SendNotification import com.tangem.features.send.impl.presentation.state.fee.FeeType import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import java.math.BigDecimal @@ -24,11 +23,12 @@ internal object SendClickIntentsStub : SendClickIntents { override fun onFailedTxEmailClick(errorMessage: String) {} - override fun onTokenDetailsClick(userWalletId: UserWalletId, currency: CryptoCurrency) {} + override fun onTokenDetailsClick(currency: CryptoCurrency) {} override fun onAmountValueChange(value: String) {} override fun onCurrencyChangeClick(isFiat: Boolean) {} + override fun onAmountNext() {} override fun onMaxValueClick() {} @@ -61,13 +61,13 @@ internal object SendClickIntentsStub : SendClickIntents { override fun onShareClick() {} - override fun onAmountReduceClick( - reduceAmountBy: BigDecimal?, - reduceAmountByDiff: BigDecimal?, - reduceAmountTo: BigDecimal?, - clazz: Class, - ) { - } + override fun onAmountReduceByClick( + reduceAmountBy: BigDecimal, + reduceAmountByDiff: BigDecimal, + notification: Class, + ) {} - override fun onNotificationCancel(clazz: Class) {} + override fun onAmountReduceToClick(reduceAmountTo: BigDecimal, notification: Class) {} + + override fun onNotificationCancel(clazz: Class) {} } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/common/Notifications.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/common/Notifications.kt index 929cfdf3b2..b6d93d00c8 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/common/Notifications.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/common/Notifications.kt @@ -5,14 +5,14 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.ui.Modifier +import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.res.TangemTheme -import com.tangem.features.send.impl.presentation.state.SendNotification import kotlinx.collections.immutable.ImmutableList @OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.notifications( - notifications: ImmutableList, + notifications: ImmutableList, modifier: Modifier = Modifier, hasPaddingAbove: Boolean = false, isClickDisabled: Boolean = false, @@ -33,17 +33,18 @@ internal fun LazyListScope.notifications( .padding(top = topPadding) .animateItemPlacement(), containerColor = when (item) { - is SendNotification.Error.ExceedsBalance, - is SendNotification.Warning.NetworkFeeUnreachable, - is SendNotification.Warning.HighFeeError, + is NotificationUM.Error.TokenExceedsBalance, + is NotificationUM.Warning.NetworkFeeUnreachable, + is NotificationUM.Warning.HighFeeError, -> TangemTheme.colors.background.action else -> TangemTheme.colors.button.disabled }, iconTint = when (item) { - is SendNotification.Error.ExceedsBalance, - is SendNotification.Warning, + is NotificationUM.Error.TokenExceedsBalance, + is NotificationUM.Warning, -> null - is SendNotification.Error -> TangemTheme.colors.icon.warning + is NotificationUM.Error -> TangemTheme.colors.icon.warning + is NotificationUM.Info -> null }, isEnabled = !isClickDisabled, ) 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 b7bc64d493..57247c99ba 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 @@ -1,10 +1,9 @@ package com.tangem.features.send.impl.presentation.viewmodel import com.tangem.common.ui.amountScreen.AmountScreenClickIntents +import com.tangem.common.ui.notifications.NotificationUM import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource -import com.tangem.features.send.impl.presentation.state.SendNotification import com.tangem.features.send.impl.presentation.state.fee.FeeType import java.math.BigDecimal @@ -25,7 +24,7 @@ internal interface SendClickIntents : AmountScreenClickIntents { fun onFailedTxEmailClick(errorMessage: String) - fun onTokenDetailsClick(userWalletId: UserWalletId, currency: CryptoCurrency) + fun onTokenDetailsClick(currency: CryptoCurrency) // region Recipient fun onRecipientAddressValueChange(value: String, type: EnterAddressSource? = null) @@ -58,13 +57,14 @@ internal interface SendClickIntents : AmountScreenClickIntents { fun onShareClick() - fun onAmountReduceClick( - reduceAmountBy: BigDecimal? = null, - reduceAmountByDiff: BigDecimal? = reduceAmountBy, - reduceAmountTo: BigDecimal? = null, - clazz: Class, + fun onAmountReduceByClick( + reduceAmountBy: BigDecimal, + reduceAmountByDiff: BigDecimal, + notification: Class, ) - fun onNotificationCancel(clazz: Class) + fun onAmountReduceToClick(reduceAmountTo: BigDecimal, notification: Class) + + fun onNotificationCancel(clazz: Class) // endregion } \ 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 67de572bab..47c9697faa 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 @@ -11,6 +11,7 @@ import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.routing.AppRoute import com.tangem.common.routing.bundle.unbundle import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase @@ -28,7 +29,6 @@ import com.tangem.domain.tokens.* import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.error.ValidateAddressError import com.tangem.domain.transaction.usecase.* @@ -47,7 +47,6 @@ import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents import com.tangem.features.send.impl.presentation.analytics.SendScreenSource import com.tangem.features.send.impl.presentation.analytics.utils.SendScreenAnalyticSender import com.tangem.features.send.impl.presentation.domain.AvailableWallet -import com.tangem.features.send.impl.presentation.errors.FeeErrorStateMapper import com.tangem.features.send.impl.presentation.state.* import com.tangem.features.send.impl.presentation.state.amount.AmountStateFactory import com.tangem.features.send.impl.presentation.state.confirm.SendNotificationFactory @@ -99,7 +98,7 @@ internal class SendViewModel @Inject constructor( private val validateWalletMemoUseCase: ValidateWalletMemoUseCase, @DelayedWork private val coroutineScope: CoroutineScope, validateTransactionUseCase: ValidateTransactionUseCase, - currencyChecksRepository: CurrencyChecksRepository, + getCurrencyCheckUseCase: GetCurrencyCheckUseCase, isFeeApproximateUseCase: IsFeeApproximateUseCase, getBalanceNotEnoughForFeeWarningUseCase: GetBalanceNotEnoughForFeeWarningUseCase, savedStateHandle: SavedStateHandle, @@ -124,8 +123,6 @@ internal class SendViewModel @Inject constructor( var stateRouter: StateRouter by Delegates.notNull() private set - private val feeErrorHandler = FeeErrorStateMapper() - private val stateFactory = SendStateFactory( clickIntents = this, stateRouterProvider = Provider { stateRouter }, @@ -174,18 +171,18 @@ internal class SendViewModel @Inject constructor( ) private val sendNotificationFactory = SendNotificationFactory( + analyticsEventHandler = analyticsEventHandler, + validateTransactionUseCase = validateTransactionUseCase, + getCurrencyCheckUseCase = getCurrencyCheckUseCase, + getBalanceNotEnoughForFeeWarningUseCase = getBalanceNotEnoughForFeeWarningUseCase, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, feeCryptoCurrencyStatusProvider = Provider { feeCryptoCurrencyStatus }, currentStateProvider = Provider { uiState.value }, - userWalletProvider = Provider { userWallet }, stateRouterProvider = Provider { stateRouter }, isSubtractAvailableProvider = Provider { isAmountSubtractAvailable }, appCurrencyProvider = Provider(selectedAppCurrencyFlow::value), - currencyChecksRepository = currencyChecksRepository, clickIntents = this, - analyticsEventHandler = analyticsEventHandler, - getBalanceNotEnoughForFeeWarningUseCase = getBalanceNotEnoughForFeeWarningUseCase, - validateTransactionUseCase = validateTransactionUseCase, + userWalletId = userWalletId, ) private val sendScreenAnalyticSender by lazy(LazyThreadSafetyMode.NONE) { @@ -537,8 +534,7 @@ internal class SendViewModel @Inject constructor( ) } - override fun onTokenDetailsClick(userWalletId: UserWalletId, currency: CryptoCurrency) = - innerRouter.openTokenDetails(userWalletId, currency) + override fun onTokenDetailsClick(currency: CryptoCurrency) = innerRouter.openTokenDetails(userWalletId, currency) private fun onFeeNext(): Boolean { val feeState = uiState.value.getFeeState(stateRouter.isEditState) @@ -730,7 +726,7 @@ internal class SendViewModel @Inject constructor( private fun onFeeLoadFailed(isShowStatus: Boolean, loadFeeError: GetFeeError?) { if (isShowStatus) { uiState.value = feeStateFactory.onFeeOnErrorState( - feeErrorHandler.getFeeError(loadFeeError, cryptoCurrency.name), + loadFeeError, ) } } @@ -814,26 +810,27 @@ internal class SendViewModel @Inject constructor( analyticsEventHandler.send(SendAnalyticEvents.ShareButtonClicked) } - override fun onAmountReduceClick( - reduceAmountBy: BigDecimal?, - reduceAmountByDiff: BigDecimal?, - reduceAmountTo: BigDecimal?, - clazz: Class, - ) { - uiState.value = when { - reduceAmountBy != null && reduceAmountByDiff != null -> amountStateFactory.getOnAmountReduceByState( - reduceAmountBy = reduceAmountBy, - reduceAmountByDiff = reduceAmountByDiff, - ) - reduceAmountTo != null -> amountStateFactory.getOnAmountReduceToState(reduceAmountTo) - else -> return - } - - uiState.value = sendNotificationFactory.dismissNotificationState(clazz) + override fun onAmountReduceToClick(reduceAmountTo: BigDecimal, notification: Class) { + uiState.value = amountStateFactory.getOnAmountReduceToState(reduceAmountTo) + uiState.value = sendNotificationFactory.dismissNotificationState(notification) updateNotifications() } - override fun onNotificationCancel(clazz: Class) { + override fun onAmountReduceByClick( + reduceAmountBy: BigDecimal, + reduceAmountByDiff: BigDecimal, + notification: Class, + ) { + uiState.value = amountStateFactory.getOnAmountReduceByState( + reduceAmountBy = reduceAmountBy, + reduceAmountByDiff = reduceAmountByDiff, + ) + + uiState.value = sendNotificationFactory.dismissNotificationState(notification) + updateNotifications() + } + + override fun onNotificationCancel(clazz: Class) { uiState.value = sendNotificationFactory.dismissNotificationState(clazz = clazz, isIgnored = true) } @@ -972,7 +969,7 @@ internal class SendViewModel @Inject constructor( private fun onCheckFeeUpdate() { val sendState = uiState.value.sendState ?: return val isSuccess = sendState.isSuccess - val noErrorNotifications = sendState.notifications.none { it is SendNotification.Error } + val noErrorNotifications = sendState.notifications.none { it is NotificationUM.Error } if (!isSuccess && noErrorNotifications) { viewModelScope.launch { diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt index bd7988cd8f..8d5c155cf7 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/AddStakingNotificationsTransformer.kt @@ -136,13 +136,11 @@ internal class AddStakingNotificationsTransformer( val cryptoCurrency = cryptoCurrencyStatus.currency val network = cryptoCurrency.network - if (feeError != null) { - addFeeUnreachableNotification( - feeError = feeError, - tokenName = cryptoCurrencyStatusProvider().currency.name, - onReload = onReload, - ) - } + addFeeUnreachableNotification( + feeError = feeError, + tokenName = cryptoCurrencyStatusProvider().currency.name, + onReload = onReload, + ) addStakeExceedBalanceNotification( feeAmount = feeValue, sendingAmount = sendingAmount,