Updated on 2026-08-14

This commit is contained in:
Tangem 2024-11-05 00:41:17 +05:00
parent 404bcc011a
commit 7ed060a37d
31 changed files with 351 additions and 66 deletions

View file

@ -1,6 +1,7 @@
package com.tangem.features.send.impl.presentation.state.amount
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.features.send.impl.presentation.state.StateRouter
@ -16,6 +17,7 @@ internal class AmountStateFactory(
private val stateRouterProvider: Provider<StateRouter>,
private val currentStateProvider: Provider<SendUiState>,
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
private val minimumTransactionAmountProvider: Provider<EnterAmountBoundary?>,
) {
private val amountFieldChangeConverter by lazy(LazyThreadSafetyMode.NONE) {
@ -23,6 +25,7 @@ internal class AmountStateFactory(
stateRouterProvider = stateRouterProvider,
currentStateProvider = currentStateProvider,
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
minimumTransactionAmountProvider = minimumTransactionAmountProvider,
)
}
private val amountFieldMaxAmountConverter by lazy(LazyThreadSafetyMode.NONE) {
@ -30,6 +33,7 @@ internal class AmountStateFactory(
stateRouterProvider = stateRouterProvider,
currentStateProvider = currentStateProvider,
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
minimumTransactionAmountProvider = minimumTransactionAmountProvider,
)
}
@ -51,6 +55,7 @@ internal class AmountStateFactory(
stateRouterProvider = stateRouterProvider,
currentStateProvider = currentStateProvider,
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
minimumTransactionAmountProvider = minimumTransactionAmountProvider,
)
}
private val amountReduceToConverter by lazy {
@ -58,6 +63,7 @@ internal class AmountStateFactory(
stateRouterProvider = stateRouterProvider,
currentStateProvider = currentStateProvider,
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
minimumTransactionAmountProvider = minimumTransactionAmountProvider,
)
}

View file

@ -1,6 +1,7 @@
package com.tangem.features.send.impl.presentation.state.amount
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.features.send.impl.presentation.state.StateRouter
@ -11,6 +12,7 @@ internal class SendAmountReduceByConverter(
private val stateRouterProvider: Provider<StateRouter>,
private val currentStateProvider: Provider<SendUiState>,
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
private val minimumTransactionAmountProvider: Provider<EnterAmountBoundary?>,
) : Converter<AmountReduceByTransformer.ReduceByData, SendUiState> {
override fun convert(value: AmountReduceByTransformer.ReduceByData): SendUiState {
@ -23,7 +25,11 @@ internal class SendAmountReduceByConverter(
sendState = state.sendState?.copy(
reduceAmountBy = value.reduceAmountBy,
),
amountState = AmountReduceByTransformer(cryptoCurrencyStatusProvider(), value).transform(amountState),
amountState = AmountReduceByTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatusProvider(),
minimumTransactionAmount = minimumTransactionAmountProvider(),
value = value,
).transform(amountState),
)
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.features.send.impl.presentation.state.amount
import com.tangem.common.ui.amountScreen.converters.AmountReduceToTransformer
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.features.send.impl.presentation.state.StateRouter
@ -12,6 +13,7 @@ internal class SendAmountReduceToConverter(
private val stateRouterProvider: Provider<StateRouter>,
private val currentStateProvider: Provider<SendUiState>,
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
private val minimumTransactionAmountProvider: Provider<EnterAmountBoundary?>,
) : Converter<BigDecimal, SendUiState> {
override fun convert(value: BigDecimal): SendUiState {
@ -21,7 +23,11 @@ internal class SendAmountReduceToConverter(
return state.copyWrapped(
isEditState = isEditState,
amountState = AmountReduceToTransformer(cryptoCurrencyStatusProvider(), value).transform(amountState),
amountState = AmountReduceToTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatusProvider(),
minimumTransactionAmount = minimumTransactionAmountProvider(),
value = value,
).transform(amountState),
)
}
}

View file

@ -11,6 +11,7 @@ import com.tangem.common.ui.notifications.NotificationsFactory.addExceedsBalance
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.addMinimumAmountErrorNotification
import com.tangem.common.ui.notifications.NotificationsFactory.addReserveAmountErrorNotification
import com.tangem.common.ui.notifications.NotificationsFactory.addTransactionLimitErrorNotification
import com.tangem.common.ui.notifications.NotificationsFactory.addValidateTransactionNotifications
@ -192,6 +193,11 @@ internal class SendNotificationFactory(
cryptoCurrency = currency,
isAccountFunded = currencyCheck.isAccountFunded,
)
addMinimumAmountErrorNotification(
minimumSendAmount = currencyCheck.minimumSendAmount,
sendingAmount = sendingAmount,
cryptoCurrency = currency,
)
}
private suspend fun MutableList<NotificationUM>.addWarningNotifications(

View file

@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.fields
import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.features.send.impl.presentation.state.StateRouter
@ -12,21 +13,29 @@ internal class SendAmountFieldChangeConverter(
private val stateRouterProvider: Provider<StateRouter>,
private val currentStateProvider: Provider<SendUiState>,
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
private val minimumTransactionAmountProvider: Provider<EnterAmountBoundary?>,
) : Converter<String, SendUiState> {
private val maxEnterAmountConverter = MaxEnterAmountConverter()
override fun convert(value: String): SendUiState {
val state = currentStateProvider()
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
val isEditState = stateRouterProvider().isEditState
val amountState = state.getAmountState(isEditState)
val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatusProvider())
val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatus)
val minimumTransactionAmount = minimumTransactionAmountProvider()
return state.copyWrapped(
isEditState = isEditState,
sendState = state.sendState?.copy(reduceAmountBy = null),
amountState = AmountFieldChangeTransformer(maxEnterAmount, value).transform(amountState),
amountState = AmountFieldChangeTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
maxEnterAmount = maxEnterAmount,
minimumTransactionAmount = minimumTransactionAmount,
value = value,
).transform(amountState),
)
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.features.send.impl.presentation.state.fields
import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldSetMaxAmountTransformer
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.features.send.impl.presentation.state.StateRouter
@ -13,6 +14,7 @@ internal class SendAmountFieldMaxAmountConverter(
private val stateRouterProvider: Provider<StateRouter>,
private val currentStateProvider: Provider<SendUiState>,
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
private val minimumTransactionAmountProvider: Provider<EnterAmountBoundary?>,
) : Converter<Unit, SendUiState> {
private val maxEnterAmountConverter = MaxEnterAmountConverter()
@ -27,11 +29,16 @@ internal class SendAmountFieldMaxAmountConverter(
if (decimalCryptoValue.isNullOrZero()) return state
val maxEnterAmount = maxEnterAmountConverter.convert(cryptoCurrencyStatus)
val minimumTransactionAmount = minimumTransactionAmountProvider()
return state.copyWrapped(
isEditState = isEditState,
sendState = state.sendState?.copy(reduceAmountBy = null),
amountState = AmountFieldSetMaxAmountTransformer(maxEnterAmount).transform(amountState),
amountState = AmountFieldSetMaxAmountTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
maxAmount = maxEnterAmount,
minAmount = minimumTransactionAmount,
).transform(amountState),
)
}
}

