Updated on 2026-08-14
This commit is contained in:
commit
c5fc1a4464
11 changed files with 641 additions and 10 deletions
|
|
@ -16,6 +16,8 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
|||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import com.tangem.lib.crypto.BlockchainUtils.getTezosThreshold
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isTezos
|
||||
import com.tangem.utils.extensions.isZero
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -240,6 +242,31 @@ object NotificationsFactory {
|
|||
}
|
||||
}
|
||||
|
||||
fun MutableList<NotificationUM>.addFeeCoverageNotification(
|
||||
isFeeCoverage: Boolean,
|
||||
enteredAmountValue: BigDecimal,
|
||||
sendingValue: BigDecimal,
|
||||
appCurrency: AppCurrency,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
) {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val fiatRate = cryptoCurrencyStatus.value.fiatRate
|
||||
|
||||
val cryptoDiff = enteredAmountValue.minus(sendingValue)
|
||||
if (isFeeCoverage) {
|
||||
add(
|
||||
NotificationUM.Warning.FeeCoverageNotification(
|
||||
cryptoAmount = cryptoDiff.format { crypto(cryptoCurrency).uncapped() },
|
||||
fiatAmount = getFiatString(
|
||||
value = cryptoDiff,
|
||||
rate = fiatRate,
|
||||
appCurrency = appCurrency,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun MutableList<NotificationUM>.addDustWarningNotification(
|
||||
dustValue: BigDecimal?,
|
||||
feeValue: BigDecimal,
|
||||
|
|
@ -425,6 +452,41 @@ object NotificationsFactory {
|
|||
add(NotificationUM.Solana.RentInfo(rentWarning))
|
||||
}
|
||||
|
||||
fun MutableList<NotificationUM>.addHighFeeWarningNotification(
|
||||
enteredAmountValue: BigDecimal,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
ignoreAmountReduce: Boolean,
|
||||
onReduceClick: (
|
||||
reduceAmountBy: BigDecimal,
|
||||
reduceAmountByDiff: BigDecimal,
|
||||
notification: Class<out NotificationUM>,
|
||||
) -> Unit,
|
||||
onCloseClick: (Class<out NotificationUM>) -> Unit,
|
||||
) {
|
||||
val balance = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
|
||||
val isTezos = isTezos(cryptoCurrencyStatus.currency.network.id.value)
|
||||
val threshold = getTezosThreshold()
|
||||
val isTotalBalance = enteredAmountValue >= balance && balance > threshold
|
||||
if (!ignoreAmountReduce && isTotalBalance && isTezos) {
|
||||
add(
|
||||
NotificationUM.Warning.HighFeeError(
|
||||
currencyName = cryptoCurrencyStatus.currency.name,
|
||||
amount = threshold.toPlainString(),
|
||||
onConfirmClick = {
|
||||
onReduceClick(
|
||||
threshold,
|
||||
threshold,
|
||||
NotificationUM.Warning.HighFeeError::class.java,
|
||||
)
|
||||
},
|
||||
onCloseClick = {
|
||||
onCloseClick(NotificationUM.Warning.HighFeeError::class.java)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkDustLimits(
|
||||
feeAmount: BigDecimal,
|
||||
sendingAmount: BigDecimal,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.features.send.v2.subcomponents.notifications
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.send.v2.subcomponents.notifications
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationsModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class NotificationsComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
params: Params,
|
||||
) : AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: NotificationsModel = getOrCreateModel(params)
|
||||
|
||||
val state: StateFlow<ImmutableList<NotificationUM>> = model.uiState
|
||||
|
||||
fun LazyListScope.content(
|
||||
state: ImmutableList<NotificationUM>,
|
||||
modifier: Modifier = Modifier,
|
||||
hasPaddingAbove: Boolean = false,
|
||||
isClickDisabled: Boolean = false,
|
||||
) {
|
||||
notifications(
|
||||
notifications = state,
|
||||
modifier = modifier,
|
||||
hasPaddingAbove = hasPaddingAbove,
|
||||
isClickDisabled = isClickDisabled,
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val analyticsCategoryName: String,
|
||||
val userWalletId: UserWalletId,
|
||||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val appCurrency: AppCurrency,
|
||||
val destinationAddress: String,
|
||||
val amountValue: BigDecimal,
|
||||
val reduceAmountBy: BigDecimal,
|
||||
val isIgnoreReduce: Boolean,
|
||||
val fee: Fee?,
|
||||
val feeError: GetFeeError?,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.features.send.v2.subcomponents.notifications
|
||||
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
interface NotificationsUpdateTrigger {
|
||||
/** Flow triggers notifications update */
|
||||
val updateTriggerFlow: Flow<NotificationData>
|
||||
|
||||
/** Flow returns whether there is error notifications */
|
||||
val hasErrorFlow: Flow<Boolean>
|
||||
|
||||
/** Trigger return callback with check result */
|
||||
suspend fun callbackHasError(hasError: Boolean)
|
||||
|
||||
/** Trigger fee check reload */
|
||||
suspend fun triggerUpdate(data: NotificationData)
|
||||
}
|
||||
|
||||
@Singleton
|
||||
internal class DefaultNotificationsUpdateTrigger @Inject constructor() : NotificationsUpdateTrigger {
|
||||
|
||||
private val _updateTriggerFlow = MutableSharedFlow<NotificationData>()
|
||||
override val updateTriggerFlow = _updateTriggerFlow.asSharedFlow()
|
||||
|
||||
private val _hasErrorFlow = MutableSharedFlow<Boolean>()
|
||||
override val hasErrorFlow = _hasErrorFlow.asSharedFlow()
|
||||
|
||||
override suspend fun callbackHasError(hasError: Boolean) {
|
||||
_hasErrorFlow.emit(hasError)
|
||||
}
|
||||
|
||||
override suspend fun triggerUpdate(data: NotificationData) {
|
||||
_updateTriggerFlow.emit(data)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.features.send.v2.subcomponents.notifications.analytics
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
|
||||
|
||||
internal sealed class NotificationsAnalyticEvents(
|
||||
category: String,
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category = category, event = event, params = params) {
|
||||
|
||||
abstract val categoryName: String
|
||||
|
||||
/** If not enough fee notification is present */
|
||||
data class NoticeNotEnoughFee(
|
||||
override val categoryName: String,
|
||||
val token: String,
|
||||
val blockchain: String,
|
||||
) : NotificationsAnalyticEvents(
|
||||
category = categoryName,
|
||||
event = "Notice - Not Enough Fee",
|
||||
params = mapOf(TOKEN_PARAM to token, BLOCKCHAIN to blockchain),
|
||||
)
|
||||
|
||||
data class NoticeFeeCoverage(
|
||||
override val categoryName: String,
|
||||
) : NotificationsAnalyticEvents(
|
||||
category = categoryName,
|
||||
event = "Notice - Network Fee Coverage",
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.send.v2.subcomponents.notifications.di
|
||||
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.DefaultNotificationsUpdateTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsUpdateTrigger
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@Module
|
||||
internal object NotificationsModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesNotificationsUpdateTrigger(): NotificationsUpdateTrigger {
|
||||
return DefaultNotificationsUpdateTrigger()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.features.send.v2.subcomponents.notifications.model
|
||||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class NotificationData(
|
||||
val destinationAddress: String,
|
||||
val amountValue: BigDecimal,
|
||||
val reduceAmountBy: BigDecimal,
|
||||
val isIgnoreReduce: Boolean,
|
||||
val fee: Fee?,
|
||||
val feeError: GetFeeError?,
|
||||
)
|
||||
|
|
@ -0,0 +1,366 @@
|
|||
package com.tangem.features.send.v2.subcomponents.notifications.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData
|
||||
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.addHighFeeWarningNotification
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addMinimumAmountErrorNotification
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addRentExemptionNotification
|
||||
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.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.tokens.GetBalanceNotEnoughForFeeWarningUseCase
|
||||
import com.tangem.domain.tokens.GetCurrencyCheckUseCase
|
||||
import com.tangem.domain.tokens.IsAmountSubtractAvailableUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck
|
||||
import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase
|
||||
import com.tangem.domain.utils.convertToSdkAmount
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeReloadTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkAndCalculateSubtractedAmount
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkFeeCoverage
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsComponent
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsUpdateTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.analytics.NotificationsAnalyticEvents
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ModelScoped
|
||||
class NotificationsModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val appRouter: AppRouter,
|
||||
private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase,
|
||||
private val getCurrencyCheckUseCase: GetCurrencyCheckUseCase,
|
||||
private val getBalanceNotEnoughForFeeWarningUseCase: GetBalanceNotEnoughForFeeWarningUseCase,
|
||||
private val validateTransactionUseCase: ValidateTransactionUseCase,
|
||||
private val sendFeeReloadTrigger: SendFeeReloadTrigger,
|
||||
private val sendAmountReduceTrigger: SendAmountReduceTrigger,
|
||||
private val notificationsUpdateTrigger: NotificationsUpdateTrigger,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
private val params: NotificationsComponent.Params = paramsContainer.require()
|
||||
|
||||
private val analyticsCategoryName = params.analyticsCategoryName
|
||||
private val userWalletId = params.userWalletId
|
||||
private val cryptoCurrencyStatus = params.cryptoCurrencyStatus
|
||||
private val feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus
|
||||
private val currency = cryptoCurrencyStatus.currency
|
||||
private val appCurrency = params.appCurrency
|
||||
|
||||
private var destinationAddress = params.destinationAddress
|
||||
private var amountValue = params.amountValue
|
||||
private var reduceAmountBy = params.reduceAmountBy
|
||||
private var isIgnoreReduce = params.isIgnoreReduce
|
||||
private var fee = params.fee
|
||||
private var feeError = params.feeError
|
||||
|
||||
private val _uiState = MutableStateFlow<ImmutableList<NotificationUM>>(persistentListOf())
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
private var isAmountSubtractAvailable = false
|
||||
|
||||
init {
|
||||
subscribeToNotificationUpdateTrigger()
|
||||
checkIfSubtractAvailable()
|
||||
}
|
||||
|
||||
private fun subscribeToNotificationUpdateTrigger() {
|
||||
notificationsUpdateTrigger.updateTriggerFlow
|
||||
.onEach { updateState(it) }
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun checkIfSubtractAvailable() {
|
||||
modelScope.launch {
|
||||
isAmountSubtractAvailable = isAmountSubtractAvailableUseCase(userWalletId, currency).getOrElse { false }
|
||||
buildNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateState(data: NotificationData) {
|
||||
destinationAddress = data.destinationAddress
|
||||
amountValue = data.amountValue
|
||||
reduceAmountBy = data.reduceAmountBy
|
||||
isIgnoreReduce = data.isIgnoreReduce
|
||||
fee = data.fee
|
||||
feeError = data.feeError
|
||||
|
||||
buildNotifications()
|
||||
}
|
||||
|
||||
private suspend fun buildNotifications() {
|
||||
val notifications = buildList {
|
||||
addFeeUnreachableNotification(
|
||||
tokenStatus = cryptoCurrencyStatus,
|
||||
coinStatus = feeCryptoCurrencyStatus,
|
||||
feeError = feeError,
|
||||
onReload = {
|
||||
modelScope.launch {
|
||||
sendFeeReloadTrigger.triggerUpdate()
|
||||
}
|
||||
},
|
||||
onClick = ::showTokenDetails,
|
||||
)
|
||||
addDomainNotifications(
|
||||
destinationAddress = destinationAddress,
|
||||
amountValue = amountValue,
|
||||
reduceAmountBy = reduceAmountBy,
|
||||
fee = fee,
|
||||
)
|
||||
}
|
||||
|
||||
notificationsUpdateTrigger.callbackHasError(notifications.any { it is NotificationUM.Error })
|
||||
|
||||
_uiState.value = notifications.toImmutableList()
|
||||
}
|
||||
|
||||
private fun showTokenDetails(currency: CryptoCurrency) {
|
||||
appRouter.pop { isSuccess ->
|
||||
if (isSuccess) {
|
||||
appRouter.push(
|
||||
AppRoute.CurrencyDetails(
|
||||
userWalletId = userWalletId,
|
||||
currency = currency,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun MutableList<NotificationUM>.addDomainNotifications(
|
||||
destinationAddress: String,
|
||||
amountValue: BigDecimal,
|
||||
reduceAmountBy: BigDecimal,
|
||||
fee: Fee?,
|
||||
) {
|
||||
val balance = cryptoCurrencyStatus.value.amount ?: return
|
||||
val feeValue = fee?.amount?.value ?: return
|
||||
val isFeeCoverage = checkFeeCoverage(
|
||||
isSubtractAvailable = isAmountSubtractAvailable,
|
||||
balance = balance,
|
||||
amountValue = amountValue,
|
||||
feeValue = feeValue,
|
||||
reduceAmountBy = reduceAmountBy,
|
||||
)
|
||||
val sendingAmount = checkAndCalculateSubtractedAmount(
|
||||
isAmountSubtractAvailable = isAmountSubtractAvailable,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
amountValue = amountValue,
|
||||
feeValue = feeValue,
|
||||
reduceAmountBy = reduceAmountBy,
|
||||
)
|
||||
val feeCurrencyBalanceAfterTransaction = getFeeCurrencyBalanceAfterTx(
|
||||
sendingAmount = sendingAmount,
|
||||
feeValue = feeValue,
|
||||
)
|
||||
val currencyCheck = getCurrencyCheckUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currencyStatus = cryptoCurrencyStatus,
|
||||
amount = sendingAmount,
|
||||
fee = feeValue,
|
||||
recipientAddress = destinationAddress,
|
||||
feeCurrencyBalanceAfterTransaction = feeCurrencyBalanceAfterTransaction,
|
||||
)
|
||||
|
||||
addErrorNotifications(
|
||||
sendingAmount = sendingAmount,
|
||||
feeValue = feeValue,
|
||||
currencyCheck = currencyCheck,
|
||||
)
|
||||
addWarningNotifications(
|
||||
enteredAmount = amountValue,
|
||||
fee = fee,
|
||||
feeValue = feeValue,
|
||||
sendingAmount = sendingAmount,
|
||||
isFeeCoverage = isFeeCoverage,
|
||||
currencyCheck = currencyCheck,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getFeeCurrencyBalanceAfterTx(sendingAmount: BigDecimal, feeValue: BigDecimal): BigDecimal? {
|
||||
val sendingCurrencyBalance = cryptoCurrencyStatus.value as? CryptoCurrencyStatus.Loaded
|
||||
val feeCurrencyBalance = feeCryptoCurrencyStatus.value as? CryptoCurrencyStatus.Loaded
|
||||
if (feeCryptoCurrencyStatus.value !is CryptoCurrencyStatus.Loaded) return null
|
||||
return when {
|
||||
feeCryptoCurrencyStatus == cryptoCurrencyStatus -> sendingCurrencyBalance?.let {
|
||||
it.amount - sendingAmount - feeValue
|
||||
}
|
||||
else -> feeCurrencyBalance?.let { it.amount - feeValue }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun MutableList<NotificationUM>.addErrorNotifications(
|
||||
sendingAmount: BigDecimal,
|
||||
feeValue: BigDecimal,
|
||||
currencyCheck: CryptoCurrencyCheck,
|
||||
) {
|
||||
val currencyWarning = getBalanceNotEnoughForFeeWarningUseCase(
|
||||
fee = feeValue,
|
||||
userWalletId = userWalletId,
|
||||
tokenStatus = cryptoCurrencyStatus,
|
||||
coinStatus = feeCryptoCurrencyStatus,
|
||||
).getOrNull()
|
||||
|
||||
addExceedBalanceNotification(
|
||||
feeAmount = feeValue,
|
||||
sendingAmount = sendingAmount,
|
||||
isSubtractionAvailable = isAmountSubtractAvailable,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
)
|
||||
addExceedsBalanceNotification(
|
||||
cryptoCurrencyWarning = currencyWarning,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
shouldMergeFeeNetworkName = BlockchainUtils.isArbitrum(currency.network.backendId),
|
||||
onClick = ::showTokenDetails,
|
||||
onAnalyticsEvent = {
|
||||
analyticsEventHandler.send(
|
||||
NotificationsAnalyticEvents.NoticeNotEnoughFee(
|
||||
categoryName = analyticsCategoryName,
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
blockchain = cryptoCurrencyStatus.currency.network.name,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
if (!BlockchainUtils.isCardano(currency.network.id.value)) {
|
||||
addDustWarningNotification(
|
||||
dustValue = currencyCheck.dustValue,
|
||||
feeValue = feeValue,
|
||||
sendingAmount = sendingAmount,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
)
|
||||
}
|
||||
addTransactionLimitErrorNotification(
|
||||
currencyCheck = currencyCheck,
|
||||
sendingAmount = sendingAmount,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
feeValue = feeValue,
|
||||
onReduceClick = { reduceTo, _ ->
|
||||
modelScope.launch {
|
||||
sendAmountReduceTrigger.triggerReduceTo(reduceTo)
|
||||
}
|
||||
},
|
||||
)
|
||||
addReserveAmountErrorNotification(
|
||||
reserveAmount = currencyCheck.reserveAmount,
|
||||
sendingAmount = sendingAmount,
|
||||
cryptoCurrency = currency,
|
||||
isAccountFunded = currencyCheck.isAccountFunded,
|
||||
)
|
||||
addMinimumAmountErrorNotification(
|
||||
minimumSendAmount = currencyCheck.minimumSendAmount,
|
||||
sendingAmount = sendingAmount,
|
||||
cryptoCurrency = currency,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun MutableList<NotificationUM>.addWarningNotifications(
|
||||
enteredAmount: BigDecimal,
|
||||
sendingAmount: BigDecimal,
|
||||
fee: Fee?,
|
||||
feeValue: BigDecimal,
|
||||
isFeeCoverage: Boolean,
|
||||
currencyCheck: CryptoCurrencyCheck,
|
||||
) {
|
||||
val validationError = validateTransactionUseCase(
|
||||
userWalletId = userWalletId,
|
||||
amount = enteredAmount.convertToSdkAmount(currency),
|
||||
fee = fee,
|
||||
memo = null,
|
||||
destination = "",
|
||||
network = currency.network,
|
||||
).leftOrNull()
|
||||
|
||||
addRentExemptionNotification(
|
||||
rentWarning = currencyCheck.rentWarning,
|
||||
)
|
||||
|
||||
addExistentialWarningNotification(
|
||||
existentialDeposit = currencyCheck.existentialDeposit,
|
||||
feeAmount = feeValue,
|
||||
sendingAmount = sendingAmount,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
onReduceClick = { reduceBy, reduceByDiff, _ ->
|
||||
modelScope.launch {
|
||||
sendAmountReduceTrigger.triggerReduceBy(
|
||||
ReduceByData(
|
||||
reduceAmountBy = reduceBy,
|
||||
reduceAmountByDiff = reduceByDiff,
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
addFeeCoverageNotification(
|
||||
isFeeCoverage = isFeeCoverage,
|
||||
enteredAmountValue = enteredAmount,
|
||||
sendingValue = sendingAmount,
|
||||
appCurrency = appCurrency,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
)
|
||||
addValidateTransactionNotifications(
|
||||
dustValue = currencyCheck.dustValue.orZero(),
|
||||
minAdaValue = (fee as? Fee.CardanoToken)?.minAdaValue,
|
||||
validationError = validationError,
|
||||
cryptoCurrency = currency,
|
||||
onReduceClick = { reduceTo, _ ->
|
||||
modelScope.launch {
|
||||
sendAmountReduceTrigger.triggerReduceTo(reduceTo)
|
||||
}
|
||||
},
|
||||
)
|
||||
addHighFeeWarningNotification(
|
||||
enteredAmountValue = enteredAmount,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
ignoreAmountReduce = isIgnoreReduce,
|
||||
onReduceClick = { reduceBy, reduceByDiff, _ ->
|
||||
modelScope.launch {
|
||||
sendAmountReduceTrigger.triggerReduceBy(
|
||||
ReduceByData(
|
||||
reduceAmountBy = reduceBy,
|
||||
reduceAmountByDiff = reduceByDiff,
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
onCloseClick = {
|
||||
modelScope.launch {
|
||||
buildNotifications()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tangem.features.send.v2.subcomponents.notifications.ui
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal fun LazyListScope.notifications(
|
||||
notifications: ImmutableList<NotificationUM>,
|
||||
modifier: Modifier = Modifier,
|
||||
hasPaddingAbove: Boolean = false,
|
||||
isClickDisabled: Boolean = false,
|
||||
) {
|
||||
itemsIndexed(
|
||||
items = notifications,
|
||||
key = { _, item -> item::class.java },
|
||||
contentType = { _, item -> item::class.java },
|
||||
itemContent = { i, item ->
|
||||
val topPadding = if (i == 0 && hasPaddingAbove) 0.dp else 12.dp
|
||||
Notification(
|
||||
config = item.config,
|
||||
modifier = modifier
|
||||
.padding(top = topPadding)
|
||||
.animateItem(),
|
||||
containerColor = when (item) {
|
||||
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 NotificationUM.Error.TokenExceedsBalance,
|
||||
is NotificationUM.Warning,
|
||||
-> null
|
||||
is NotificationUM.Error -> TangemTheme.colors.icon.warning
|
||||
is NotificationUM.Info -> TangemTheme.colors.icon.accent
|
||||
},
|
||||
isEnabled = !isClickDisabled,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
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.blockchainsdk.utils.minimalAmount
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.common.ui.notifications.NotificationsFactory.addDustWarningNotification
|
||||
|
|
@ -29,13 +27,14 @@ import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase
|
|||
import com.tangem.domain.utils.convertToSdkAmount
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents
|
||||
import com.tangem.features.send.impl.presentation.model.SendClickIntents
|
||||
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.model.SendClickIntents
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.lib.crypto.BlockchainUtils.getTezosThreshold
|
||||
import com.tangem.lib.crypto.BlockchainUtils.isTezos
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.extensions.orZero
|
||||
|
|
@ -297,7 +296,7 @@ internal class SendNotificationFactory(
|
|||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val balance = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
|
||||
val isTezos = isTezos(cryptoCurrencyStatus.currency.network.id.value)
|
||||
val threshold = Blockchain.Tezos.minimalAmount()
|
||||
val threshold = getTezosThreshold()
|
||||
val isTotalBalance = sendAmount >= balance && balance > threshold
|
||||
if (!ignoreAmountReduce && isTotalBalance && isTezos) {
|
||||
add(
|
||||
|
|
|
|||
|
|
@ -463,10 +463,6 @@ fun Blockchain.amountToCreateAccount(walletManager: WalletManager, token: Token?
|
|||
}
|
||||
}
|
||||
|
||||
fun Blockchain.minimalAmount(): BigDecimal {
|
||||
return BigDecimal.ONE.movePointLeft(decimals())
|
||||
}
|
||||
|
||||
const val OLD_POLYGON_NAME = "matic-network"
|
||||
const val NEW_POLYGON_NAME = "polygon-ecosystem-token"
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchainsdk.compatibility.l2BlockchainsList
|
||||
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.blockchainsdk.utils.minimalAmount
|
||||
import com.tangem.lib.crypto.converter.XrpTaggedAddressConverter
|
||||
import com.tangem.lib.crypto.models.XrpTaggedAddress
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -125,7 +124,7 @@ object BlockchainUtils {
|
|||
return l2BlockchainsList.contains(blockchain)
|
||||
}
|
||||
|
||||
fun getTezosThreshold(): BigDecimal = Blockchain.Tezos.minimalAmount()
|
||||
fun getTezosThreshold(): BigDecimal = BigDecimal.ONE.movePointLeft(Blockchain.Tezos.decimals())
|
||||
|
||||
/**
|
||||
* Blockchains not affecting total balance counting on errors
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue