From 244af774f488ae005ef01f0c9f34f800f7511bad Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 28 Mar 2024 16:34:17 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../presentation/state/SendNotification.kt | 54 +++++++- .../impl/presentation/state/SendUiState.kt | 5 +- .../state/amount/AmountNotificationFactory.kt | 38 ++++++ .../state/amount/AmountStateFactory.kt | 9 ++ .../state/amount/SendAmountStateConverter.kt | 2 + .../{ => confirm}/SendNotificationFactory.kt | 92 ++++++++++++- .../state/fee/FeeNotificationFactory.kt | 103 +------------- .../presentation/state/fee/FeeStateFactory.kt | 7 +- .../state/fee/SendFeeNotification.kt | 88 ------------ .../previewdata/AmountStatePreviewData.kt | 2 + .../presentation/ui/amount/AmountButtons.kt | 109 +++++++++++++++ .../ui/amount/AmountFieldContainer.kt | 74 +++++----- .../ui/amount/SendAmountContent.kt | 128 +++++------------- .../ui/fee/SendSpeedAndFeeContent.kt | 10 +- .../presentation/ui/fee/SendSpeedSelector.kt | 5 +- .../impl/presentation/ui/send/SendContent.kt | 8 +- .../presentation/viewmodel/SendViewModel.kt | 28 +++- 17 files changed, 424 insertions(+), 338 deletions(-) create mode 100644 features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountNotificationFactory.kt rename features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/{ => confirm}/SendNotificationFactory.kt (74%) delete mode 100644 features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt create mode 100644 features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountButtons.kt 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 index 64817ff69e..752a235ce0 100644 --- 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 @@ -11,13 +11,14 @@ 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 = R.drawable.ic_alert_24, + iconResId = iconResId, buttonsState = buttonState, onCloseClick = onCloseClick, ), @@ -59,6 +60,41 @@ internal sealed class SendNotification(val config: NotificationConfig) { 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, + ) + }, + ) } sealed class Warning( @@ -98,5 +134,21 @@ internal sealed class SendNotification(val config: NotificationConfig) { 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, + ), + ) } } \ No newline at end of file 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 5ac0949edf..bb10ce4ab4 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 @@ -10,7 +10,6 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent import com.tangem.features.send.impl.presentation.state.amount.SendAmountSegmentedButtonsConfig import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState -import com.tangem.features.send.impl.presentation.state.fee.SendFeeNotification import com.tangem.features.send.impl.presentation.state.fields.SendTextField import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import kotlinx.collections.immutable.ImmutableList @@ -50,6 +49,8 @@ internal sealed class SendStates { val tokenIconState: TokenIconState, val segmentedButtonConfig: PersistentList, val amountTextField: SendTextField.AmountField, + val notifications: ImmutableList, + val isFeeLoading: Boolean, ) : SendStates() /** Recipient state */ @@ -75,7 +76,7 @@ internal sealed class SendStates { val rate: BigDecimal?, val appCurrency: AppCurrency, val isFeeApproximate: Boolean, - val notifications: ImmutableList, + val notifications: ImmutableList, ) : SendStates() /** Send state */ diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountNotificationFactory.kt new file mode 100644 index 0000000000..222a8704cf --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountNotificationFactory.kt @@ -0,0 +1,38 @@ +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.SendUiStateType +import com.tangem.features.send.impl.presentation.state.StateRouter +import com.tangem.features.send.impl.presentation.state.SendNotification +import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState +import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents +import com.tangem.utils.Provider +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map + +internal class AmountNotificationFactory( + private val stateRouterProvider: Provider, + private val currentStateProvider: Provider, + private val clickIntents: SendClickIntents, +) { + + fun create() = stateRouterProvider().currentState + .filter { it.type == SendUiStateType.Amount } + .map { + buildList { + addFeeUnreachableNotification() + }.toImmutableList() + } + + private fun MutableList.addFeeUnreachableNotification() { + val state = currentStateProvider() + val feeState = state.feeState ?: return + + if (feeState.feeSelectorState is FeeSelectorState.Error) { + add( + SendNotification.Warning.NetworkFeeUnreachable(clickIntents::feeReload), + ) + } + } +} \ No newline at end of file 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 6f56b6aff3..626da0c89b 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 @@ -1,10 +1,12 @@ package com.tangem.features.send.impl.presentation.state.amount import com.tangem.domain.tokens.model.CryptoCurrencyStatus +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.fields.SendAmountFieldChangeConverter import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldMaxAmountConverter import com.tangem.utils.Provider +import kotlinx.collections.immutable.ImmutableList /** * Factory to produce amount state for [SendUiState] @@ -41,4 +43,11 @@ internal class AmountStateFactory( } fun getOnCurrencyChangedState(isFiat: Boolean) = amountCurrencyConverter.convert(isFiat) + + fun getAmountNotificationState(notifications: ImmutableList): SendUiState { + val state = currentStateProvider() + return state.copy( + amountState = state.amountState?.copy(notifications = notifications), + ) + } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt index e1c163e2d4..9e393e8bae 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt @@ -38,6 +38,8 @@ internal class SendAmountStateConverter( tokenIconState = iconStateConverter.convert(status), amountTextField = sendAmountFieldConverter.convert(value), isPrimaryButtonEnabled = false, + notifications = persistentListOf(), + isFeeLoading = false, segmentedButtonConfig = if (status.value.fiatRate.isNullOrZero()) { persistentListOf() } else { diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt similarity index 74% rename from features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt rename to features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt index 0064f20684..85402bfc8f 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt @@ -1,15 +1,26 @@ -package com.tangem.features.send.impl.presentation.state +package com.tangem.features.send.impl.presentation.state.confirm import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee 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.parseToBigDecimal +import com.tangem.domain.common.extensions.fromNetworkId +import com.tangem.domain.tokens.GetBalanceNotEnoughForFeeWarningUseCase import com.tangem.domain.tokens.model.CryptoCurrency 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.wallets.models.UserWallet +import com.tangem.features.send.impl.R 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.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 import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.FeeType import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents @@ -33,6 +44,7 @@ internal class SendNotificationFactory( private val stateRouterProvider: Provider, private val clickIntents: SendClickIntents, private val analyticsEventHandler: AnalyticsEventHandler, + private val getBalanceNotEnoughForFeeWarningUseCase: GetBalanceNotEnoughForFeeWarningUseCase, ) { fun create(): Flow> = stateRouterProvider().currentState @@ -47,6 +59,7 @@ internal class SendNotificationFactory( buildList { // errors addExceedBalanceNotification(feeAmount, sendAmount) + addExceedsBalanceNotification(feeState.fee) addInvalidAmountNotification(sendState.isSubtract, sendAmount) addMinimumAmountErrorNotification(feeAmount, sendAmount) addDustWarningNotification(feeAmount, sendAmount) @@ -272,6 +285,83 @@ internal class SendNotificationFactory( } } + private suspend fun MutableList.addExceedsBalanceNotification(fee: Fee?) { + val feeValue = fee?.amount?.value ?: BigDecimal.ZERO + val userWalletId = userWalletProvider().walletId + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() + + val warning = getBalanceNotEnoughForFeeWarningUseCase( + fee = feeValue, + userWalletId = userWalletId, + tokenStatus = cryptoCurrencyStatus, + coinStatus = coinCryptoCurrencyStatusProvider(), + ).fold( + ifLeft = { null }, + ifRight = { it }, + ) ?: 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 + } + companion object { private const val CARDANO_MINIMUM = "1" private const val DOGECOIN_MINIMUM = "0.01" 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 d3fcad540b..aee69c85fb 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,22 +1,12 @@ package com.tangem.features.send.impl.presentation.state.fee -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee -import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.core.ui.extensions.networkIconResId import com.tangem.core.ui.utils.parseToBigDecimal -import com.tangem.domain.common.extensions.fromNetworkId -import com.tangem.domain.tokens.GetBalanceNotEnoughForFeeWarningUseCase -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.features.send.impl.R -import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents 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.fields.SendTextField +import com.tangem.features.send.impl.presentation.state.SendNotification import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.utils.Provider import com.tangem.utils.toFormattedString @@ -28,14 +18,9 @@ import java.math.BigDecimal @Suppress("LongParameterList") internal class FeeNotificationFactory( - private val coinCryptoCurrencyStatusProvider: Provider, - private val cryptoCurrencyStatusProvider: Provider, private val currentStateProvider: Provider, - private val userWalletProvider: Provider, private val stateRouterProvider: Provider, private val clickIntents: SendClickIntents, - private val getBalanceNotEnoughForFeeWarningUseCase: GetBalanceNotEnoughForFeeWarningUseCase, - private val analyticsEventHandler: AnalyticsEventHandler, ) { fun create() = stateRouterProvider().currentState @@ -53,19 +38,18 @@ internal class FeeNotificationFactory( val customFee = feeSelectorState.customValues val selectedFee = feeSelectorState.selectedFee addTooHighNotification(feeSelectorState.fees, selectedFee, customFee) - addExceedsBalanceNotification(feeState.fee) } } }.toImmutableList() } - private fun MutableList.addFeeUnreachableNotification(feeSelectorState: FeeSelectorState) { + private fun MutableList.addFeeUnreachableNotification(feeSelectorState: FeeSelectorState) { if (feeSelectorState is FeeSelectorState.Error) { - add(SendFeeNotification.Warning.NetworkFeeUnreachable(clickIntents::feeReload)) + add(SendNotification.Warning.NetworkFeeUnreachable(clickIntents::feeReload)) } } - private fun MutableList.addTooHighNotification( + private fun MutableList.addTooHighNotification( transactionFee: TransactionFee, selectedFee: FeeType, customFee: List, @@ -76,87 +60,10 @@ internal class FeeNotificationFactory( val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals) val diff = customValue / highValue if (selectedFee == FeeType.Custom && diff > FEE_MAX_DIFF) { - add(SendFeeNotification.Warning.TooHigh(diff.toFormattedString(HIGH_FEE_DIFF_DECIMALS))) + add(SendNotification.Warning.TooHigh(diff.toFormattedString(HIGH_FEE_DIFF_DECIMALS))) } } - private suspend fun MutableList.addExceedsBalanceNotification(fee: Fee?) { - val feeValue = fee?.amount?.value ?: BigDecimal.ZERO - val userWalletId = userWalletProvider().walletId - val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - - val warning = getBalanceNotEnoughForFeeWarningUseCase( - fee = feeValue, - userWalletId = userWalletId, - tokenStatus = cryptoCurrencyStatus, - coinStatus = coinCryptoCurrencyStatusProvider(), - ).fold( - ifLeft = { null }, - ifRight = { it }, - ) ?: return - - val mergeFeeNetworkName = cryptoCurrencyStatus.shouldMergeFeeNetworkName() - when (warning) { - is CryptoCurrencyWarning.BalanceNotEnoughForFee -> { - add( - SendFeeNotification.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( - SendFeeNotification.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 - } - companion object { private val FEE_MAX_DIFF = BigDecimal(5) private const val HIGH_FEE_DIFF_DECIMALS = 0 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 6a2804103f..d2339a3de9 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,6 +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.viewmodel.SendClickIntents @@ -111,7 +112,7 @@ internal class FeeStateFactory( ) } - fun getFeeNotificationState(notifications: ImmutableList): SendUiState { + fun getFeeNotificationState(notifications: ImmutableList): SendUiState { val state = currentStateProvider() return state.copy( feeState = state.feeState?.copy( @@ -123,7 +124,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() @@ -134,7 +135,7 @@ internal class FeeStateFactory( } else { false } - val noErrors = notifications.none { it is SendFeeNotification.Error } + val noErrors = notifications.none { it is SendNotification.Error } return noErrors && (isNotEmptyCustom || isNotCustom) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt deleted file mode 100644 index b3a2846e5f..0000000000 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.tangem.features.send.impl.presentation.state.fee - -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.features.send.impl.R - -sealed class SendFeeNotification(val config: NotificationConfig) { - - sealed class Warning( - val title: TextReference, - val subtitle: TextReference, - val buttonsState: NotificationConfig.ButtonsState? = null, - ) : SendFeeNotification( - config = NotificationConfig( - title = title, - subtitle = subtitle, - iconResId = R.drawable.img_attention_20, - buttonsState = buttonsState, - ), - ) { - 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, - ), - ) - } - - sealed class Error( - val title: TextReference, - val subtitle: TextReference, - val iconResId: Int, - val buttonsState: NotificationConfig.ButtonsState? = null, - ) : SendFeeNotification( - config = NotificationConfig( - title = title, - subtitle = subtitle, - iconResId = iconResId, - buttonsState = buttonsState, - ), - ) { - 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, - buttonsState = onClick?.let { - NotificationConfig.ButtonsState.SecondaryButtonConfig( - text = resourceReference( - R.string.common_buy_currency, - wrappedList( - if (mergeFeeNetworkName) { - "$currencyName ($feeSymbol)" - } else { - feeName - }, - ), - ), - onClick = onClick, - ) - }, - ) - } -} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/AmountStatePreviewData.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/AmountStatePreviewData.kt index 8423ec20eb..43d0d75572 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/AmountStatePreviewData.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/AmountStatePreviewData.kt @@ -22,6 +22,8 @@ internal object AmountStatePreviewData { walletBalance = stringReference("123.123"), tokenIconState = TokenIconState.Loading, segmentedButtonConfig = persistentListOf(), + notifications = persistentListOf(), + isFeeLoading = false, amountTextField = SendTextField.AmountField( value = "123.123123123123123123", onValueChange = {}, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountButtons.kt new file mode 100644 index 0000000000..68573bf3c7 --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountButtons.kt @@ -0,0 +1,109 @@ +package com.tangem.features.send.impl.presentation.ui.amount + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.res.stringResource +import com.tangem.core.ui.components.SecondaryButton +import com.tangem.core.ui.components.SpacerWMax +import com.tangem.core.ui.components.buttons.common.TangemButtonSize +import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons +import com.tangem.core.ui.components.currency.fiaticon.FiatIcon +import com.tangem.core.ui.components.currency.tokenicon.TokenIcon +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.send.impl.R +import com.tangem.features.send.impl.presentation.state.amount.SendAmountSegmentedButtonsConfig +import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents +import kotlinx.collections.immutable.PersistentList + +private const val AMOUNT_BUTTONS_KEY = "amountButtonsKey" + +internal fun LazyListScope.buttons( + segmentedButtonConfig: PersistentList, + clickIntents: SendClickIntents, +) { + item( + key = AMOUNT_BUTTONS_KEY, + ) { + val hapticFeedback = LocalHapticFeedback.current + Row( + modifier = Modifier.padding(top = TangemTheme.dimens.spacing12), + ) { + if (segmentedButtonConfig.isNotEmpty()) { + SegmentedButtons( + modifier = Modifier + .weight(1f) + .height(TangemTheme.dimens.size40), + config = segmentedButtonConfig, + showIndication = false, + onClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + clickIntents.onCurrencyChangeClick(it.isFiat) + }, + ) { + SendAmountCurrencyButton(it) + } + } else { + SpacerWMax() + } + SecondaryButton( + text = stringResource(R.string.send_max_amount), + onClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + clickIntents.onMaxValueClick() + }, + size = TangemButtonSize.Text, + shape = RoundedCornerShape(TangemTheme.dimens.radius26), + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing8) + .height(TangemTheme.dimens.size40), + ) + } + } +} + +@Composable +private fun SendAmountCurrencyButton(button: SendAmountSegmentedButtonsConfig) { + Row( + modifier = Modifier + .fillMaxSize() + .padding( + horizontal = TangemTheme.dimens.spacing10, + ), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + if (button.isFiat) { + FiatIcon( + url = button.iconUrl, + size = TangemTheme.dimens.size18, + modifier = Modifier.size(TangemTheme.dimens.size18), + ) + } else { + button.iconState?.let { + TokenIcon( + state = it, + shouldDisplayNetwork = false, + modifier = Modifier + .size(TangemTheme.dimens.size18), + ) + } + } + Text( + text = button.title.resolveReference(), + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.button, + modifier = Modifier + .padding( + start = TangemTheme.dimens.spacing8, + ), + ) + } +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountFieldContainer.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountFieldContainer.kt index bc0887866a..9b44cc2e1f 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountFieldContainer.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountFieldContainer.kt @@ -5,9 +5,9 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text -import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -18,50 +18,52 @@ import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.features.send.impl.presentation.state.SendStates -@Composable -internal fun AmountFieldContainer( +private const val AMOUNT_FIELD_KEY = "amountFieldKey" + +internal fun LazyListScope.amountField( amountState: SendStates.AmountState, isBalanceHiding: Boolean, modifier: Modifier = Modifier, ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier - .fillMaxWidth() - .padding(horizontal = TangemTheme.dimens.spacing16) - .clip(RoundedCornerShape(TangemTheme.dimens.radius16)) - .background(TangemTheme.colors.background.action), - ) { - Text( - text = amountState.walletName, - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.tertiary, - modifier = Modifier - .padding(top = TangemTheme.dimens.spacing14), - ) - - val balance = if (isBalanceHiding) STARS else amountState.walletBalance.resolveReference() - AnimatedContent( - targetState = balance, - label = "Hide Balance Animation", + item(key = AMOUNT_FIELD_KEY) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier + .fillMaxWidth() + .clip(RoundedCornerShape(TangemTheme.dimens.radius16)) + .background(TangemTheme.colors.background.action), ) { Text( - text = it, - style = TangemTheme.typography.caption2, + text = amountState.walletName, + style = TangemTheme.typography.subtitle2, color = TangemTheme.colors.text.tertiary, - textAlign = TextAlign.Center, modifier = Modifier - .padding(top = TangemTheme.dimens.spacing2), + .padding(top = TangemTheme.dimens.spacing14), + ) + + val balance = if (isBalanceHiding) STARS else amountState.walletBalance.resolveReference() + AnimatedContent( + targetState = balance, + label = "Hide Balance Animation", + ) { + Text( + text = it, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Center, + modifier = Modifier + .padding(top = TangemTheme.dimens.spacing2), + ) + } + TokenIcon( + state = amountState.tokenIconState, + modifier = Modifier + .padding(top = TangemTheme.dimens.spacing32), + ) + AmountField( + sendField = amountState.amountTextField, + isFiat = amountState.amountTextField.isFiatValue, ) } - TokenIcon( - state = amountState.tokenIconState, - modifier = Modifier - .padding(top = TangemTheme.dimens.spacing32), - ) - AmountField( - sendField = amountState.amountTextField, - isFiat = amountState.amountTextField.isFiatValue, - ) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt index 143b2e1e16..3f3d18d3bd 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt @@ -1,27 +1,19 @@ package com.tangem.features.send.impl.presentation.ui.amount +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Text +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.hapticfeedback.HapticFeedbackType -import androidx.compose.ui.platform.LocalHapticFeedback -import androidx.compose.ui.res.stringResource -import com.tangem.core.ui.components.SecondaryButton -import com.tangem.core.ui.components.SpacerWMax -import com.tangem.core.ui.components.buttons.common.TangemButtonSize -import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons -import com.tangem.core.ui.components.currency.fiaticon.FiatIcon -import com.tangem.core.ui.components.currency.tokenicon.TokenIcon -import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.res.TangemTheme -import com.tangem.features.send.impl.R +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.amount.SendAmountSegmentedButtonsConfig import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents +import kotlinx.collections.immutable.ImmutableList @Composable internal fun SendAmountContent( @@ -30,88 +22,38 @@ internal fun SendAmountContent( clickIntents: SendClickIntents, ) { if (amountState == null) return - val hapticFeedback = LocalHapticFeedback.current - Column( + LazyColumn( modifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing16) .background(TangemTheme.colors.background.tertiary), ) { - AmountFieldContainer(amountState = amountState, isBalanceHiding = isBalanceHiding) - Row( - modifier = Modifier - .padding( - top = TangemTheme.dimens.spacing12, - start = TangemTheme.dimens.spacing16, - end = TangemTheme.dimens.spacing16, - ), - ) { - if (amountState.segmentedButtonConfig.isNotEmpty()) { - SegmentedButtons( - modifier = Modifier - .weight(1f) - .height(TangemTheme.dimens.size40), - config = amountState.segmentedButtonConfig, - showIndication = false, - onClick = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - clickIntents.onCurrencyChangeClick(it.isFiat) - }, - ) { - SendAmountCurrencyButton(it) - } - } else { - SpacerWMax() - } - SecondaryButton( - text = stringResource(R.string.send_max_amount), - onClick = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - clickIntents.onMaxValueClick() - }, - size = TangemButtonSize.Text, - shape = RoundedCornerShape(TangemTheme.dimens.radius26), - modifier = Modifier - .padding(start = TangemTheme.dimens.spacing8) - .height(TangemTheme.dimens.size40), - ) - } + amountField(amountState = amountState, isBalanceHiding = isBalanceHiding) + buttons(amountState.segmentedButtonConfig, clickIntents) + notifications(amountState.notifications) } } -@Composable -private fun SendAmountCurrencyButton(button: SendAmountSegmentedButtonsConfig) { - Row( - modifier = Modifier - .fillMaxSize() - .padding( - horizontal = TangemTheme.dimens.spacing10, - ), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically, - ) { - if (button.isFiat) { - FiatIcon( - url = button.iconUrl, - size = TangemTheme.dimens.size18, - modifier = Modifier.size(TangemTheme.dimens.size18), +@OptIn(ExperimentalFoundationApi::class) +internal fun LazyListScope.notifications(configs: ImmutableList, modifier: Modifier = Modifier) { + items( + items = configs, + key = { it::class.java }, + contentType = { it::class.java }, + itemContent = { + val bottomPadding = if (it == configs.last()) TangemTheme.dimens.spacing12 else TangemTheme.dimens.spacing0 + Notification( + config = it.config, + modifier = modifier + .animateItemPlacement() + .padding( + top = TangemTheme.dimens.spacing12, + bottom = bottomPadding, + ), + containerColor = when (it) { + is SendNotification.Warning.NetworkFeeUnreachable -> TangemTheme.colors.background.action + else -> TangemTheme.colors.button.disabled + }, ) - } else { - button.iconState?.let { - TokenIcon( - state = it, - shouldDisplayNetwork = false, - modifier = Modifier - .size(TangemTheme.dimens.size18), - ) - } - } - Text( - text = button.title.resolveReference(), - color = TangemTheme.colors.text.primary1, - style = TangemTheme.typography.button, - modifier = Modifier - .padding( - start = TangemTheme.dimens.spacing8, - ), - ) - } + }, + ) } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt index 444d03e9a6..f9e410b2ee 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt @@ -13,9 +13,9 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier 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 com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState -import com.tangem.features.send.impl.presentation.state.fee.SendFeeNotification import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import kotlinx.collections.immutable.ImmutableList @@ -56,7 +56,7 @@ private fun LazyListScope.feeSelector(state: SendStates.FeeState, clickIntents: @OptIn(ExperimentalFoundationApi::class) private fun LazyListScope.notifications( - configs: ImmutableList, + configs: ImmutableList, modifier: Modifier = Modifier, isLast: Boolean = false, ) { @@ -75,13 +75,13 @@ private fun LazyListScope.notifications( ) .animateItemPlacement(), containerColor = when (it) { - is SendFeeNotification.Error.ExceedsBalance, - is SendFeeNotification.Warning.NetworkFeeUnreachable, + is SendNotification.Error.ExceedsBalance, + is SendNotification.Warning.NetworkFeeUnreachable, -> TangemTheme.colors.background.action else -> TangemTheme.colors.button.disabled }, iconTint = when (it) { - is SendFeeNotification.Error.ExceedsBalance -> { + is SendNotification.Error.ExceedsBalance -> { if (it.config.buttonsState == null) { TangemTheme.colors.icon.warning } else { diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt index e0a3868ec1..dfaed7cb57 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt @@ -13,7 +13,6 @@ import androidx.compose.foundation.text.ClickableText import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -40,10 +39,10 @@ import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.core.ui.utils.BigDecimalFormatter.CAN_BE_LOWER_SIGN import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.features.send.impl.R +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.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.FeeType -import com.tangem.features.send.impl.presentation.state.fee.SendFeeNotification import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import java.math.BigDecimal @@ -113,7 +112,7 @@ internal fun SendSpeedSelector( visible = fees.normal is Fee.Ethereum, label = "Custom fee appearance animation", ) { - val showWarning = state.notifications.any { it is SendFeeNotification.Warning.TooHigh } + val showWarning = state.notifications.any { it is SendNotification.Warning.TooHigh } SendSpeedSelectorItem( titleRes = R.string.common_fee_selector_option_custom, iconRes = R.drawable.ic_edit_24, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt index 9dff71b1db..c5be1f7b27 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt @@ -146,12 +146,16 @@ internal fun LazyListScope.notifications(configs: ImmutableList TangemTheme.colors.background.action + is SendNotification.Error.ExceedsBalance, + is SendNotification.Warning.HighFeeError, + -> TangemTheme.colors.background.action else -> TangemTheme.colors.button.disabled }, iconTint = when (it) { + is SendNotification.Error.ExceedsBalance, + is SendNotification.Warning, + -> null is SendNotification.Error -> TangemTheme.colors.icon.warning - is SendNotification.Warning -> null }, ) }, 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 bc74ced905..fd799169cc 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 @@ -47,7 +47,9 @@ import com.tangem.features.send.impl.presentation.analytics.SendScreenSource import com.tangem.features.send.impl.presentation.analytics.utils.SendOnNextScreenAnalyticSender import com.tangem.features.send.impl.presentation.domain.AvailableWallet import com.tangem.features.send.impl.presentation.state.* +import com.tangem.features.send.impl.presentation.state.amount.AmountNotificationFactory import com.tangem.features.send.impl.presentation.state.amount.AmountStateFactory +import com.tangem.features.send.impl.presentation.state.confirm.SendNotificationFactory import com.tangem.features.send.impl.presentation.state.fee.* import com.tangem.lib.crypto.BlockchainUtils import com.tangem.utils.Provider @@ -146,15 +148,16 @@ internal class SendViewModel @Inject constructor( feeStateFactory = feeStateFactory, ) - private val feeNotificationFactory = FeeNotificationFactory( - cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, - coinCryptoCurrencyStatusProvider = Provider { coinCryptoCurrencyStatus }, + private val amountNotificationFactory = AmountNotificationFactory( + currentStateProvider = Provider { uiState }, + stateRouterProvider = Provider { stateRouter }, + clickIntents = this, + ) + + private val feeNotificationFactory = FeeNotificationFactory( currentStateProvider = Provider { uiState }, - userWalletProvider = Provider { userWallet }, stateRouterProvider = Provider { stateRouter }, clickIntents = this, - getBalanceNotEnoughForFeeWarningUseCase = getBalanceNotEnoughForFeeWarningUseCase, - analyticsEventHandler = analyticsEventHandler, ) private val sendNotificationFactory = SendNotificationFactory( @@ -166,6 +169,7 @@ internal class SendViewModel @Inject constructor( currencyChecksRepository = currencyChecksRepository, clickIntents = this, analyticsEventHandler = analyticsEventHandler, + getBalanceNotEnoughForFeeWarningUseCase = getBalanceNotEnoughForFeeWarningUseCase, ) private val sendOnNextScreenAnalyticSender by lazy(LazyThreadSafetyMode.NONE) { @@ -191,6 +195,7 @@ internal class SendViewModel @Inject constructor( private var memoValidationJobHolder = JobHolder() private var sendNotificationsJobHolder = JobHolder() private var feeNotificationsJobHolder = JobHolder() + private var amountNotificationsJobHolder = JobHolder() private var qrScannerJobHolder = JobHolder() private var sendIdleTimer = 0L @@ -465,6 +470,16 @@ internal class SendViewModel @Inject constructor( .saveIn(feeNotificationsJobHolder) } + private fun updateAmountNotifications() { + amountNotificationFactory.create() + .conflate() + .distinctUntilChanged() + .onEach { uiState = amountStateFactory.getAmountNotificationState(notifications = it) } + .flowOn(dispatchers.io) + .launchIn(viewModelScope) + .saveIn(amountNotificationsJobHolder) + } + // region screen state navigation override fun popBackStack() = stateRouter.popBackStack() override fun onBackClick() = stateRouter.onBackClick(isSuccess = uiState.sendState?.isSuccess == true) @@ -643,6 +658,7 @@ internal class SendViewModel @Inject constructor( ifLeft = { feeStateFactory.onFeeOnErrorState() }, ) ?: feeStateFactory.onFeeOnErrorState() updateFeeNotifications() + updateAmountNotifications() }.saveIn(feeJobHolder) }