View file

@ -12,6 +12,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.amountScreen.models.EnterAmountBoundary
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.utils.parseBigDecimal
@ -59,6 +60,7 @@ import com.tangem.features.send.impl.presentation.state.recipient.RecipientSendF
import com.tangem.lib.crypto.BlockchainUtils
import com.tangem.utils.Provider
import com.tangem.utils.coroutines.*
import com.tangem.utils.extensions.orZero
import com.tangem.utils.extensions.stripZeroPlainString
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
@ -78,6 +80,7 @@ internal class SendViewModel @Inject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getWalletsUseCase: GetWalletsUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
private val getNetworkAddressesUseCase: GetNetworkAddressesUseCase,
private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
@ -153,6 +156,7 @@ internal class SendViewModel @Inject constructor(
stateRouterProvider = Provider { stateRouter },
currentStateProvider = Provider { uiState.value },
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
minimumTransactionAmountProvider = Provider { minimumTransactionAmount },
)
private val feeStateFactory = FeeStateFactory(
@ -212,6 +216,7 @@ internal class SendViewModel @Inject constructor(
private var isTapHelpPreviewEnabled: Boolean = false
private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null
private var minimumTransactionAmount: EnterAmountBoundary? = null
private var balanceJobHolder = JobHolder()
private var balanceHidingJobHolder = JobHolder()
@ -295,6 +300,7 @@ internal class SendViewModel @Inject constructor(
onDataLoaded(
currencyStatus = cryptoCurrencyStatus,
feeCurrencyStatus = getFeeCurrencyStatusSync(cryptoCurrencyStatus, isMultiCurrency),
minTransactionAmount = getMinimumTransactionAmount(cryptoCurrencyStatus),
)
},
ifLeft = { showErrorAlert() },
@ -336,6 +342,18 @@ internal class SendViewModel @Inject constructor(
}
}
private suspend fun getMinimumTransactionAmount(cryptoCurrencyStatus: CryptoCurrencyStatus): EnterAmountBoundary? {
return getMinimumTransactionAmountSyncUseCase(
userWalletId = userWalletId,
cryptoCurrencyStatus = cryptoCurrencyStatus,
).getOrNull()?.let {
EnterAmountBoundary(
amount = it,
fiatRate = cryptoCurrencyStatus.value.fiatRate.orZero(),
)
}
}
private fun createSelectedAppCurrencyFlow(): StateFlow<AppCurrency> {
return getSelectedAppCurrencyUseCase()
.map { maybeAppCurrency ->
@ -348,9 +366,14 @@ internal class SendViewModel @Inject constructor(
)
}
private fun onDataLoaded(currencyStatus: CryptoCurrencyStatus, feeCurrencyStatus: CryptoCurrencyStatus?) {
private fun onDataLoaded(
currencyStatus: CryptoCurrencyStatus,
feeCurrencyStatus: CryptoCurrencyStatus?,
minTransactionAmount: EnterAmountBoundary?,
) {
cryptoCurrencyStatus = currencyStatus
feeCryptoCurrencyStatus = feeCurrencyStatus
minimumTransactionAmount = minTransactionAmount
subscribeOnQRScannerResult()
when {
uiState.value.sendState?.isSuccess == true -> return