From a8dd36e98cecd0c4636a6019036e221c9db92e45 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 19 Sep 2020 21:34:59 +0300 Subject: [PATCH 01/10] Updated on 2026-08-14 --- .../tap/features/send/redux/middlewares/RequestFeeMiddleware.kt | 2 +- .../tangem/tap/features/send/redux/states/AddressPayIdState.kt | 2 +- .../java/com/tangem/tap/features/send/redux/states/FeeState.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt index f1d202cf46..c78c05f21f 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt @@ -60,7 +60,7 @@ class RequestFeeMiddleware { dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = false)) } } else { - dispatch(FeeAction.ChangeLayoutVisibility(main = true, controls = true, chipGroup = true)) + dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = true)) } } is Result.Failure -> { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt index e7b081c183..e7e10b5acd 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt @@ -40,7 +40,7 @@ data class AddressPayIdState( } fun copyPayIdWalletAddress(payId: String, address: String): AddressPayIdState { - val truncated = truncateHandler?.invoke(address) ?: address + val truncated = truncateHandler?.invoke(payId) ?: address return this.copy( etFieldValue = payId, normalFieldValue = payId, diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt index 2c95438ca3..37260d98be 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt @@ -17,7 +17,7 @@ data class FeeState( val currentFee: Value? = null, val feeIsIncluded: Boolean = false, val mainLayoutIsVisible: Boolean = false, - val controlsLayoutIsVisible: Boolean = true, + val controlsLayoutIsVisible: Boolean = false, val feeChipGroupIsVisible: Boolean = true, val error: FeeAction.Error? = null, override val stateId: StateId = StateId.FEE From 9e399946d7a03ab910442c5b1aa8925334a81315 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 19 Sep 2020 21:58:22 +0300 Subject: [PATCH 02/10] Updated on 2026-08-14 --- app/build.gradle | 2 +- .../tangem/tap/common/CurrencyConverter.kt | 27 +++-- .../tangem/tap/common/extensions/Specific.kt | 25 ++++- .../common/redux/NotificationsMiddleware.kt | 29 +++-- .../java/com/tangem/tap/domain/TapErrors.kt | 44 ++++++-- .../features/send/redux/SendScreenAction.kt | 7 +- .../redux/middlewares/AmountMiddleware.kt | 100 ++++++------------ .../send/redux/reducers/AmountReducer.kt | 28 ++--- .../send/redux/reducers/ReceiptReducer.kt | 76 +++++++------ .../send/redux/reducers/SendScreenReducer.kt | 31 ++++-- .../send/redux/states/AddressPayIdState.kt | 5 +- .../features/send/redux/states/FeeState.kt | 5 +- .../send/redux/states/ReceiptState.kt | 13 +-- .../features/send/redux/states/SendState.kt | 51 +++++++-- .../tap/features/send/ui/SendFragment.kt | 8 ++ .../stateSubscribers/SendStateSubscriber.kt | 41 +++---- app/src/main/res/values/strings.xml | 10 +- 17 files changed, 304 insertions(+), 198 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7a6c354f62..56fa51dc9b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -64,7 +64,7 @@ dependencies { implementation 'com.google.android.material:material:1.2.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' - implementation 'com.tangem:blockchain:1.31.0' + implementation 'com.tangem:blockchain:1.35.0' //lifecycle implementation "androidx.lifecycle:lifecycle-runtime:2.2.0" diff --git a/app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt b/app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt index 07ac39e6dc..0ca9b929bd 100644 --- a/app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt +++ b/app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt @@ -1,23 +1,36 @@ package com.tangem.tap.common import com.tangem.common.extensions.isZero +import com.tangem.tap.common.extensions.scaleToFiat import java.math.BigDecimal import java.math.RoundingMode /** [REDACTED_AUTHOR] */ -class CurrencyConverter(var rateValue: BigDecimal) { +class CurrencyConverter( + private val rateValue: BigDecimal, + private val decimals: Int +) { + private val roundingMode = RoundingMode.DOWN - fun toFiat(crypto: BigDecimal, decimals: Int = 2): BigDecimal { - return rateValue.multiply(crypto).setScale(decimals, RoundingMode.DOWN) + fun toFiat(crypto: BigDecimal, fiatDecimals: Int = 2): BigDecimal { + return toFiatUnscaled(crypto).setScale(fiatDecimals, roundingMode) } - fun toCrypto(fiat: BigDecimal, decimals: Int): BigDecimal { - if (fiat.isZero() || rateValue.isZero()) return fiat + fun toFiatUnscaled(crypto: BigDecimal): BigDecimal { + return rateValue.multiply(crypto).setScale(decimals, roundingMode) + } - val scaledRateValue = rateValue.setScale(decimals, RoundingMode.DOWN) - val scaledFiat = fiat.setScale(decimals, RoundingMode.DOWN) + fun toFiatWithPrecision(crypto: BigDecimal): BigDecimal { + return toFiatUnscaled(crypto).scaleToFiat(true) + } + + fun toCrypto(fiat: BigDecimal): BigDecimal { + if (fiat.isZero()) return fiat + + val scaledRateValue = rateValue.setScale(decimals, roundingMode) + val scaledFiat = fiat.setScale(decimals, roundingMode) return scaledFiat.divide(scaledRateValue, RoundingMode.UP) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt index 46072d5765..925969d32f 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt @@ -6,6 +6,7 @@ import com.google.zxing.BarcodeFormat import com.google.zxing.EncodeHintType import com.google.zxing.qrcode.QRCodeWriter import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel +import com.tangem.common.extensions.isZero import com.tangem.tap.common.redux.global.FiatCurrencyName import com.tangem.tap.network.coinmarketcap.FiatCurrency import java.math.BigDecimal @@ -58,8 +59,28 @@ fun BigDecimal.toFiatString(rateValue: BigDecimal, fiatCurrencyName: FiatCurrenc return "≈ ${fiatCurrencyName}  $fiatValue" } +fun FiatCurrency.toFormattedString(): String = "${this.name} (${this.symbol}) - ${this.sign}" + fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString() +// 0.00 -> 0.00 +// 0.00002345 -> 0.00002 +// 1.00002345 -> 1.00 +// 1.45002345 -> 1.45 +fun BigDecimal.scaleToFiat(applyPrecision: Boolean = false): BigDecimal { + if (this.isZero()) return this + + val scaledFiat = this.setScale(2, RoundingMode.DOWN) + return if (scaledFiat.isZero() && applyPrecision) this.setPrecision(1) + else scaledFiat + +} + +fun BigDecimal.setPrecision(precision: Int, roundingMode: RoundingMode = RoundingMode.DOWN): BigDecimal { + if (precision == precision() || scale() <= precision) return this + return this.setScale(scale() - precision() + precision, roundingMode) +} + fun BigDecimal.isPositive(): Boolean = this.compareTo(BigDecimal.ZERO) == 1 fun BigDecimal.isNegative(): Boolean = this.compareTo(BigDecimal.ZERO) == -1 fun BigDecimal.isGreaterThan(value: BigDecimal): Boolean = this.compareTo(value) == 1 @@ -73,6 +94,4 @@ fun BigDecimal.isGreaterThanOrEqual(value: BigDecimal): Boolean { fun BigDecimal.isLessThanOrEqual(value: BigDecimal): Boolean { val compareResult = this.compareTo(value) return compareResult == -1 || compareResult == 0 -} - -fun FiatCurrency.toFormattedString(): String = "${this.name} (${this.symbol}) - ${this.sign}" \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt index 177089ffba..7e84963643 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt @@ -3,7 +3,9 @@ package com.tangem.tap.common.redux import android.widget.Toast import androidx.coordinatorlayout.widget.CoordinatorLayout import com.google.android.material.snackbar.Snackbar +import com.tangem.tap.domain.MultiMessageError import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.assembleErrorIds import com.tangem.tap.notificationsHandler import org.rekotlin.Action import org.rekotlin.Middleware @@ -39,19 +41,30 @@ class NotificationsHandler(coordinatorLayout: CoordinatorLayout) { Toast.makeText(it.context, it.context.getString(message), Toast.LENGTH_LONG).show() } } + + fun showNotification(errorList: List, builder: (List) -> String) { + val context = baseLayout.get()?.context ?: return + + val message = builder(errorList.map { context.getString(it) }) + showNotification(message) + } } val notificationsMiddleware: Middleware = { dispatch, state -> { next -> { action -> - if (action is NotificationAction) { - notificationsHandler?.showNotification(action.messageResource) - } - if (action is ToastNotificationAction) { - notificationsHandler?.showToastNotification(action.messageResource) - } - if (action is ErrorAction) { - notificationsHandler?.showNotification(action.error.localizedMessage) + when (action) { + is NotificationAction -> notificationsHandler?.showNotification(action.messageResource) + is ToastNotificationAction -> notificationsHandler?.showToastNotification(action.messageResource) + is ErrorAction -> { + when (action.error) { + is MultiMessageError -> { + val multiError = action.error as MultiMessageError + notificationsHandler?.showNotification(multiError.assembleErrorIds(), multiError.builder) + } + else -> notificationsHandler?.showNotification(action.error.localizedMessage) + } + } } next(action) } diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index 4f98257223..4bd45c181d 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -3,13 +3,39 @@ package com.tangem.tap.domain import androidx.annotation.StringRes import com.tangem.wallet.R -sealed class TapError(@StringRes val localizedMessage: Int): Throwable() { - object PayIdAlreadyCreated: TapError(R.string.error_payid_already_created) - object PayIdCreatingError: TapError(R.string.error_creating_payid) - object PayIdEmptyField: TapError(R.string.wallet_create_payid_empty) - object UnknownBlockchain: TapError(R.string.wallet_unknown_blockchain) - object NoInternetConnection: TapError(R.string.notification_no_internet) - object InsufficientBalance: TapError(R.string.error_insufficient_balance) - object BlockchainInternalError: TapError(R.string.error_blockchain_internal) - object UnknownError: TapError(R.string.error_unknown) +interface TapErrors +interface MultiMessageError : TapErrors { + val errorList: List + val builder: (List) -> String +} + +sealed class TapError(@StringRes val localizedMessage: Int) : Throwable(), TapErrors { + object UnknownError : TapError(R.string.error_unknown) + object PayIdAlreadyCreated : TapError(R.string.error_payid_already_created) + object PayIdCreatingError : TapError(R.string.error_creating_payid) + object PayIdEmptyField : TapError(R.string.wallet_create_payid_empty) + object UnknownBlockchain : TapError(R.string.wallet_unknown_blockchain) + object NoInternetConnection : TapError(R.string.notification_no_internet) + object InsufficientBalance : TapError(R.string.error_insufficient_balance) + object BlockchainInternalError : TapError(R.string.error_blockchain_internal) + object AmountExceedsBalance : TapError(R.string.amount_exceeds_balance) + object FeeExceedsBalance : TapError(R.string.fee_exceeds_balance) + object TotalExceedsBalance : TapError(R.string.total_exceeds_balance) + object InvalidAmountValue : TapError(R.string.invalid_amount_value) + object InvalidFeeValue : TapError(R.string.invalid_fee_value) + object DustAmount : TapError(R.string.dust_amount) + object DustChang : TapError(R.string.dust_change) + data class ValidateTransactionErrors( + override val errorList: List, + override val builder: (List) -> String + ) : TapError(-1), MultiMessageError +} + +fun TapErrors.assembleErrorIds(): MutableList { + val idList = mutableListOf() + when (this) { + is MultiMessageError -> this.errorList.forEach { idList.addAll(it.assembleErrorIds()) } + is TapError -> idList.add(this.localizedMessage) + } + return idList } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 2dba8c385f..5596123814 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -64,14 +64,9 @@ sealed class AmountActionUi : SendScreenActionUi { } sealed class AmountAction : SendScreenAction { - enum class Error { - FEE_GREATER_THAN_AMOUNT, - AMOUNT_WITH_FEE_GREATER_THAN_BALANCE - } - sealed class AmountVerification : AmountAction() { data class SetAmount(val amount: BigDecimal) : AmountVerification() - data class SetError(val amount: BigDecimal, val error: Error) : AmountVerification() + data class SetError(val amount: BigDecimal, val error: TapError) : AmountVerification() } } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt index 259051b8a4..c68901c350 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt @@ -1,13 +1,13 @@ package com.tangem.tap.features.send.redux.middlewares -import com.tangem.tap.common.CurrencyConverter -import com.tangem.tap.common.extensions.isGreaterThan -import com.tangem.tap.common.extensions.isGreaterThanOrEqual +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.TransactionError +import com.tangem.common.extensions.isZero import com.tangem.tap.common.redux.AppState +import com.tangem.tap.domain.TapError import com.tangem.tap.features.send.redux.AmountAction import com.tangem.tap.features.send.redux.ReceiptAction import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.features.send.redux.states.MainCurrencyType import com.tangem.tap.store import org.rekotlin.Action import java.math.BigDecimal @@ -19,81 +19,47 @@ class AmountMiddleware { fun handle(rawData: String?, appState: AppState?, dispatch: (Action) -> Unit) { val sendState = appState?.sendState ?: return + val walletManager = sendState.walletManager ?: return val rawData = rawData ?: store.state.sendState.amountState.viewAmountValue val data = if (rawData == ".") "0.0" else rawData - val proposedAmountValue = when { + val inputValue = when { data.isEmpty() || data == "0" -> BigDecimal.ZERO else -> BigDecimal(data) } - val balanceCrypto = sendState.amount?.value ?: BigDecimal.ZERO - val amountChecker = when (sendState.amountState.mainCurrency.value) { - MainCurrencyType.FIAT -> FiatAmountChecker(balanceCrypto, sendState.currencyConverter) - MainCurrencyType.CRYPTO -> CryptoAmountChecker(balanceCrypto) - } + if (inputValue.isZero() && sendState.amountState.amountToSendCrypto.isZero()) return - val checkResult = amountChecker.check( - proposedAmountValue, - sendState.feeState.getCurrentFee(), - sendState.feeState.feeIsIncluded - ) - if (checkResult.error == null) { - dispatch(AmountAction.AmountVerification.SetAmount(checkResult.amount)) + val inputCrypto = sendState.convertInputValueToCrypto(inputValue) + val fee = sendState.feeState.getCurrentFee() + + val needToExtractFee = sendState.amountState.isCoinAmount() && sendState.feeState.feeIsIncluded + val totalToSend = if (needToExtractFee) inputCrypto.minus(fee) else inputCrypto + + val feeAmount = Amount(fee, walletManager.wallet.blockchain) + val totalAmount = Amount(totalToSend, walletManager.wallet.blockchain) + + val transactionErrors = walletManager.validateTransaction(totalAmount, feeAmount) + if (transactionErrors.isEmpty()) { + dispatch(AmountAction.AmountVerification.SetAmount(inputValue)) } else { - dispatch(AmountAction.AmountVerification.SetError(checkResult.amount, checkResult.error)) + val tapErrors = transactionErrors.map { + when (it) { + TransactionError.AmountExceedsBalance -> TapError.AmountExceedsBalance + TransactionError.FeeExceedsBalance -> TapError.FeeExceedsBalance + TransactionError.TotalExceedsBalance -> TapError.TotalExceedsBalance + TransactionError.InvalidAmountValue -> TapError.InvalidAmountValue + TransactionError.InvalidFeeValue -> TapError.InvalidFeeValue + TransactionError.DustAmount -> TapError.DustAmount + TransactionError.DustChange -> TapError.DustChang + else -> TapError.UnknownError + } + } + val error = TapError.ValidateTransactionErrors(tapErrors) { it.joinToString("\r\n") } + dispatch(AmountAction.AmountVerification.SetError(inputValue, error)) } dispatch(ReceiptAction.RefreshReceipt) dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) } -} - -interface AmountChecker { - data class Result(val amount: BigDecimal, val error: AmountAction.Error? = null) - - fun check(value: BigDecimal, feeCrypto: BigDecimal, feeIsIncluded: Boolean): Result -} - -abstract class BaseAmountChecker( - protected val balanceCrypto: BigDecimal -) : AmountChecker { - - override fun check(value: BigDecimal, feeCrypto: BigDecimal, feeIsIncluded: Boolean): AmountChecker.Result { - val convertedFee = convert(feeCrypto) - val convertedBalance = convert(balanceCrypto) - if (feeIsIncluded) { - return if (value.isGreaterThan(convertedFee)) { - if (convertedBalance.isGreaterThanOrEqual(value)) { - AmountChecker.Result(value) - } else { - AmountChecker.Result(value, AmountAction.Error.FEE_GREATER_THAN_AMOUNT) - } - } else { - AmountChecker.Result(value, AmountAction.Error.AMOUNT_WITH_FEE_GREATER_THAN_BALANCE) - } - } else { - val amountWithFee = value.plus(convertedFee) - return if (convertedBalance.isGreaterThanOrEqual(amountWithFee)) { - AmountChecker.Result(value) - } else { - AmountChecker.Result(value, AmountAction.Error.AMOUNT_WITH_FEE_GREATER_THAN_BALANCE) - } - } - } - - protected abstract fun convert(value: BigDecimal): BigDecimal -} - -class CryptoAmountChecker( - balanceCrypto: BigDecimal -) : BaseAmountChecker(balanceCrypto) { - override fun convert(value: BigDecimal): BigDecimal = value -} - -class FiatAmountChecker( - balanceCrypto: BigDecimal, - private val converter: CurrencyConverter -) : BaseAmountChecker(balanceCrypto) { - override fun convert(value: BigDecimal): BigDecimal = converter.toFiat(value) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt index 5129a95646..a9416145d3 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.common.extensions.isZero -import com.tangem.tap.common.CurrencyConverter import com.tangem.tap.common.extensions.isNegative import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.features.send.redux.AmountAction @@ -11,8 +10,6 @@ import com.tangem.tap.features.send.redux.SendScreenAction import com.tangem.tap.features.send.redux.states.AmountState import com.tangem.tap.features.send.redux.states.MainCurrencyType import com.tangem.tap.features.send.redux.states.SendState -import com.tangem.tap.features.send.redux.states.Value -import com.tangem.tap.store import java.math.BigDecimal /** @@ -26,7 +23,6 @@ class AmountReducer : SendInternalReducer { } private fun handleUiAction(action: AmountActionUi, sendState: SendState, state: AmountState): SendState { - val converter = sendState.currencyConverter val result = when (action) { is ToggleMainCurrency -> { val type = if (state.mainCurrency.value == MainCurrencyType.FIAT) MainCurrencyType.CRYPTO @@ -38,21 +34,21 @@ class AmountReducer : SendInternalReducer { when (action.mainCurrency) { MainCurrencyType.FIAT -> { val fiatToSend = if (state.amountToSendCrypto.isZero()) BigDecimal.ZERO - else converter.toFiat(state.amountToSendCrypto) + else sendState.convertToFiat(state.amountToSendCrypto) + val rescaledBalance = sendState.convertToFiat(state.balanceCrypto, true) state.copy( viewAmountValue = fiatToSend.stripZeroPlainString(), - viewBalanceValue = converter.toFiat(state.balanceCrypto).stripZeroPlainString(), - mainCurrency = Value(MainCurrencyType.FIAT, store.state.globalState.appCurrency), + viewBalanceValue = rescaledBalance.stripZeroPlainString(), + mainCurrency = state.createMainCurrencyValue(action.mainCurrency), maxLengthOfAmount = sendState.getDecimals(action.mainCurrency), cursorAtTheSamePosition = false ) } MainCurrencyType.CRYPTO -> { - val mainCurrency = Value(MainCurrencyType.CRYPTO, sendState.amount?.currencySymbol ?: "null") state.copy( viewAmountValue = state.amountToSendCrypto.stripZeroPlainString(), viewBalanceValue = state.balanceCrypto.stripZeroPlainString(), - mainCurrency = mainCurrency, + mainCurrency = state.createMainCurrencyValue(action.mainCurrency), maxLengthOfAmount = sendState.getDecimals(action.mainCurrency), cursorAtTheSamePosition = false ) @@ -69,7 +65,7 @@ class AmountReducer : SendInternalReducer { } val etFieldValue = if (state.mainCurrency.value == MainCurrencyType.CRYPTO) maxAmount - else converter.toFiat(maxAmount) + else sendState.convertToFiat(maxAmount) state.copy( viewAmountValue = etFieldValue.stripZeroPlainString(), amountToSendCrypto = maxAmount, @@ -83,25 +79,21 @@ class AmountReducer : SendInternalReducer { } private fun handleAction(action: AmountAction, sendState: SendState, state: AmountState): SendState { - val decimals = sendState.amount?.decimals ?: return sendState - val result = when (action) { is AmountAction.AmountVerification.SetAmount -> { - setAmount(sendState.currencyConverter, decimals, action.amount, state) + setAmount(sendState, action.amount, state) } is AmountAction.AmountVerification.SetError -> { - setAmount(sendState.currencyConverter, decimals, action.amount, state).copy(error = action.error) + setAmount(sendState, action.amount, state).copy(error = action.error) } } - return updateLastState(sendState.copy(amountState = result), result) - } - private fun setAmount(converter: CurrencyConverter, decimals: Int, amount: BigDecimal, state: AmountState): AmountState { + private fun setAmount(sendState: SendState, amount: BigDecimal, state: AmountState): AmountState { return when (state.mainCurrency.value) { MainCurrencyType.FIAT -> { - val amountCrypto = converter.toCrypto(amount, decimals).stripTrailingZeros() + val amountCrypto = sendState.convertToCrypto(amount) state.copy( viewAmountValue = amount.stripZeroPlainString(), amountToSendCrypto = amountCrypto, diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt index 91726cb199..41eeb5d614 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Wallet import com.tangem.tap.common.CurrencyConverter +import com.tangem.tap.common.extensions.scaleToFiat import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.features.send.redux.ReceiptAction.RefreshReceipt import com.tangem.tap.features.send.redux.SendScreenAction @@ -24,20 +25,21 @@ class ReceiptReducer : SendInternalReducer { private fun handleRefresh(action: RefreshReceipt, sendState: SendState, state: ReceiptState): SendState { val wallet = sendState.walletManager?.wallet ?: return sendState - val converter = sendState.currencyConverter + val coinConverter = sendState.coinConverter + val tokenConverter = sendState.tokenConverter val amountState = sendState.amountState val feeState = sendState.feeState - val layoutType = determineLayoutType(amountState.mainCurrency.value, amountState.typeOfAmount) + val layoutType = determineLayoutType(amountState.mainCurrency.value, sendState.amountState.typeOfAmount) val symbols = determineSymbols(wallet) val showBlank = !sendState.isReadyToSend() val result = state.copy( visibleTypeOfReceipt = layoutType, mainCurrencyType = sendState.amountState.mainCurrency, - fiat = createFiatType(converter, amountState, feeState, symbols, showBlank), - crypto = createCryptoType(converter, amountState, feeState, symbols, showBlank), - tokenFiat = createTokenFiatType(converter, amountState, feeState, symbols, showBlank), - tokenCrypto = createTokenCryptoType(converter, amountState, feeState, symbols, showBlank) + fiat = createFiatType(coinConverter, amountState, feeState, symbols, showBlank), + crypto = createCryptoType(coinConverter, amountState, feeState, symbols, showBlank), + tokenFiat = createTokenFiatType(coinConverter, tokenConverter, amountState, feeState, symbols, showBlank), + tokenCrypto = createTokenCryptoType(coinConverter, tokenConverter, amountState, feeState, symbols, showBlank) ) return updateLastState(sendState.copy(receiptState = result), result) } @@ -50,15 +52,15 @@ class ReceiptReducer : SendInternalReducer { showBlank: Boolean ): ReceiptFiat { val feeCrypto = feeState.getCurrentFee() - val feeFiat = converter.toFiat(feeCrypto) + val feeFiat = converter.toFiatWithPrecision(feeCrypto) if (showBlank) { - return ReceiptFiat("0", feeFiat.stripZeroPlainString(), feeFiat.stripZeroPlainString(), "0", symbols) + return ReceiptFiat("0", feeFiat.stripZeroPlainString(), "0", "0", symbols) } return if (feeState.feeIsIncluded) { - val amountFiat = converter.toFiat(amountState.amountToSendCrypto.minus(feeCrypto)) - val totalFiat = converter.toFiat(amountState.amountToSendCrypto) + val amountFiat = converter.toFiatWithPrecision(amountState.amountToSendCrypto.minus(feeCrypto)) + val totalFiat = converter.toFiatWithPrecision(amountState.amountToSendCrypto) ReceiptFiat( amountFiat = amountFiat.stripZeroPlainString(), feeFiat = feeFiat.stripZeroPlainString(), @@ -68,8 +70,8 @@ class ReceiptReducer : SendInternalReducer { ) } else { val totalAmountCrypto = amountState.amountToSendCrypto.plus(feeCrypto) - val amountFiat = converter.toFiat(amountState.amountToSendCrypto) - val totalFiat = converter.toFiat(totalAmountCrypto) + val amountFiat = converter.toFiatWithPrecision(amountState.amountToSendCrypto) + val totalFiat = converter.toFiatWithPrecision(totalAmountCrypto) ReceiptFiat( amountFiat = amountFiat.stripZeroPlainString(), feeFiat = feeFiat.stripZeroPlainString(), @@ -88,9 +90,9 @@ class ReceiptReducer : SendInternalReducer { showBlank: Boolean ): ReceiptCrypto { val feeCrypto = feeState.getCurrentFee() - + val feeFiat = converter.toFiatWithPrecision(feeCrypto) if (showBlank) { - return ReceiptCrypto("0", feeCrypto.stripZeroPlainString(), "0", converter.toFiat(feeCrypto).stripZeroPlainString(), "0", symbols) + return ReceiptCrypto("0", feeCrypto.stripZeroPlainString(), "0", "0", "0", symbols) } if (feeState.feeIsIncluded) { @@ -98,8 +100,8 @@ class ReceiptReducer : SendInternalReducer { amountCrypto = amountState.amountToSendCrypto.minus(feeCrypto).stripZeroPlainString(), feeCrypto = feeCrypto.stripZeroPlainString(), totalCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), - feeFiat = converter.toFiat(feeCrypto).stripZeroPlainString(), - willSentFiat = converter.toFiat(amountState.amountToSendCrypto).stripZeroPlainString(), + feeFiat = feeFiat.stripZeroPlainString(), + willSentFiat = converter.toFiatWithPrecision(amountState.amountToSendCrypto).stripZeroPlainString(), symbols = symbols ) } else { @@ -108,7 +110,7 @@ class ReceiptReducer : SendInternalReducer { amountCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), feeCrypto = feeCrypto.stripZeroPlainString(), totalCrypto = totalCrypto.stripZeroPlainString(), - feeFiat = converter.toFiat(feeCrypto).stripZeroPlainString(), + feeFiat = feeFiat.stripZeroPlainString(), willSentFiat = converter.toFiat(totalCrypto).stripZeroPlainString(), symbols = symbols ) @@ -116,47 +118,55 @@ class ReceiptReducer : SendInternalReducer { } private fun createTokenFiatType( - converter: CurrencyConverter, + coinConverter: CurrencyConverter, + tokenConverter: CurrencyConverter, amountState: AmountState, feeState: FeeState, symbols: ReceiptSymbols, showBlank: Boolean ): ReceiptTokenFiat { - val feeCrypto = feeState.getCurrentFee() - val amountFiat = converter.toFiat(amountState.amountToSendCrypto) - val feeFiat = converter.toFiat(feeCrypto) - + val feeCoin = feeState.getCurrentFee() + val feeFiat = coinConverter.toFiatWithPrecision(feeCoin) if (showBlank) { return ReceiptTokenFiat("0", feeFiat.stripZeroPlainString(), "0", "0", "0", symbols) } + val tokensToSend = amountState.amountToSendCrypto + val amountFiat = tokenConverter.toFiatUnscaled(tokensToSend) + val totalFiat = amountFiat.plus(feeFiat) + return ReceiptTokenFiat( - amountFiat = amountFiat.stripZeroPlainString(), + amountFiat = amountFiat.scaleToFiat().stripZeroPlainString(), feeFiat = feeFiat.stripZeroPlainString(), - totalFiat = amountFiat.plus(feeFiat).stripZeroPlainString(), - willSentTokenCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), - willSentFeeCrypto = feeCrypto.stripZeroPlainString(), + totalFiat = totalFiat.scaleToFiat().stripZeroPlainString(), + willSentToken = tokensToSend.stripZeroPlainString(), + willSentFeeCoin = feeCoin.stripZeroPlainString(), symbols = symbols ) } private fun createTokenCryptoType( - converter: CurrencyConverter, + coinConverter: CurrencyConverter, + tokenConverter: CurrencyConverter, amountState: AmountState, feeState: FeeState, symbols: ReceiptSymbols, showBlank: Boolean ): ReceiptTokenCrypto { - val feeCrypto = feeState.getCurrentFee() - val totalFiat = converter.toFiat(amountState.amountToSendCrypto).plus(converter.toFiat(feeCrypto)) + val feeCoin = feeState.getCurrentFee() if (showBlank) { - return ReceiptTokenCrypto("0", feeCrypto.stripZeroPlainString(), "0", symbols) + return ReceiptTokenCrypto("0", feeCoin.stripZeroPlainString(), "0", symbols) } + val tokensToSend = amountState.amountToSendCrypto + val tokenFiat = tokenConverter.toFiatUnscaled(tokensToSend) + val feeFiat = coinConverter.toFiatUnscaled(feeCoin) + val totalFiat = tokenFiat.plus(feeFiat) + return ReceiptTokenCrypto( - amountToken = amountState.amountToSendCrypto.stripZeroPlainString(), - feeCrypto = feeCrypto.stripZeroPlainString(), - totalFiat = totalFiat.stripZeroPlainString(), + amountToken = tokensToSend.stripZeroPlainString(), + feeCoin = feeCoin.stripZeroPlainString(), + totalFiat = totalFiat.scaleToFiat().stripZeroPlainString(), symbols = symbols ) } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt index 858b626b15..8a18db9d5e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt @@ -1,6 +1,6 @@ package com.tangem.tap.features.send.redux.reducers -import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.AmountType import com.tangem.tap.common.CurrencyConverter import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.states.IdStateHolder @@ -54,19 +54,34 @@ private class EmptyReducer : SendInternalReducer { private class PrepareSendScreenStatesReducer : SendInternalReducer { override fun handle(action: SendScreenAction, sendState: SendState): SendState { - val amount = (action as PrepareSendScreen).tokenAmount ?: action.coinAmount + val prepareAction = action as PrepareSendScreen val walletManager = store.state.globalState.scanNoteResponse!!.walletManager!! + val walletAmount = prepareAction.tokenAmount ?: prepareAction.coinAmount!! + val decimals = walletAmount.decimals + + val coinConverter = createCurrencyConverter(walletManager.wallet.blockchain.currency, decimals) + val tokenConverter = createCurrencyConverter(prepareAction.tokenAmount?.currencySymbol ?: "", decimals) + if (coinConverter == null || (walletAmount.type == AmountType.Token && tokenConverter == null)) { + return sendState.copy(hasInitializationError = true) + } return sendState.copy( - amount = amount, walletManager = walletManager, - currencyConverter = createCurrencyConverter(walletManager), - amountState = sendState.amountState.copy(balanceCrypto = amount?.value ?: BigDecimal.ZERO) + coinConverter = coinConverter, + tokenConverter = tokenConverter ?: CurrencyConverter(BigDecimal.ONE, decimals), + amountState = sendState.amountState.copy( + walletAmount = walletAmount, + typeOfAmount = walletAmount.type, + balanceCrypto = walletAmount.value ?: BigDecimal.ZERO + ), + feeState = sendState.feeState.copy( + includeFeeSwitcherIsEnabled = walletAmount.type == AmountType.Coin + ) ) } - private fun createCurrencyConverter(walletManager: WalletManager): CurrencyConverter { - val rate = store.state.globalState.conversionRates.getRate(walletManager.wallet.blockchain.currency) - return if (rate == null) CurrencyConverter(BigDecimal.ONE) else CurrencyConverter(rate) + private fun createCurrencyConverter(currency: String, decimals: Int): CurrencyConverter? { + val rate = store.state.globalState.conversionRates.getRate(currency) + return if (rate == null) null else CurrencyConverter(rate, decimals) } } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt index e7e10b5acd..6c453d6ae5 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt @@ -9,10 +9,11 @@ data class AddressPayIdState( val recipientWalletAddress: String? = null, val error: AddressPayIdVerifyAction.Error? = null, val truncateHandler: ((String) -> String)? = null, - val pasteIsEnabled: Boolean = false, - override val stateId: StateId = StateId.ADDRESS_PAY_ID + val pasteIsEnabled: Boolean = false ) : SendScreenState { + override val stateId: StateId = StateId.ADDRESS_PAY_ID + fun isReady(): Boolean = error == null && recipientWalletAddress?.isNotEmpty() ?: false fun isPayIdState(): Boolean = recipientWalletAddress != null && recipientWalletAddress != normalFieldValue diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt index 37260d98be..3e450e5c09 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt @@ -19,9 +19,12 @@ data class FeeState( val mainLayoutIsVisible: Boolean = false, val controlsLayoutIsVisible: Boolean = false, val feeChipGroupIsVisible: Boolean = true, + val includeFeeSwitcherIsEnabled: Boolean = true, val error: FeeAction.Error? = null, - override val stateId: StateId = StateId.FEE ) : SendScreenState { + + override val stateId: StateId = StateId.FEE + fun isReady(): Boolean = error == null && currentFee != null fun getCurrentFee(): BigDecimal = currentFee?.value?.value ?: BigDecimal.ZERO diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/ReceiptState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/ReceiptState.kt index 321cfe43b8..df43c5f9a5 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/ReceiptState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/ReceiptState.kt @@ -11,9 +11,10 @@ data class ReceiptState( val crypto: ReceiptCrypto? = null, val tokenFiat: ReceiptTokenFiat? = null, val tokenCrypto: ReceiptTokenCrypto? = null, - val mainCurrencyType: Value? = null, - override val stateId: StateId = StateId.RECEIPT -) : SendScreenState + val mainCurrencyType: Value? = null +) : SendScreenState { + override val stateId: StateId = StateId.RECEIPT +} data class ReceiptSymbols( val fiat: String, @@ -40,7 +41,7 @@ data class ReceiptCrypto( data class ReceiptTokenCrypto( val amountToken: String, - val feeCrypto: String, + val feeCoin: String, val totalFiat: String, val symbols: ReceiptSymbols ) @@ -49,7 +50,7 @@ data class ReceiptTokenFiat( val amountFiat: String, val feeFiat: String, val totalFiat: String, - val willSentTokenCrypto: String, - val willSentFeeCrypto: String, + val willSentToken: String, + val willSentFeeCoin: String, val symbols: ReceiptSymbols ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index 7d3df6c928..578cda3497 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -6,7 +6,7 @@ import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.isZero import com.tangem.tap.common.CurrencyConverter import com.tangem.tap.common.entities.TapCurrency -import com.tangem.tap.features.send.redux.AmountAction +import com.tangem.tap.domain.TapError import com.tangem.tap.store import org.rekotlin.StateType import java.math.BigDecimal @@ -26,18 +26,20 @@ enum class StateId { interface SendScreenState : StateType, IdStateHolder data class SendState( - val amount: Amount? = null, val walletManager: WalletManager? = null, - val currencyConverter: CurrencyConverter = CurrencyConverter(BigDecimal.ONE), + val coinConverter: CurrencyConverter = CurrencyConverter(BigDecimal.ONE, 2), + val tokenConverter: CurrencyConverter = CurrencyConverter(BigDecimal.ONE, 2), val lastChangedStates: LinkedHashSet = linkedSetOf(), val addressPayIdState: AddressPayIdState = AddressPayIdState(), val amountState: AmountState = AmountState(), val feeState: FeeState = FeeState(), val receiptState: ReceiptState = ReceiptState(), val sendButtonState: SendButtonState = SendButtonState.DISABLED, - override val stateId: StateId = StateId.SEND_SCREEN + val hasInitializationError: Boolean = false ) : SendScreenState { + override val stateId: StateId = StateId.SEND_SCREEN + fun isReadyToSend(): Boolean { val sendState = store.state.sendState return addressPayIdIsReady() && sendState.amountState.isReady() && sendState.feeState.isReady() @@ -47,7 +49,28 @@ data class SendState( fun getDecimals(type: MainCurrencyType): Int = when (type) { MainCurrencyType.FIAT -> 2 - MainCurrencyType.CRYPTO -> amount?.decimals ?: 0 + MainCurrencyType.CRYPTO -> amountState.walletAmount?.decimals ?: 0 + } + + fun getConverter(): CurrencyConverter { + return if (amountState.typeOfAmount == AmountType.Coin) coinConverter + else tokenConverter + } + + fun convertToCrypto(value: BigDecimal): BigDecimal { + return getConverter().toCrypto(value) + } + + fun convertToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { + val converter = getConverter() + return if (scaleWithPrecision) converter.toFiat(value) else converter.toFiatWithPrecision(value) + } + + fun convertInputValueToCrypto(inputValue: BigDecimal): BigDecimal { + return when (amountState.mainCurrency.value) { + MainCurrencyType.FIAT -> convertToCrypto(inputValue) + MainCurrencyType.CRYPTO -> inputValue + } } fun getButtonState(): SendButtonState = if (isReadyToSend()) SendButtonState.ENABLED else SendButtonState.DISABLED @@ -58,18 +81,30 @@ enum class SendButtonState { } data class AmountState( + val walletAmount: Amount? = null, + val typeOfAmount: AmountType = AmountType.Coin, val viewAmountValue: String = BigDecimal.ZERO.toPlainString(), val viewBalanceValue: String = BigDecimal.ZERO.toPlainString(), val mainCurrency: Value = Value(MainCurrencyType.FIAT, TapCurrency.DEFAULT_FIAT_CURRENCY), - val typeOfAmount: AmountType = AmountType.Coin, val amountToSendCrypto: BigDecimal = BigDecimal.ZERO, val balanceCrypto: BigDecimal = BigDecimal.ZERO, val cursorAtTheSamePosition: Boolean = true, val maxLengthOfAmount: Int = 2, - val error: AmountAction.Error? = null, - override val stateId: StateId = StateId.AMOUNT + val error: TapError? = null ) : SendScreenState { + + override val stateId: StateId = StateId.AMOUNT + fun isReady(): Boolean = error == null && !amountToSendCrypto.isZero() + + fun isCoinAmount(): Boolean = typeOfAmount == AmountType.Coin + + fun createMainCurrencyValue(type: MainCurrencyType): Value { + return when (type) { + MainCurrencyType.FIAT -> Value(type, store.state.globalState.appCurrency) + MainCurrencyType.CRYPTO -> Value(type, walletAmount?.currencySymbol ?: "NONE") + } + } } enum class MainCurrencyType { diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index 8201b03f38..415731d02c 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -16,6 +16,7 @@ import com.tangem.tap.common.extensions.getDrawableCompat import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.extensions.setOnImeActionListener import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity +import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.common.snackBar.MaxAmountSnackbar import com.tangem.tap.common.text.truncateMiddleWith import com.tangem.tap.common.toggleWidget.* @@ -57,6 +58,11 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + if (store.state.sendState.hasInitializationError) { + store.dispatch(NavigationAction.PopBackTo()) + return + } + initSendButtonStates() setupAddressOrPayIdLayout() setupAmountLayout() @@ -187,6 +193,8 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { } override fun subscribeToStore() { + if (store.state.sendState.hasInitializationError) return + store.subscribe(sendSubscriber) { appState -> appState.skipRepeats { oldState, newState -> oldState.sendState == newState.sendState diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index c4ee8d767a..38a9768bd5 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -10,9 +10,10 @@ import com.tangem.tap.common.extensions.show import com.tangem.tap.common.extensions.update import com.tangem.tap.common.text.DecimalDigitsInputFilter import com.tangem.tap.common.toggleWidget.ProgressState +import com.tangem.tap.domain.MultiMessageError +import com.tangem.tap.domain.assembleErrorIds import com.tangem.tap.features.send.BaseStoreFragment import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error -import com.tangem.tap.features.send.redux.AmountAction import com.tangem.tap.features.send.redux.FeeAction import com.tangem.tap.features.send.redux.states.* import com.tangem.tap.features.send.ui.FeeUiHelper @@ -96,19 +97,21 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber } private fun handleAmountState(fg: BaseStoreFragment, state: AmountState) { - when (state.error) { - AmountAction.Error.FEE_GREATER_THAN_AMOUNT -> { - fg.amountContainer.parent?.beginDelayedTransition() - fg.tilAmountToSend.enableError(true, fg.getString(R.string.error_fee_greater_than_amount)) - } - AmountAction.Error.AMOUNT_WITH_FEE_GREATER_THAN_BALANCE -> { - fg.amountContainer.parent?.beginDelayedTransition() - fg.tilAmountToSend.enableError(true, fg.getString(R.string.error_amount_with_fee_greater_than_balance)) - } - null -> { - if (fg.tilAmountToSend.isErrorEnabled) fg.amountContainer.parent?.beginDelayedTransition() - fg.tilAmountToSend.enableError(false) + if (state.error != null) { + val context = fg.requireContext() + val message = when (state.error) { + is MultiMessageError -> { + val multiError = state.error as MultiMessageError + val messageList = multiError.assembleErrorIds().map { context.getString(it) } + multiError.builder(messageList) + } + else -> context.getString(state.error.localizedMessage) } + fg.amountContainer.parent?.beginDelayedTransition() + fg.tilAmountToSend.enableError(true, message) + } else { + if (fg.tilAmountToSend.isErrorEnabled) fg.amountContainer.parent?.beginDelayedTransition() + fg.tilAmountToSend.enableError(false) } fg.etAmountToSend.filters = arrayOf(DecimalDigitsInputFilter(12, state.maxLengthOfAmount)) @@ -162,6 +165,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber } } + fg.swIncludeFee.isEnabled = state.includeFeeSwitcherIsEnabled if (fg.swIncludeFee.isChecked != state.feeIsIncluded) { fg.swIncludeFee.isChecked = state.feeIsIncluded } @@ -182,6 +186,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val totalTokenLayout = fg.flTotalTokenCrypto as ViewGroup fun getString(id: Int): String = mainLayout.context.getString(id) + val rough = getString(R.string.sign_rough) when (state.visibleTypeOfReceipt) { ReceiptLayoutType.FIAT -> { val receipt = state.fiat ?: return @@ -190,7 +195,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber totalTokenLayout.show(false) fg.tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}") fg.tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}") - totalLayout.tvTotalValue.update("${receipt.totalFiat} ${receipt.symbols.fiat}") + totalLayout.tvTotalValue.update("$rough ${receipt.totalFiat} ${receipt.symbols.fiat}") val willSent = SpannableStringBuilder() .bold { append(receipt.willSentCrypto) }.append(" ") @@ -210,7 +215,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val willSent = SpannableStringBuilder() .bold { - append(getString(R.string.sign_rough)).append(" ") + append(rough).append(" ") append(receipt.willSentFiat).append(" ") append(receipt.symbols.fiat) append(" (fee: ${receipt.feeFiat} ") @@ -231,11 +236,11 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber .bold { append(receipt.symbols.token) append(" ") - append(receipt.willSentFeeCrypto) + append(receipt.willSentToken) }.append(" ").append(getString(R.string.generic_and)).append(" ") .bold { append(receipt.symbols.crypto).append(" ") - append(receipt.willSentFeeCrypto).append(" ") + append(receipt.willSentFeeCoin).append(" ") } .append(mainLayout.context.getString(R.string.send_total_will_be_sent)) totalLayout.tvWillBeSentValue.update(willSent.toString()) @@ -247,7 +252,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber totalTokenLayout.show(true) fg.tvReceiptAmountValue.update("${receipt.amountToken} ${receipt.symbols.token}") - fg.tvReceiptFeeValue.update("${receipt.feeCrypto} ${receipt.symbols.crypto}") + fg.tvReceiptFeeValue.update("${receipt.feeCoin} ${receipt.symbols.crypto}") val willSent = SpannableStringBuilder() .bold { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9373ea5d54..22a73a85d7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -57,11 +57,15 @@ Address is invalid or unsupported by blockchain Address is the same as wallet address Network fee request is failed - Fee greater than amount - Amount with fee greater than balance Insufficient balance Blockchain internal error - + amount_exceeds_balance + fee_exceeds_balance + total_exceeds_balance + invalid_amount_value + invalid_fee_value + dust_amount + dust_change Send Address or PayID From f8a1e3f2fe1f274062d6165e686c4d2324c70542 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 20 Sep 2020 00:03:29 +0300 Subject: [PATCH 03/10] Updated on 2026-08-14 --- .../features/send/redux/middlewares/AmountMiddleware.kt | 9 +++------ .../features/send/redux/middlewares/SendMiddleware.kt | 2 +- .../tangem/tap/features/send/redux/states/SendState.kt | 5 +++++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt index c68901c350..083f9f2852 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt @@ -31,13 +31,10 @@ class AmountMiddleware { if (inputValue.isZero() && sendState.amountState.amountToSendCrypto.isZero()) return val inputCrypto = sendState.convertInputValueToCrypto(inputValue) - val fee = sendState.feeState.getCurrentFee() + val feeCrypto = sendState.feeState.getCurrentFee() - val needToExtractFee = sendState.amountState.isCoinAmount() && sendState.feeState.feeIsIncluded - val totalToSend = if (needToExtractFee) inputCrypto.minus(fee) else inputCrypto - - val feeAmount = Amount(fee, walletManager.wallet.blockchain) - val totalAmount = Amount(totalToSend, walletManager.wallet.blockchain) + val feeAmount = Amount(feeCrypto, walletManager.wallet.blockchain) + val totalAmount = Amount(sendState.getTotalAmountToSend(inputCrypto), walletManager.wallet.blockchain) val transactionErrors = walletManager.validateTransaction(totalAmount, feeAmount) if (transactionErrors.isEmpty()) { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 3f55dfddd5..d0504ce882 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -59,7 +59,7 @@ private fun verifyAndSendTransaction(appState: AppState?, dispatch: (Action) -> val recipientAddress = sendState.addressPayIdState.recipientWalletAddress!! val feeAmount = Amount(sendState.feeState.getCurrentFee(), blockchain) - val amountToSend = Amount(sendState.amountState.amountToSendCrypto, blockchain, recipientAddress) + val amountToSend = Amount(sendState.getTotalAmountToSend(), blockchain, recipientAddress) val txSender = walletManager as TransactionSender diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index 578cda3497..a33dab9035 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -74,6 +74,11 @@ data class SendState( } fun getButtonState(): SendButtonState = if (isReadyToSend()) SendButtonState.ENABLED else SendButtonState.DISABLED + + fun getTotalAmountToSend(value: BigDecimal = amountState.amountToSendCrypto): BigDecimal { + val needToExtractFee = amountState.isCoinAmount() && feeState.feeIsIncluded + return if (needToExtractFee) value.minus(feeState.getCurrentFee()) else value + } } enum class SendButtonState { From 52391d0dad1c7b1b42cbfbeb675a1b1043b94fb8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 20 Sep 2020 18:22:00 +0300 Subject: [PATCH 04/10] Updated on 2026-08-14 --- .../features/send/redux/SendScreenAction.kt | 9 +- .../redux/middlewares/AmountMiddleware.kt | 64 ++++++++++++-- .../send/redux/middlewares/SendMiddleware.kt | 4 +- .../send/redux/reducers/AmountReducer.kt | 86 +++++++------------ .../send/redux/reducers/FeeReducer.kt | 9 +- .../send/redux/reducers/ReceiptReducer.kt | 6 +- .../features/send/redux/states/FeeState.kt | 4 +- .../send/redux/states/ReceiptState.kt | 2 +- .../features/send/redux/states/SendState.kt | 26 +++--- .../tap/features/send/ui/SendFragment.kt | 12 +-- .../stateSubscribers/SendStateSubscriber.kt | 8 +- 11 files changed, 120 insertions(+), 110 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 5596123814..a8ecd6b2a4 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -57,17 +57,16 @@ sealed class AddressPayIdVerifyAction : SendScreenAction { // Amount to send sealed class AmountActionUi : SendScreenActionUi { + data class HandleUserInput(val proposedAmount: String) : AmountActionUi() + object CheckAmountToSend : AmountActionUi() object SetMaxAmount : AmountActionUi() - data class CheckAmountToSend(val data: String? = null) : AmountActionUi() data class SetMainCurrency(val mainCurrency: MainCurrencyType) : AmountActionUi() object ToggleMainCurrency : AmountActionUi() } sealed class AmountAction : SendScreenAction { - sealed class AmountVerification : AmountAction() { - data class SetAmount(val amount: BigDecimal) : AmountVerification() - data class SetError(val amount: BigDecimal, val error: TapError) : AmountVerification() - } + data class SetAmount(val amount: BigDecimal, val isUserInput: Boolean) : AmountAction() + data class SetAmountError(val error: TapError?) : AmountAction() } // Fee diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt index 083f9f2852..6d791062f5 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt @@ -3,12 +3,14 @@ package com.tangem.tap.features.send.redux.middlewares import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.TransactionError import com.tangem.common.extensions.isZero +import com.tangem.tap.common.extensions.isNegative import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.TapError import com.tangem.tap.features.send.redux.AmountAction +import com.tangem.tap.features.send.redux.AmountActionUi import com.tangem.tap.features.send.redux.ReceiptAction import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.store +import com.tangem.tap.features.send.redux.states.MainCurrencyType import org.rekotlin.Action import java.math.BigDecimal @@ -17,12 +19,20 @@ import java.math.BigDecimal */ class AmountMiddleware { - fun handle(rawData: String?, appState: AppState?, dispatch: (Action) -> Unit) { - val sendState = appState?.sendState ?: return - val walletManager = sendState.walletManager ?: return + fun handle(action: AmountActionUi, appState: AppState?, dispatch: (Action) -> Unit) { + when (action) { + is AmountActionUi.HandleUserInput -> handleUserInput(action.proposedAmount, appState, dispatch) + is AmountActionUi.CheckAmountToSend -> checkAmountToSend(appState, dispatch) + is AmountActionUi.SetMaxAmount -> setMaxAmount(appState, dispatch) + is AmountActionUi.ToggleMainCurrency -> toggleMainCurrency(appState, dispatch) + is AmountActionUi.SetMainCurrency -> return + } + } - val rawData = rawData ?: store.state.sendState.amountState.viewAmountValue - val data = if (rawData == ".") "0.0" else rawData + private fun handleUserInput(data: String, appState: AppState?, dispatch: (Action) -> Unit) { + val sendState = appState?.sendState ?: return + + val data = if (data == ".") "0.0" else data val inputValue = when { data.isEmpty() || data == "0" -> BigDecimal.ZERO else -> BigDecimal(data) @@ -30,7 +40,17 @@ class AmountMiddleware { if (inputValue.isZero() && sendState.amountState.amountToSendCrypto.isZero()) return - val inputCrypto = sendState.convertInputValueToCrypto(inputValue) + dispatch(AmountAction.SetAmount(inputValue, true)) + dispatch(AmountActionUi.CheckAmountToSend) + } + + private fun checkAmountToSend(appState: AppState?, dispatch: (Action) -> Unit, isUserInput: Boolean = false) { + val sendState = appState?.sendState ?: return + val walletManager = sendState.walletManager ?: return + + val inputCrypto = sendState.amountState.amountToSendCrypto + if (sendState.amountState.viewAmountValue.value == "0" && inputCrypto.isZero()) return + val feeCrypto = sendState.feeState.getCurrentFee() val feeAmount = Amount(feeCrypto, walletManager.wallet.blockchain) @@ -38,7 +58,7 @@ class AmountMiddleware { val transactionErrors = walletManager.validateTransaction(totalAmount, feeAmount) if (transactionErrors.isEmpty()) { - dispatch(AmountAction.AmountVerification.SetAmount(inputValue)) + dispatch(AmountAction.SetAmountError(null)) } else { val tapErrors = transactionErrors.map { when (it) { @@ -53,10 +73,36 @@ class AmountMiddleware { } } val error = TapError.ValidateTransactionErrors(tapErrors) { it.joinToString("\r\n") } - dispatch(AmountAction.AmountVerification.SetError(inputValue, error)) + dispatch(AmountAction.SetAmountError(error)) } dispatch(ReceiptAction.RefreshReceipt) dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) } + private fun setMaxAmount(appState: AppState?, dispatch: (Action) -> Unit) { + val sendState = appState?.sendState ?: return + val amountState = sendState.amountState + + val maxAmount = if (sendState.feeState.feeIsIncluded) { + amountState.balanceCrypto + } else { + val balanceExtractFee = amountState.balanceCrypto.minus(sendState.feeState.getCurrentFee()) + if (balanceExtractFee.isNegative()) BigDecimal.ZERO + else balanceExtractFee + } + + val currentCurrencyValue = if (amountState.mainCurrency.type == MainCurrencyType.CRYPTO) maxAmount + else sendState.convertToFiat(maxAmount) + + dispatch(AmountAction.SetAmount(currentCurrencyValue, false)) + } + + private fun toggleMainCurrency(appState: AppState?, dispatch: (Action) -> Unit) { + val amountState = appState?.sendState?.amountState ?: return + + val type = if (amountState.mainCurrency.type == MainCurrencyType.FIAT) MainCurrencyType.CRYPTO + else MainCurrencyType.FIAT + + dispatch(AmountActionUi.SetMainCurrency(type)) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index d0504ce882..b6261e5f90 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -9,7 +9,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TapError import com.tangem.tap.features.send.redux.AddressPayIdActionUi.ChangeAddressOrPayId import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.* -import com.tangem.tap.features.send.redux.AmountActionUi.CheckAmountToSend +import com.tangem.tap.features.send.redux.AmountActionUi import com.tangem.tap.features.send.redux.FeeAction.RequestFee import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.send.redux.SendActionUi @@ -42,7 +42,7 @@ val sendMiddleware: Middleware = { dispatch, appState -> } } } - is CheckAmountToSend -> AmountMiddleware().handle(action.data, appState(), dispatch) + is AmountActionUi -> AmountMiddleware().handle(action, appState(), dispatch) is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch) is SendActionUi.SendAmountToRecipient -> verifyAndSendTransaction(appState(), dispatch) } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt index a9416145d3..8e89987039 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt @@ -1,15 +1,16 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.common.extensions.isZero -import com.tangem.tap.common.extensions.isNegative +import com.tangem.tap.common.extensions.scaleToFiat import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.features.send.redux.AmountAction import com.tangem.tap.features.send.redux.AmountActionUi -import com.tangem.tap.features.send.redux.AmountActionUi.* +import com.tangem.tap.features.send.redux.AmountActionUi.SetMainCurrency import com.tangem.tap.features.send.redux.SendScreenAction import com.tangem.tap.features.send.redux.states.AmountState import com.tangem.tap.features.send.redux.states.MainCurrencyType import com.tangem.tap.features.send.redux.states.SendState +import com.tangem.tap.features.send.redux.states.ViewAmountValue import java.math.BigDecimal /** @@ -24,12 +25,6 @@ class AmountReducer : SendInternalReducer { private fun handleUiAction(action: AmountActionUi, sendState: SendState, state: AmountState): SendState { val result = when (action) { - is ToggleMainCurrency -> { - val type = if (state.mainCurrency.value == MainCurrencyType.FIAT) MainCurrencyType.CRYPTO - else MainCurrencyType.FIAT - - return handleUiAction(SetMainCurrency(type), sendState, state) - } is SetMainCurrency -> { when (action.mainCurrency) { MainCurrencyType.FIAT -> { @@ -37,42 +32,25 @@ class AmountReducer : SendInternalReducer { else sendState.convertToFiat(state.amountToSendCrypto) val rescaledBalance = sendState.convertToFiat(state.balanceCrypto, true) state.copy( - viewAmountValue = fiatToSend.stripZeroPlainString(), + viewAmountValue = ViewAmountValue(fiatToSend.stripZeroPlainString()), viewBalanceValue = rescaledBalance.stripZeroPlainString(), - mainCurrency = state.createMainCurrencyValue(action.mainCurrency), + mainCurrency = state.createMainCurrency(action.mainCurrency), maxLengthOfAmount = sendState.getDecimals(action.mainCurrency), cursorAtTheSamePosition = false ) } MainCurrencyType.CRYPTO -> { state.copy( - viewAmountValue = state.amountToSendCrypto.stripZeroPlainString(), + viewAmountValue = ViewAmountValue(state.amountToSendCrypto.stripZeroPlainString()), viewBalanceValue = state.balanceCrypto.stripZeroPlainString(), - mainCurrency = state.createMainCurrencyValue(action.mainCurrency), + mainCurrency = state.createMainCurrency(action.mainCurrency), maxLengthOfAmount = sendState.getDecimals(action.mainCurrency), cursorAtTheSamePosition = false ) } } } - is SetMaxAmount -> { - val maxAmount = if (sendState.feeState.feeIsIncluded) { - state.balanceCrypto - } else { - val balanceExtractFee = state.balanceCrypto.minus(sendState.feeState.getCurrentFee()) - if (balanceExtractFee.isNegative()) BigDecimal.ZERO - else balanceExtractFee - } - - val etFieldValue = if (state.mainCurrency.value == MainCurrencyType.CRYPTO) maxAmount - else sendState.convertToFiat(maxAmount) - state.copy( - viewAmountValue = etFieldValue.stripZeroPlainString(), - amountToSendCrypto = maxAmount, - cursorAtTheSamePosition = false - ) - } - is CheckAmountToSend -> return sendState + else -> return sendState } return updateLastState(sendState.copy(amountState = result), result) @@ -80,35 +58,31 @@ class AmountReducer : SendInternalReducer { private fun handleAction(action: AmountAction, sendState: SendState, state: AmountState): SendState { val result = when (action) { - is AmountAction.AmountVerification.SetAmount -> { - setAmount(sendState, action.amount, state) + is AmountAction.SetAmount -> { + when (state.mainCurrency.type) { + MainCurrencyType.FIAT -> { + val amountCrypto = sendState.convertToCrypto(action.amount) + state.copy( + viewAmountValue = ViewAmountValue(action.amount.scaleToFiat(false).stripZeroPlainString(), action.isUserInput), + amountToSendCrypto = amountCrypto, + cursorAtTheSamePosition = true, + error = null + ) + } + MainCurrencyType.CRYPTO -> { + state.copy( + viewAmountValue = ViewAmountValue(action.amount.stripZeroPlainString(), action.isUserInput), + amountToSendCrypto = action.amount, + cursorAtTheSamePosition = true, + error = null + ) + } + } } - is AmountAction.AmountVerification.SetError -> { - setAmount(sendState, action.amount, state).copy(error = action.error) + is AmountAction.SetAmountError -> { + state.copy(error = action.error) } } return updateLastState(sendState.copy(amountState = result), result) } - - private fun setAmount(sendState: SendState, amount: BigDecimal, state: AmountState): AmountState { - return when (state.mainCurrency.value) { - MainCurrencyType.FIAT -> { - val amountCrypto = sendState.convertToCrypto(amount) - state.copy( - viewAmountValue = amount.stripZeroPlainString(), - amountToSendCrypto = amountCrypto, - cursorAtTheSamePosition = true, - error = null - ) - } - MainCurrencyType.CRYPTO -> { - state.copy( - viewAmountValue = amount.stripZeroPlainString(), - amountToSendCrypto = amount, - cursorAtTheSamePosition = true, - error = null - ) - } - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt index 883ca9f708..f5da58ab54 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt @@ -2,14 +2,12 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.blockchain.common.Amount import com.tangem.blockchain.extensions.isAboveZero -import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.features.send.redux.FeeAction import com.tangem.tap.features.send.redux.FeeActionUi import com.tangem.tap.features.send.redux.SendScreenAction import com.tangem.tap.features.send.redux.states.FeeState import com.tangem.tap.features.send.redux.states.FeeType import com.tangem.tap.features.send.redux.states.SendState -import com.tangem.tap.features.send.redux.states.Value /** [REDACTED_AUTHOR] @@ -86,9 +84,10 @@ class FeeReducer : SendInternalReducer { return updateLastState(sendState.copy(feeState = result), result) } - private fun createValueOfFeeAmount(feeType: FeeType, list: List?): Value? { + private fun createValueOfFeeAmount(feeType: FeeType, list: List?): Amount? { if (list == null || list.isEmpty()) return null - val feeAmount = if (list.size == 1) { + + return if (list.size == 1) { if (!list[0].isAboveZero()) return null list[0] @@ -100,8 +99,6 @@ class FeeReducer : SendInternalReducer { FeeType.PRIORITY -> list[2] } } - - return Value(feeAmount, feeAmount.value?.stripZeroPlainString() ?: "") } private fun getCurrentFeeType(state: FeeState): FeeType { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt index 41eeb5d614..0d92ab9a57 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt @@ -30,12 +30,12 @@ class ReceiptReducer : SendInternalReducer { val amountState = sendState.amountState val feeState = sendState.feeState - val layoutType = determineLayoutType(amountState.mainCurrency.value, sendState.amountState.typeOfAmount) + val layoutType = determineLayoutType(amountState.mainCurrency.type, sendState.amountState.typeOfAmount) val symbols = determineSymbols(wallet) val showBlank = !sendState.isReadyToSend() val result = state.copy( visibleTypeOfReceipt = layoutType, - mainCurrencyType = sendState.amountState.mainCurrency, + mainCurrency = sendState.amountState.mainCurrency, fiat = createFiatType(coinConverter, amountState, feeState, symbols, showBlank), crypto = createCryptoType(coinConverter, amountState, feeState, symbols, showBlank), tokenFiat = createTokenFiatType(coinConverter, tokenConverter, amountState, feeState, symbols, showBlank), @@ -111,7 +111,7 @@ class ReceiptReducer : SendInternalReducer { feeCrypto = feeCrypto.stripZeroPlainString(), totalCrypto = totalCrypto.stripZeroPlainString(), feeFiat = feeFiat.stripZeroPlainString(), - willSentFiat = converter.toFiat(totalCrypto).stripZeroPlainString(), + willSentFiat = converter.toFiatWithPrecision(totalCrypto).stripZeroPlainString(), symbols = symbols ) } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt index 3e450e5c09..72575e6a6b 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt @@ -14,7 +14,7 @@ enum class FeeType { data class FeeState( val selectedFeeType: FeeType = FeeType.NORMAL, val feeList: List? = null, - val currentFee: Value? = null, + val currentFee: Amount? = null, val feeIsIncluded: Boolean = false, val mainLayoutIsVisible: Boolean = false, val controlsLayoutIsVisible: Boolean = false, @@ -27,5 +27,5 @@ data class FeeState( fun isReady(): Boolean = error == null && currentFee != null - fun getCurrentFee(): BigDecimal = currentFee?.value?.value ?: BigDecimal.ZERO + fun getCurrentFee(): BigDecimal = currentFee?.value ?: BigDecimal.ZERO } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/ReceiptState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/ReceiptState.kt index df43c5f9a5..90e22a0e9e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/ReceiptState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/ReceiptState.kt @@ -11,7 +11,7 @@ data class ReceiptState( val crypto: ReceiptCrypto? = null, val tokenFiat: ReceiptTokenFiat? = null, val tokenCrypto: ReceiptTokenCrypto? = null, - val mainCurrencyType: Value? = null + val mainCurrency: MainCurrency? = null ) : SendScreenState { override val stateId: StateId = StateId.RECEIPT } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index a33dab9035..7dd7d3b242 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -63,14 +63,7 @@ data class SendState( fun convertToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { val converter = getConverter() - return if (scaleWithPrecision) converter.toFiat(value) else converter.toFiatWithPrecision(value) - } - - fun convertInputValueToCrypto(inputValue: BigDecimal): BigDecimal { - return when (amountState.mainCurrency.value) { - MainCurrencyType.FIAT -> convertToCrypto(inputValue) - MainCurrencyType.CRYPTO -> inputValue - } + return if (!scaleWithPrecision) converter.toFiat(value) else converter.toFiatWithPrecision(value) } fun getButtonState(): SendButtonState = if (isReadyToSend()) SendButtonState.ENABLED else SendButtonState.DISABLED @@ -88,9 +81,9 @@ enum class SendButtonState { data class AmountState( val walletAmount: Amount? = null, val typeOfAmount: AmountType = AmountType.Coin, - val viewAmountValue: String = BigDecimal.ZERO.toPlainString(), + val viewAmountValue: ViewAmountValue = ViewAmountValue(BigDecimal.ZERO.toPlainString()), val viewBalanceValue: String = BigDecimal.ZERO.toPlainString(), - val mainCurrency: Value = Value(MainCurrencyType.FIAT, TapCurrency.DEFAULT_FIAT_CURRENCY), + val mainCurrency: MainCurrency = MainCurrency(MainCurrencyType.FIAT, TapCurrency.DEFAULT_FIAT_CURRENCY), val amountToSendCrypto: BigDecimal = BigDecimal.ZERO, val balanceCrypto: BigDecimal = BigDecimal.ZERO, val cursorAtTheSamePosition: Boolean = true, @@ -104,19 +97,20 @@ data class AmountState( fun isCoinAmount(): Boolean = typeOfAmount == AmountType.Coin - fun createMainCurrencyValue(type: MainCurrencyType): Value { + fun createMainCurrency(type: MainCurrencyType): MainCurrency { return when (type) { - MainCurrencyType.FIAT -> Value(type, store.state.globalState.appCurrency) - MainCurrencyType.CRYPTO -> Value(type, walletAmount?.currencySymbol ?: "NONE") + MainCurrencyType.FIAT -> MainCurrency(type, store.state.globalState.appCurrency) + MainCurrencyType.CRYPTO -> MainCurrency(type, walletAmount?.currencySymbol ?: "NONE") } } } +data class ViewAmountValue(val value: String, val isUserInput: Boolean = false) enum class MainCurrencyType { FIAT, CRYPTO } -data class Value( - val value: T, - val displayedValue: String +data class MainCurrency( + val type: MainCurrencyType, + val currencySymbol: String ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index 415731d02c..e731a059f6 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -128,7 +128,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { etAmountToSend.clearFocus() etAmountToSend.postDelayed(200) { etAmountToSend.hideSoftKeyboard() } store.dispatch(SetMaxAmount) - store.dispatch(CheckAmountToSend()) + store.dispatch(CheckAmountToSend) } var snackbarControlledByChangingFocus = false keyboardObserver = KeyboardObserver(requireActivity()) @@ -160,14 +160,14 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { val prevFocusChangeListener = etAmountToSend.onFocusChangeListener etAmountToSend.setOnFocusChangeListener { v, hasFocus -> prevFocusChangeListener.onFocusChange(v, hasFocus) -// if (hasFocus && etAmountToSend.text?.toString() == "0") etAmountToSend.setText("") + if (hasFocus && etAmountToSend.text?.toString() == "0") etAmountToSend.setText("") if (!hasFocus && etAmountToSend.text?.toString() == "") etAmountToSend.setText("0") } etAmountToSend.inputtedTextAsFlow() .debounce(400) - .filter { store.state.sendState.amountState.viewAmountValue != it && it.isNotEmpty() } - .onEach { store.dispatch(CheckAmountToSend(it)) } + .filter { store.state.sendState.amountState.viewAmountValue.value != it && it.isNotEmpty() } + .onEach { store.dispatch(HandleUserInput(it)) } .launchIn(mainScope) etAmountToSend.setOnImeActionListener(EditorInfo.IME_ACTION_DONE) { @@ -184,11 +184,11 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { if (checkedId == -1) return@setOnCheckedChangeListener store.dispatch(ChangeSelectedFee(FeeUiHelper.idToFee(checkedId))) - store.dispatch(CheckAmountToSend()) + store.dispatch(CheckAmountToSend) } swIncludeFee.setOnCheckedChangeListener { btn, isChecked -> store.dispatch(ChangeIncludeFee(isChecked)) - store.dispatch(CheckAmountToSend()) + store.dispatch(CheckAmountToSend) } } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 38a9768bd5..3f53f1966e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -116,7 +116,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber fg.etAmountToSend.filters = arrayOf(DecimalDigitsInputFilter(12, state.maxLengthOfAmount)) val amountToSend = state.viewAmountValue - fg.etAmountToSend.update(amountToSend) + if (!amountToSend.isUserInput) fg.etAmountToSend.update(amountToSend.value) // fg.tvAmountToSendShadow.text = amountToSend // if (amountToSend.length > 10) { @@ -134,11 +134,11 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber // if (!state.cursorAtTheSamePosition) fg.etAmountToSend.setSelection(amountToSend.length) // } - fg.tvAmountCurrency.update(state.mainCurrency.displayedValue) - (fg as? SendFragment)?.saveMainCurrency(state.mainCurrency.value) + fg.tvAmountCurrency.update(state.mainCurrency.currencySymbol) + (fg as? SendFragment)?.saveMainCurrency(state.mainCurrency.type) val balanceText = fg.getString(R.string.send_balance, - state.mainCurrency.displayedValue, + state.mainCurrency.currencySymbol, state.viewBalanceValue) fg.tvBalance.update(balanceText) } From b58c9b153668552fb146066a1505dbb887e0dba9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 20 Sep 2020 21:04:18 +0300 Subject: [PATCH 05/10] Updated on 2026-08-14 --- .../features/send/redux/SendScreenAction.kt | 17 +-- .../middlewares/AddressPayIdMiddleware.kt | 122 +++++++++++++----- .../redux/middlewares/AmountMiddleware.kt | 4 +- .../send/redux/middlewares/SendMiddleware.kt | 17 +-- .../redux/reducers/AddressPayIdReducer.kt | 36 ++++-- .../send/redux/reducers/AmountReducer.kt | 10 +- .../send/redux/states/AddressPayIdState.kt | 48 +------ .../features/send/redux/states/SendState.kt | 4 +- .../tap/features/send/ui/SendFragment.kt | 12 +- .../stateSubscribers/SendStateSubscriber.kt | 8 +- 10 files changed, 149 insertions(+), 129 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index a8ecd6b2a4..bd93f890d5 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -26,14 +26,16 @@ data class PrepareSendScreen( // Address or PayId sealed class AddressPayIdActionUi : SendScreenActionUi { - data class ChangeAddressOrPayId(val data: String) : AddressPayIdActionUi() + data class HandleUserInput(val data: String) : AddressPayIdActionUi() + data class PasteAddressPayId(val data: String) : AddressPayIdActionUi() + data class CheckClipboard(val data: String?) : AddressPayIdActionUi() + object CheckAddressPayId: AddressPayIdActionUi() data class SetTruncateHandler(val handler: (String) -> String) : AddressPayIdActionUi() data class TruncateOrRestore(val truncate: Boolean) : AddressPayIdActionUi() } sealed class AddressPayIdVerifyAction : SendScreenAction { enum class Error { - IS_NOT_PAY_ID, PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN, PAY_ID_NOT_REGISTERED, PAY_ID_REQUEST_FAILED, @@ -41,23 +43,22 @@ sealed class AddressPayIdVerifyAction : SendScreenAction { ADDRESS_SAME_AS_WALLET } - data class VerifyClipboard(val data: String?) : AddressPayIdVerifyAction() data class ChangePasteBtnEnableState(val isEnabled: Boolean) : AddressPayIdVerifyAction() sealed class PayIdVerification : AddressPayIdVerifyAction() { - data class SetError(val payId: String, val error: Error) : PayIdVerification() - data class SetPayIdWalletAddress(val payId: String, val payIdWalletAddress: String) : PayIdVerification() + data class SetPayIdError(val error: Error?) : PayIdVerification() + data class SetPayIdWalletAddress(val payId: String, val payIdWalletAddress: String, val isUserInput: Boolean) : PayIdVerification() } sealed class AddressVerification : AddressPayIdVerifyAction() { - data class SetError(val address: String, val error: Error) : AddressVerification() - data class SetWalletAddress(val address: String) : AddressVerification() + data class SetAddressError(val error: Error?) : AddressVerification() + data class SetWalletAddress(val address: String, val isUserInput: Boolean) : AddressVerification() } } // Amount to send sealed class AmountActionUi : SendScreenActionUi { - data class HandleUserInput(val proposedAmount: String) : AmountActionUi() + data class HandleUserInput(val data: String) : AmountActionUi() object CheckAmountToSend : AmountActionUi() object SetMaxAmount : AmountActionUi() data class SetMainCurrency(val mainCurrency: MainCurrencyType) : AmountActionUi() diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt index b0169f3df7..f9e70c740f 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt @@ -1,52 +1,77 @@ package com.tangem.tap.features.send.redux.middlewares import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.WalletManager import com.tangem.commands.common.network.Result import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.isPayIdSupported -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.* +import com.tangem.tap.features.send.redux.AddressPayIdActionUi +import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction +import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerification.SetAddressError +import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerification.SetWalletAddress +import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error +import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification.SetPayIdError +import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification.SetPayIdWalletAddress import com.tangem.tap.features.send.redux.FeeAction import com.tangem.tap.scope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.rekotlin.Action import org.rekotlin.DispatchFunction /** [REDACTED_AUTHOR] */ internal class AddressPayIdMiddleware { - fun handle(data: String?, appState: AppState?, dispatch: DispatchFunction) { - if (data == null) { - dispatch(AddressVerification.SetError("", Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN)) - return - } - val sendState = appState?.sendState ?: return - val walletManager = sendState.walletManager ?: return - if (data == sendState.addressPayIdState.etFieldValue) return - - if (PayIdManager.isPayId(data)) { - verifyPayId(data, walletManager, dispatch) - } else { - val supposedAddress = extractAddressFromShareUri(data) - dispatch(PayIdVerification.SetError(supposedAddress, Error.IS_NOT_PAY_ID)) - - val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(walletManager.wallet, supposedAddress) - if (failReason == null) { - dispatch(AddressVerification.SetWalletAddress(supposedAddress)) - } else { - dispatch(AddressVerification.SetError(supposedAddress, failReason)) - } + fun handle(action: AddressPayIdActionUi, appState: AppState?, dispatch: (Action) -> Unit) { + when (action) { + is AddressPayIdActionUi.HandleUserInput -> handleUserInput(action.data, appState, dispatch) + is AddressPayIdActionUi.PasteAddressPayId -> pasteAddressPayId(action.data, dispatch) + is AddressPayIdActionUi.CheckClipboard -> verifyClipboard(action.data, appState, dispatch) + is AddressPayIdActionUi.CheckAddressPayId -> verifyAddressPayId(appState, dispatch) + else -> return } } - private fun verifyPayId(payId: String, walletManager: WalletManager, dispatch: DispatchFunction) { - val blockchain = walletManager.wallet.blockchain + private fun handleUserInput(input: String, appState: AppState?, dispatch: DispatchFunction) { + val sendState = appState?.sendState ?: return + if (input == sendState.addressPayIdState.viewFieldValue.value) return + + setAddressAndCheck(input, true, dispatch) + } + + private fun pasteAddressPayId(data: String, dispatch: (Action) -> Unit) { + setAddressAndCheck(data, false, dispatch) + } + + private fun setAddressAndCheck(data: String, isUserInput: Boolean, dispatch: (Action) -> Unit) { + if (PayIdManager.isPayId(data)) { + dispatch(SetPayIdWalletAddress(data, "", isUserInput)) + } else { + dispatch(SetWalletAddress(data, isUserInput)) + } + dispatch(AddressPayIdActionUi.CheckAddressPayId) + } + + private fun verifyAddressPayId(appState: AppState?, dispatch: (Action) -> Unit) { + val sendState = appState?.sendState ?: return + val wallet = sendState.walletManager?.wallet ?: return + val addressPayId = sendState.addressPayIdState.normalFieldValue ?: return + val isUserInput = sendState.addressPayIdState.viewFieldValue.isFromUserInput + + if (PayIdManager.isPayId(addressPayId)) { + verifyPayId(addressPayId, wallet, isUserInput, dispatch) + } else { + verifyAddress(addressPayId, wallet, isUserInput, dispatch) + } + } + + private fun verifyPayId(payId: String, wallet: Wallet, isUserInput: Boolean, dispatch: DispatchFunction) { + val blockchain = wallet.blockchain if (!blockchain.isPayIdSupported()) { - dispatch(PayIdVerification.SetError(payId, Error.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN)) + dispatch(SetPayIdError(Error.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN)) return } @@ -57,27 +82,37 @@ internal class AddressPayIdMiddleware { is Result.Success -> { val address = result.data.getAddress() if (address == null) { - dispatch(PayIdVerification.SetError(payId, Error.PAY_ID_NOT_REGISTERED)) + dispatch(SetPayIdError(Error.PAY_ID_NOT_REGISTERED)) return@withContext } - val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(walletManager.wallet, address) + val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(wallet, address) if (failReason == null) { - dispatch(PayIdVerification.SetPayIdWalletAddress(payId, address)) + dispatch(SetPayIdWalletAddress(payId, address, isUserInput)) dispatch(FeeAction.RequestFee) } else { - dispatch(AddressVerification.SetError(payId, failReason)) + dispatch(SetAddressError(failReason)) } - } is Result.Failure -> { - dispatch(PayIdVerification.SetError(payId, Error.PAY_ID_REQUEST_FAILED)) + dispatch(SetPayIdError(Error.PAY_ID_REQUEST_FAILED)) } } } } } + private fun verifyAddress(address: String, wallet: Wallet, isUserInput: Boolean, dispatch: (Action) -> Unit) { + val supposedAddress = extractAddressFromShareUri(address) + + val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(wallet, supposedAddress) + if (failReason == null) { + dispatch(SetWalletAddress(supposedAddress, isUserInput)) + } else { + dispatch(SetAddressError(failReason)) + } + } + private fun isValidBlockchainAddressAndNotTheSameAsWallet(wallet: Wallet, address: String): Error? { return if (wallet.blockchain.validateAddress(address)) { if (wallet.address != address) { @@ -97,4 +132,27 @@ internal class AddressPayIdMiddleware { return if (prefixes.isEmpty()) shareUri else shareUri.replace(prefixes[0], "") } + + private fun verifyClipboard(input: String?, appState: AppState?, dispatch: DispatchFunction) { + val addressPayId = input ?: return + val wallet = appState?.sendState?.walletManager?.wallet ?: return + + + val internalDispatcher: (Action) -> Unit = { + when (it) { + is SetWalletAddress, is SetPayIdWalletAddress -> { + dispatch(AddressPayIdVerifyAction.ChangePasteBtnEnableState(true)) + } + is SetAddressError, is SetPayIdError -> { + dispatch(AddressPayIdVerifyAction.ChangePasteBtnEnableState(false)) + } + } + } + + if (PayIdManager.isPayId(addressPayId)) { + verifyPayId(addressPayId, wallet, false, internalDispatcher) + } else { + verifyAddress(addressPayId, wallet, false, internalDispatcher) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt index 6d791062f5..769fb170d4 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt @@ -21,7 +21,7 @@ class AmountMiddleware { fun handle(action: AmountActionUi, appState: AppState?, dispatch: (Action) -> Unit) { when (action) { - is AmountActionUi.HandleUserInput -> handleUserInput(action.proposedAmount, appState, dispatch) + is AmountActionUi.HandleUserInput -> handleUserInput(action.data, appState, dispatch) is AmountActionUi.CheckAmountToSend -> checkAmountToSend(appState, dispatch) is AmountActionUi.SetMaxAmount -> setMaxAmount(appState, dispatch) is AmountActionUi.ToggleMainCurrency -> toggleMainCurrency(appState, dispatch) @@ -44,7 +44,7 @@ class AmountMiddleware { dispatch(AmountActionUi.CheckAmountToSend) } - private fun checkAmountToSend(appState: AppState?, dispatch: (Action) -> Unit, isUserInput: Boolean = false) { + private fun checkAmountToSend(appState: AppState?, dispatch: (Action) -> Unit) { val sendState = appState?.sendState ?: return val walletManager = sendState.walletManager ?: return diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index b6261e5f90..cdf07f2690 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -7,8 +7,7 @@ import com.tangem.blockchain.extensions.SimpleResult import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TapError -import com.tangem.tap.features.send.redux.AddressPayIdActionUi.ChangeAddressOrPayId -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.* +import com.tangem.tap.features.send.redux.AddressPayIdActionUi import com.tangem.tap.features.send.redux.AmountActionUi import com.tangem.tap.features.send.redux.FeeAction.RequestFee import com.tangem.tap.features.send.redux.SendAction @@ -29,19 +28,7 @@ val sendMiddleware: Middleware = { dispatch, appState -> { nextDispatch -> { action -> when (action) { - is ChangeAddressOrPayId -> AddressPayIdMiddleware().handle(action.data, appState(), dispatch) - is VerifyClipboard -> { - AddressPayIdMiddleware().handle(action.data, appState()) { - when (it) { - is AddressVerification.SetWalletAddress, is PayIdVerification.SetPayIdWalletAddress -> { - dispatch(ChangePasteBtnEnableState(true)) - } - is AddressVerification.SetError, is PayIdVerification.SetError -> { - dispatch(ChangePasteBtnEnableState(false)) - } - } - } - } + is AddressPayIdActionUi -> AddressPayIdMiddleware().handle(action, appState(), dispatch) is AmountActionUi -> AmountMiddleware().handle(action, appState(), dispatch) is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch) is SendActionUi.SendAmountToRecipient -> verifyAndSendTransaction(appState(), dispatch) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt index cc5fa3430f..c20db4385b 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt @@ -6,6 +6,7 @@ import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerifi import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification import com.tangem.tap.features.send.redux.SendScreenAction import com.tangem.tap.features.send.redux.states.AddressPayIdState +import com.tangem.tap.features.send.redux.states.InputViewValue import com.tangem.tap.features.send.redux.states.SendState /** @@ -20,24 +21,43 @@ class AddressPayIdReducer : SendInternalReducer { private fun handleUiAction(action: AddressPayIdActionUi, sendState: SendState, state: AddressPayIdState): SendState { val result = when (action) { - is AddressPayIdActionUi.ChangeAddressOrPayId -> state + is AddressPayIdActionUi.HandleUserInput -> state is AddressPayIdActionUi.SetTruncateHandler -> state.copy(truncateHandler = action.handler) is AddressPayIdActionUi.TruncateOrRestore -> { - if (action.truncate) state.copy(etFieldValue = state.truncatedFieldValue) - else state.copy(etFieldValue = state.normalFieldValue) + val value = if (action.truncate) state.truncatedFieldValue ?: "" + else state.normalFieldValue ?: "" + state.copy(viewFieldValue = state.viewFieldValue.copy(value = value)) } + is AddressPayIdActionUi.PasteAddressPayId -> return sendState + is AddressPayIdActionUi.CheckClipboard -> return sendState + is AddressPayIdActionUi.CheckAddressPayId -> return sendState } return updateLastState(sendState.copy(addressPayIdState = result), result) } private fun handleAction(action: AddressPayIdVerifyAction, sendState: SendState, state: AddressPayIdState): SendState { val result = when (action) { - is PayIdVerification.SetPayIdWalletAddress -> state.copyPayIdWalletAddress(action.payId, action.payIdWalletAddress) - is PayIdVerification.SetError -> state.copyPayIdError(action.payId, action.error) - is AddressVerification.SetWalletAddress -> state.copyWalletAddress(action.address) - is AddressVerification.SetError -> state.copyError(action.address, action.error) + is PayIdVerification.SetPayIdWalletAddress -> { + state.copy( + viewFieldValue = InputViewValue(action.payId, action.isUserInput), + normalFieldValue = action.payId, + truncatedFieldValue = state.truncate(action.payId), + recipientWalletAddress = action.payIdWalletAddress, + error = null + ) + } + is AddressVerification.SetWalletAddress -> { + state.copy( + viewFieldValue = InputViewValue(action.address, action.isUserInput), + normalFieldValue = action.address, + truncatedFieldValue = state.truncate(action.address), + recipientWalletAddress = action.address, + error = null + ) + } is AddressPayIdVerifyAction.ChangePasteBtnEnableState -> state.copy(pasteIsEnabled = action.isEnabled) - is AddressPayIdVerifyAction.VerifyClipboard -> state + is AddressVerification.SetAddressError -> state.copy(error = action.error, recipientWalletAddress = null) + is PayIdVerification.SetPayIdError -> state.copy(error = action.error, recipientWalletAddress = null) } return updateLastState(sendState.copy(addressPayIdState = result), result) } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt index 8e89987039..db8fe61e47 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt @@ -8,9 +8,9 @@ import com.tangem.tap.features.send.redux.AmountActionUi import com.tangem.tap.features.send.redux.AmountActionUi.SetMainCurrency import com.tangem.tap.features.send.redux.SendScreenAction import com.tangem.tap.features.send.redux.states.AmountState +import com.tangem.tap.features.send.redux.states.InputViewValue import com.tangem.tap.features.send.redux.states.MainCurrencyType import com.tangem.tap.features.send.redux.states.SendState -import com.tangem.tap.features.send.redux.states.ViewAmountValue import java.math.BigDecimal /** @@ -32,7 +32,7 @@ class AmountReducer : SendInternalReducer { else sendState.convertToFiat(state.amountToSendCrypto) val rescaledBalance = sendState.convertToFiat(state.balanceCrypto, true) state.copy( - viewAmountValue = ViewAmountValue(fiatToSend.stripZeroPlainString()), + viewAmountValue = InputViewValue(fiatToSend.stripZeroPlainString()), viewBalanceValue = rescaledBalance.stripZeroPlainString(), mainCurrency = state.createMainCurrency(action.mainCurrency), maxLengthOfAmount = sendState.getDecimals(action.mainCurrency), @@ -41,7 +41,7 @@ class AmountReducer : SendInternalReducer { } MainCurrencyType.CRYPTO -> { state.copy( - viewAmountValue = ViewAmountValue(state.amountToSendCrypto.stripZeroPlainString()), + viewAmountValue = InputViewValue(state.amountToSendCrypto.stripZeroPlainString()), viewBalanceValue = state.balanceCrypto.stripZeroPlainString(), mainCurrency = state.createMainCurrency(action.mainCurrency), maxLengthOfAmount = sendState.getDecimals(action.mainCurrency), @@ -63,7 +63,7 @@ class AmountReducer : SendInternalReducer { MainCurrencyType.FIAT -> { val amountCrypto = sendState.convertToCrypto(action.amount) state.copy( - viewAmountValue = ViewAmountValue(action.amount.scaleToFiat(false).stripZeroPlainString(), action.isUserInput), + viewAmountValue = InputViewValue(action.amount.scaleToFiat(false).stripZeroPlainString(), action.isUserInput), amountToSendCrypto = amountCrypto, cursorAtTheSamePosition = true, error = null @@ -71,7 +71,7 @@ class AmountReducer : SendInternalReducer { } MainCurrencyType.CRYPTO -> { state.copy( - viewAmountValue = ViewAmountValue(action.amount.stripZeroPlainString(), action.isUserInput), + viewAmountValue = InputViewValue(action.amount.stripZeroPlainString(), action.isUserInput), amountToSendCrypto = action.amount, cursorAtTheSamePosition = true, error = null diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt index 6c453d6ae5..dfcc6ca4c5 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt @@ -3,7 +3,7 @@ package com.tangem.tap.features.send.redux.states import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction data class AddressPayIdState( - val etFieldValue: String? = null, + val viewFieldValue: InputViewValue = InputViewValue(""), val normalFieldValue: String? = null, val truncatedFieldValue: String? = null, val recipientWalletAddress: String? = null, @@ -14,51 +14,9 @@ data class AddressPayIdState( override val stateId: StateId = StateId.ADDRESS_PAY_ID + fun truncate(value: String): String = truncateHandler?.invoke(value) ?: value + fun isReady(): Boolean = error == null && recipientWalletAddress?.isNotEmpty() ?: false fun isPayIdState(): Boolean = recipientWalletAddress != null && recipientWalletAddress != normalFieldValue - - fun copyWalletAddress(address: String): AddressPayIdState { - val truncated = truncateHandler?.invoke(address) ?: address - return this.copy( - etFieldValue = address, - normalFieldValue = address, - truncatedFieldValue = truncated, - recipientWalletAddress = address, - error = null - ) - } - - fun copyError(address: String, error: AddressPayIdVerifyAction.Error): AddressPayIdState { - val truncated = truncateHandler?.invoke(address) ?: address - return this.copy( - etFieldValue = address, - normalFieldValue = address, - truncatedFieldValue = truncated, - error = error, - recipientWalletAddress = null - ) - } - - fun copyPayIdWalletAddress(payId: String, address: String): AddressPayIdState { - val truncated = truncateHandler?.invoke(payId) ?: address - return this.copy( - etFieldValue = payId, - normalFieldValue = payId, - truncatedFieldValue = truncated, - recipientWalletAddress = address, - error = null - ) - } - - fun copyPayIdError(payId: String, error: AddressPayIdVerifyAction.Error): AddressPayIdState { - val truncated = truncateHandler?.invoke(payId) ?: payId - return this.copy( - etFieldValue = payId, - normalFieldValue = payId, - truncatedFieldValue = truncated, - error = error, - recipientWalletAddress = null - ) - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index 7dd7d3b242..5742c5c60d 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -81,7 +81,7 @@ enum class SendButtonState { data class AmountState( val walletAmount: Amount? = null, val typeOfAmount: AmountType = AmountType.Coin, - val viewAmountValue: ViewAmountValue = ViewAmountValue(BigDecimal.ZERO.toPlainString()), + val viewAmountValue: InputViewValue = InputViewValue(BigDecimal.ZERO.toPlainString()), val viewBalanceValue: String = BigDecimal.ZERO.toPlainString(), val mainCurrency: MainCurrency = MainCurrency(MainCurrencyType.FIAT, TapCurrency.DEFAULT_FIAT_CURRENCY), val amountToSendCrypto: BigDecimal = BigDecimal.ZERO, @@ -105,7 +105,7 @@ data class AmountState( } } -data class ViewAmountValue(val value: String, val isUserInput: Boolean = false) +data class InputViewValue(val value: String, val isFromUserInput: Boolean = false) enum class MainCurrencyType { FIAT, CRYPTO } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index e731a059f6..de7856d3e6 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -75,22 +75,22 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { private fun setupAddressOrPayIdLayout() { store.dispatch(SetTruncateHandler { etAddressOrPayId.truncateMiddleWith(it, "...") }) - store.dispatch(AddressPayIdVerifyAction.VerifyClipboard(requireContext().getFromClipboard()?.toString())) + store.dispatch(CheckClipboard(requireContext().getFromClipboard()?.toString())) etAddressOrPayId.setOnFocusChangeListener { v, hasFocus -> store.dispatch(TruncateOrRestore(!hasFocus)) } etAddressOrPayId.inputtedTextAsFlow() .debounce(400) - .filter { store.state.sendState.addressPayIdState.etFieldValue != it } + .filter { store.state.sendState.addressPayIdState.viewFieldValue.value != it } .onEach { - store.dispatch(ChangeAddressOrPayId(it)) + store.dispatch(AddressPayIdActionUi.HandleUserInput(it)) store.dispatch(FeeAction.RequestFee) } .launchIn(mainScope) imvPaste.setOnClickListener { - store.dispatch(ChangeAddressOrPayId(requireContext().getFromClipboard()?.toString() ?: "")) + store.dispatch(PasteAddressPayId(requireContext().getFromClipboard()?.toString() ?: "")) store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused)) store.dispatch(FeeAction.RequestFee) } @@ -108,7 +108,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { val scannedCode = data?.getStringExtra(ScanQrCodeActivity.SCAN_RESULT) ?: "" if (scannedCode.isEmpty()) return - store.dispatch(ChangeAddressOrPayId(scannedCode)) + store.dispatch(PasteAddressPayId(scannedCode)) store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused)) store.dispatch(FeeAction.RequestFee) } @@ -167,7 +167,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { etAmountToSend.inputtedTextAsFlow() .debounce(400) .filter { store.state.sendState.amountState.viewAmountValue.value != it && it.isNotEmpty() } - .onEach { store.dispatch(HandleUserInput(it)) } + .onEach { store.dispatch(AmountActionUi.HandleUserInput(it)) } .launchIn(mainScope) etAmountToSend.setOnImeActionListener(EditorInfo.IME_ACTION_DONE) { diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 3f53f1966e..278b8d62b4 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -67,7 +67,6 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber private fun handleAddressPayIdState(fg: BaseStoreFragment, state: AddressPayIdState) { fun parseError(context: Context, error: Error?): String? { val resId = when (error) { - Error.IS_NOT_PAY_ID -> R.string.error_payid_verification_failed Error.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.error_payid_unsupported_by_blockchain Error.PAY_ID_NOT_REGISTERED -> R.string.error_payid_not_registere Error.PAY_ID_REQUEST_FAILED -> R.string.error_payid_request_failed @@ -90,10 +89,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber til.helperText = state.recipientWalletAddress til.isHelperTextEnabled = state.isPayIdState() && parsedError == null - // prevent cycling - if (state.etFieldValue == null) return - - et.update(state.etFieldValue) + if (!state.viewFieldValue.isFromUserInput) et.update(state.viewFieldValue.value) } private fun handleAmountState(fg: BaseStoreFragment, state: AmountState) { @@ -116,7 +112,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber fg.etAmountToSend.filters = arrayOf(DecimalDigitsInputFilter(12, state.maxLengthOfAmount)) val amountToSend = state.viewAmountValue - if (!amountToSend.isUserInput) fg.etAmountToSend.update(amountToSend.value) + if (!amountToSend.isFromUserInput) fg.etAmountToSend.update(amountToSend.value) // fg.tvAmountToSendShadow.text = amountToSend // if (amountToSend.length > 10) { From 592f25f69a5fd813677426b99423f780450cba67 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 20 Sep 2020 21:12:44 +0300 Subject: [PATCH 06/10] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 99243416f0..0def014c91 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,7 +7,6 @@ android:name="android.hardware.nfc" android:required="true" /> - From 8522ddfe85bbc41de9dd5ab20b7a68118a84aaa4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 20 Sep 2020 22:32:39 +0300 Subject: [PATCH 07/10] Updated on 2026-08-14 --- .../com/tangem/tap/features/details/redux/DetailsReducer.kt | 2 +- .../java/com/tangem/tap/features/wallet/redux/WalletReducer.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index ba2a2b0387..9cc2fbb831 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -41,7 +41,7 @@ private fun handleEraseWallet(action: DetailsAction.EraseWallet, state: DetailsS return when (action) { DetailsAction.EraseWallet.Check -> { val notAllowedByCard = state.card?.settingsMask?.contains(Settings.ProhibitPurgeWallet) == true - val notEmpty = state.wallet?.transactions?.isNullOrEmpty() != true || + val notEmpty = state.wallet?.recentTransactions?.isNullOrEmpty() != true || state.wallet.amounts.toList().unzip().second.map { it.value?.isZero() }.contains(false) val eraseWalletState = when { notAllowedByCard -> EraseWalletState.NotAllowedByCard diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt index 233a6c5500..c348a46df0 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletReducer.kt @@ -104,7 +104,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { val fiatRate = state.globalState.conversionRates.getRate(action.wallet.blockchain.currency) val fiatAmount = fiatRate?.let { amount?.toFiatString(it, fiatCurrencySymbol) } - val pendingTransactions = action.wallet.transactions + val pendingTransactions = action.wallet.recentTransactions .toPendingTransactions(action.wallet.address) val sendButtonEnabled = amount?.isZero() == false || token?.value?.isZero() == false From b047221916ebf0f5fee36d829291b3defc1b8e95 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 20 Sep 2020 23:25:25 +0300 Subject: [PATCH 08/10] Updated on 2026-08-14 --- .../send/redux/middlewares/AmountMiddleware.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt index 769fb170d4..55817d9003 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt @@ -3,7 +3,6 @@ package com.tangem.tap.features.send.redux.middlewares import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.TransactionError import com.tangem.common.extensions.isZero -import com.tangem.tap.common.extensions.isNegative import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.TapError import com.tangem.tap.features.send.redux.AmountAction @@ -49,7 +48,11 @@ class AmountMiddleware { val walletManager = sendState.walletManager ?: return val inputCrypto = sendState.amountState.amountToSendCrypto - if (sendState.amountState.viewAmountValue.value == "0" && inputCrypto.isZero()) return + if (sendState.amountState.viewAmountValue.value == "0" && inputCrypto.isZero()) { + dispatch(ReceiptAction.RefreshReceipt) + dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) + return + } val feeCrypto = sendState.feeState.getCurrentFee() @@ -87,8 +90,9 @@ class AmountMiddleware { amountState.balanceCrypto } else { val balanceExtractFee = amountState.balanceCrypto.minus(sendState.feeState.getCurrentFee()) - if (balanceExtractFee.isNegative()) BigDecimal.ZERO - else balanceExtractFee +// if (balanceExtractFee.isNegative()) BigDecimal.ZERO +// else balanceExtractFee + balanceExtractFee } val currentCurrencyValue = if (amountState.mainCurrency.type == MainCurrencyType.CRYPTO) maxAmount From a2c2cb52ca9d383f2c547b1749e19d46103dd22a Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 20 Sep 2020 23:41:08 +0300 Subject: [PATCH 09/10] Updated on 2026-08-14 --- .../redux/middlewares/AmountMiddleware.kt | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt index 55817d9003..3c001122bd 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.send.redux.middlewares import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.TransactionError import com.tangem.common.extensions.isZero +import com.tangem.tap.common.extensions.isNegative import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.TapError import com.tangem.tap.features.send.redux.AmountAction @@ -86,19 +87,17 @@ class AmountMiddleware { val sendState = appState?.sendState ?: return val amountState = sendState.amountState - val maxAmount = if (sendState.feeState.feeIsIncluded) { - amountState.balanceCrypto + val maxAmount = if (sendState.feeState.feeIsIncluded) amountState.balanceCrypto + else amountState.balanceCrypto.minus(sendState.feeState.getCurrentFee()) + + if (maxAmount.isNegative()) { + dispatch(AmountAction.SetAmountError(TapError.FeeExceedsBalance)) } else { - val balanceExtractFee = amountState.balanceCrypto.minus(sendState.feeState.getCurrentFee()) -// if (balanceExtractFee.isNegative()) BigDecimal.ZERO -// else balanceExtractFee - balanceExtractFee + val currentCurrencyValue = if (amountState.mainCurrency.type == MainCurrencyType.CRYPTO) maxAmount + else sendState.convertToFiat(maxAmount) + + dispatch(AmountAction.SetAmount(currentCurrencyValue, false)) } - - val currentCurrencyValue = if (amountState.mainCurrency.type == MainCurrencyType.CRYPTO) maxAmount - else sendState.convertToFiat(maxAmount) - - dispatch(AmountAction.SetAmount(currentCurrencyValue, false)) } private fun toggleMainCurrency(appState: AppState?, dispatch: (Action) -> Unit) { From 829a2d97969a048f16e7d084819a948e0ad2f46c Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 20 Sep 2020 23:47:16 +0300 Subject: [PATCH 10/10] Updated on 2026-08-14 --- .../tap/features/send/redux/middlewares/RequestFeeMiddleware.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt index c78c05f21f..5ba0bc7bc2 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt @@ -28,7 +28,7 @@ class RequestFeeMiddleware { if (!sendState.addressPayIdIsReady()) { dispatch(FeeAction.FeeCalculation.SetFeeError(FeeAction.Error.ADDRESS_OR_AMOUNT_IS_EMPTY)) - dispatch(FeeAction.ChangeLayoutVisibility(main = false, controls = true, chipGroup = true)) + dispatch(FeeAction.ChangeLayoutVisibility(main = false, chipGroup = true)) dispatch(ReceiptAction.RefreshReceipt) dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) return