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 bd93f890d5..a97d84ef97 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 @@ -66,7 +66,7 @@ sealed class AmountActionUi : SendScreenActionUi { } sealed class AmountAction : SendScreenAction { - data class SetAmount(val amount: BigDecimal, val isUserInput: Boolean) : AmountAction() + data class SetAmount(val amountCrypto: BigDecimal, val isUserInput: Boolean) : AmountAction() data class SetAmountError(val error: TapError?) : AmountAction() } 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 f9e70c740f..c2bd4f286c 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 @@ -129,8 +129,7 @@ internal class AddressPayIdMiddleware { private fun extractAddressFromShareUri(shareUri: String): String { val sharePrefix = listOf("bitcoin:", "ethereum:", "ripple:") val prefixes = sharePrefix.filter { shareUri.contains(it) } - return if (prefixes.isEmpty()) shareUri - else shareUri.replace(prefixes[0], "") + return if (prefixes.isEmpty()) shareUri else shareUri.replace(prefixes[0], "") } private fun verifyClipboard(input: String?, appState: AppState?, dispatch: DispatchFunction) { 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 3c001122bd..c54d5f2dd1 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 @@ -31,22 +30,29 @@ class AmountMiddleware { private fun handleUserInput(data: String, appState: AppState?, dispatch: (Action) -> Unit) { val sendState = appState?.sendState ?: return + val amountState = sendState.amountState val data = if (data == ".") "0.0" else data val inputValue = when { data.isEmpty() || data == "0" -> BigDecimal.ZERO else -> BigDecimal(data) } + if (inputValue.isZero() && amountState.amountToSendCrypto.isZero()) return - if (inputValue.isZero() && sendState.amountState.amountToSendCrypto.isZero()) return + val inputValueCrypto = if (amountState.mainCurrency.type == MainCurrencyType.CRYPTO) { + inputValue + } else { + sendState.convertFiatToExtractCrypto(inputValue) + } - dispatch(AmountAction.SetAmount(inputValue, true)) + dispatch(AmountAction.SetAmount(inputValueCrypto, true)) dispatch(AmountActionUi.CheckAmountToSend) } private fun checkAmountToSend(appState: AppState?, dispatch: (Action) -> Unit) { val sendState = appState?.sendState ?: return val walletManager = sendState.walletManager ?: return + val typedAmount = sendState.amountState.amountToExtract ?: return val inputCrypto = sendState.amountState.amountToSendCrypto if (sendState.amountState.viewAmountValue.value == "0" && inputCrypto.isZero()) { @@ -55,12 +61,8 @@ class AmountMiddleware { return } - val feeCrypto = sendState.feeState.getCurrentFee() - - val feeAmount = Amount(feeCrypto, walletManager.wallet.blockchain) - val totalAmount = Amount(sendState.getTotalAmountToSend(inputCrypto), walletManager.wallet.blockchain) - - val transactionErrors = walletManager.validateTransaction(totalAmount, feeAmount) + val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend(inputCrypto)) + val transactionErrors = walletManager.validateTransaction(amountToSend, sendState.feeState.currentFee) if (transactionErrors.isEmpty()) { dispatch(AmountAction.SetAmountError(null)) } else { @@ -84,27 +86,20 @@ class AmountMiddleware { } private fun setMaxAmount(appState: AppState?, dispatch: (Action) -> Unit) { - val sendState = appState?.sendState ?: return - val amountState = sendState.amountState + val amountState = appState?.sendState?.amountState ?: return - 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 currentCurrencyValue = if (amountState.mainCurrency.type == MainCurrencyType.CRYPTO) maxAmount - else sendState.convertToFiat(maxAmount) - - dispatch(AmountAction.SetAmount(currentCurrencyValue, false)) - } + dispatch(AmountAction.SetAmount(amountState.balanceCrypto, false)) } private fun toggleMainCurrency(appState: AppState?, dispatch: (Action) -> Unit) { val amountState = appState?.sendState?.amountState ?: return + if (!appState.sendState.coinIsConvertible()) return - val type = if (amountState.mainCurrency.type == MainCurrencyType.FIAT) MainCurrencyType.CRYPTO - else MainCurrencyType.FIAT + val type = if (amountState.mainCurrency.type == MainCurrencyType.FIAT) { + MainCurrencyType.CRYPTO + } else { + MainCurrencyType.FIAT + } dispatch(AmountActionUi.SetMainCurrency(type)) } 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 cdf07f2690..ed70149d38 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 @@ -20,6 +20,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.rekotlin.Action import org.rekotlin.Middleware +import timber.log.Timber /** [REDACTED_AUTHOR] @@ -41,14 +42,11 @@ val sendMiddleware: Middleware = { dispatch, appState -> private fun verifyAndSendTransaction(appState: AppState?, dispatch: (Action) -> Unit) { val sendState = appState?.sendState ?: return val walletManager = appState.globalState.scanNoteResponse?.walletManager ?: return + val recipientAddress = sendState.addressPayIdState.recipientWalletAddress ?: return + val typedAmount = sendState.amountState.amountToExtract ?: return + val feeAmount = sendState.feeState.currentFee ?: return - val blockchain = walletManager.wallet.blockchain - val recipientAddress = sendState.addressPayIdState.recipientWalletAddress!! - - val feeAmount = Amount(sendState.feeState.getCurrentFee(), blockchain) - val amountToSend = Amount(sendState.getTotalAmountToSend(), blockchain, recipientAddress) - - val txSender = walletManager as TransactionSender + val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend()) val verifyResult = walletManager.validateTransaction(amountToSend, feeAmount) if (verifyResult.isNotEmpty()) { @@ -59,7 +57,8 @@ private fun verifyAndSendTransaction(appState: AppState?, dispatch: (Action) -> dispatch(SendAction.ChangeSendButtonState(SendButtonState.PROGRESS)) val txData = walletManager.createTransaction(amountToSend, feeAmount, recipientAddress) scope.launch { - val result = txSender.send(txData, Signer(tangemSdk)) + walletManager.update() + val result = (walletManager as TransactionSender).send(txData, Signer(tangemSdk)) withContext(Dispatchers.Main) { when (result) { is SimpleResult.Success -> { @@ -78,6 +77,7 @@ private fun verifyAndSendTransaction(appState: AppState?, dispatch: (Action) -> // user was cancelled the operation by closing the Sdk bottom sheet } else -> { + Timber.e(result.error) dispatch(SendAction.SendError(TapError.BlockchainInternalError)) } } 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 c20db4385b..e7c726241f 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 @@ -24,8 +24,7 @@ class AddressPayIdReducer : SendInternalReducer { is AddressPayIdActionUi.HandleUserInput -> state is AddressPayIdActionUi.SetTruncateHandler -> state.copy(truncateHandler = action.handler) is AddressPayIdActionUi.TruncateOrRestore -> { - val value = if (action.truncate) state.truncatedFieldValue ?: "" - else 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 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 db8fe61e47..e8e9a7eb43 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.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 @@ -26,16 +25,21 @@ class AmountReducer : SendInternalReducer { private fun handleUiAction(action: AmountActionUi, sendState: SendState, state: AmountState): SendState { val result = when (action) { is SetMainCurrency -> { - when (action.mainCurrency) { + val currencyCanBeSwitched = sendState.mainCurrencyCanBeSwitched() + + when (val currency = if (currencyCanBeSwitched) action.mainCurrency else MainCurrencyType.CRYPTO) { MainCurrencyType.FIAT -> { - val fiatToSend = if (state.amountToSendCrypto.isZero()) BigDecimal.ZERO - else sendState.convertToFiat(state.amountToSendCrypto) - val rescaledBalance = sendState.convertToFiat(state.balanceCrypto, true) + val fiatToSend = if (state.amountToSendCrypto.isZero()) { + BigDecimal.ZERO + } else { + sendState.convertExtractCryptoToFiat(state.amountToSendCrypto) + } + val rescaledBalance = sendState.convertExtractCryptoToFiat(state.balanceCrypto, true) state.copy( viewAmountValue = InputViewValue(fiatToSend.stripZeroPlainString()), viewBalanceValue = rescaledBalance.stripZeroPlainString(), - mainCurrency = state.createMainCurrency(action.mainCurrency), - maxLengthOfAmount = sendState.getDecimals(action.mainCurrency), + mainCurrency = state.createMainCurrency(currency, currencyCanBeSwitched), + maxLengthOfAmount = sendState.getDecimals(currency), cursorAtTheSamePosition = false ) } @@ -43,8 +47,8 @@ class AmountReducer : SendInternalReducer { state.copy( viewAmountValue = InputViewValue(state.amountToSendCrypto.stripZeroPlainString()), viewBalanceValue = state.balanceCrypto.stripZeroPlainString(), - mainCurrency = state.createMainCurrency(action.mainCurrency), - maxLengthOfAmount = sendState.getDecimals(action.mainCurrency), + mainCurrency = state.createMainCurrency(currency, currencyCanBeSwitched), + maxLengthOfAmount = sendState.getDecimals(currency), cursorAtTheSamePosition = false ) } @@ -59,25 +63,17 @@ class AmountReducer : SendInternalReducer { private fun handleAction(action: AmountAction, sendState: SendState, state: AmountState): SendState { val result = when (action) { is AmountAction.SetAmount -> { - when (state.mainCurrency.type) { - MainCurrencyType.FIAT -> { - val amountCrypto = sendState.convertToCrypto(action.amount) - state.copy( - viewAmountValue = InputViewValue(action.amount.scaleToFiat(false).stripZeroPlainString(), action.isUserInput), - amountToSendCrypto = amountCrypto, - cursorAtTheSamePosition = true, - error = null - ) - } - MainCurrencyType.CRYPTO -> { - state.copy( - viewAmountValue = InputViewValue(action.amount.stripZeroPlainString(), action.isUserInput), - amountToSendCrypto = action.amount, - cursorAtTheSamePosition = true, - error = null - ) - } + val amount = if (state.mainCurrency.type == MainCurrencyType.CRYPTO) { + action.amountCrypto + } else { + sendState.convertExtractCryptoToFiat(action.amountCrypto, true) } + state.copy( + viewAmountValue = InputViewValue(amount.stripZeroPlainString(), action.isUserInput), + amountToSendCrypto = action.amountCrypto, + cursorAtTheSamePosition = true, + error = null + ) } is AmountAction.SetAmountError -> { state.copy(error = action.error) 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 0d92ab9a57..c6f6511773 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 @@ -2,95 +2,91 @@ 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 import com.tangem.tap.features.send.redux.states.* import com.tangem.tap.store +import java.math.BigDecimal /** [REDACTED_AUTHOR] */ class ReceiptReducer : SendInternalReducer { + + companion object { + const val EMPTY = "-" + } + + private lateinit var sendState: SendState + private lateinit var amountState: AmountState + private lateinit var feeState: FeeState + override fun handle(action: SendScreenAction, sendState: SendState): SendState { + this.sendState = sendState + this.amountState = sendState.amountState + this.feeState = sendState.feeState + return when (action) { - is RefreshReceipt -> handleRefresh(action, sendState, sendState.receiptState) + is RefreshReceipt -> handleRefresh(action, sendState.receiptState) else -> sendState } } - private fun handleRefresh(action: RefreshReceipt, sendState: SendState, state: ReceiptState): SendState { + private fun handleRefresh(action: RefreshReceipt, state: ReceiptState): SendState { val wallet = sendState.walletManager?.wallet ?: return sendState - val coinConverter = sendState.coinConverter - val tokenConverter = sendState.tokenConverter - val amountState = sendState.amountState - val feeState = sendState.feeState - - val layoutType = determineLayoutType(amountState.mainCurrency.type, sendState.amountState.typeOfAmount) + val layoutType = determineLayoutType(amountState.mainCurrency.type, amountState.typeOfAmount) val symbols = determineSymbols(wallet) val showBlank = !sendState.isReadyToSend() val result = state.copy( visibleTypeOfReceipt = layoutType, - 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), - tokenCrypto = createTokenCryptoType(coinConverter, tokenConverter, amountState, feeState, symbols, showBlank) + mainCurrency = amountState.mainCurrency, + fiat = createFiatType(symbols, showBlank), + crypto = createCryptoType(symbols, showBlank), + tokenFiat = createTokenFiatType(symbols, showBlank), + tokenCrypto = createTokenCryptoType(symbols, showBlank) ) return updateLastState(sendState.copy(receiptState = result), result) } - private fun createFiatType( - converter: CurrencyConverter, - amountState: AmountState, - feeState: FeeState, - symbols: ReceiptSymbols, - showBlank: Boolean - ): ReceiptFiat { - val feeCrypto = feeState.getCurrentFee() - val feeFiat = converter.toFiatWithPrecision(feeCrypto) + private fun createFiatType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptFiat { + val feeCrypto = feeState.getCurrentFeeValue() + val feeFiat = convertToFiatPrecision(feeCrypto) if (showBlank) { - return ReceiptFiat("0", feeFiat.stripZeroPlainString(), "0", "0", symbols) + return ReceiptFiat("0", feeFiat, "0", "0", symbols) } return if (feeState.feeIsIncluded) { - val amountFiat = converter.toFiatWithPrecision(amountState.amountToSendCrypto.minus(feeCrypto)) - val totalFiat = converter.toFiatWithPrecision(amountState.amountToSendCrypto) + val amountFiat = convertToFiatPrecision(amountState.amountToSendCrypto.minus(feeCrypto)) + val totalFiat = convertToFiatPrecision(amountState.amountToSendCrypto) ReceiptFiat( - amountFiat = amountFiat.stripZeroPlainString(), - feeFiat = feeFiat.stripZeroPlainString(), - totalFiat = totalFiat.stripZeroPlainString(), + amountFiat = amountFiat, + feeFiat = feeFiat, + totalFiat = totalFiat, willSentCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), symbols = symbols ) } else { val totalAmountCrypto = amountState.amountToSendCrypto.plus(feeCrypto) - val amountFiat = converter.toFiatWithPrecision(amountState.amountToSendCrypto) - val totalFiat = converter.toFiatWithPrecision(totalAmountCrypto) + val amountFiat = convertToFiatPrecision(amountState.amountToSendCrypto) + val totalFiat = convertToFiatPrecision(totalAmountCrypto) ReceiptFiat( - amountFiat = amountFiat.stripZeroPlainString(), - feeFiat = feeFiat.stripZeroPlainString(), - totalFiat = totalFiat.stripZeroPlainString(), + amountFiat = amountFiat, + feeFiat = feeFiat, + totalFiat = totalFiat, willSentCrypto = totalAmountCrypto.stripZeroPlainString(), symbols = symbols ) } } - private fun createCryptoType( - converter: CurrencyConverter, - amountState: AmountState, - feeState: FeeState, - symbols: ReceiptSymbols, - showBlank: Boolean - ): ReceiptCrypto { - val feeCrypto = feeState.getCurrentFee() - val feeFiat = converter.toFiatWithPrecision(feeCrypto) + private fun createCryptoType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptCrypto { + val feeCrypto = feeState.getCurrentFeeValue() + val feeFiat = convertToFiatPrecision(feeCrypto) if (showBlank) { return ReceiptCrypto("0", feeCrypto.stripZeroPlainString(), "0", "0", "0", symbols) } @@ -100,8 +96,8 @@ class ReceiptReducer : SendInternalReducer { amountCrypto = amountState.amountToSendCrypto.minus(feeCrypto).stripZeroPlainString(), feeCrypto = feeCrypto.stripZeroPlainString(), totalCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), - feeFiat = feeFiat.stripZeroPlainString(), - willSentFiat = converter.toFiatWithPrecision(amountState.amountToSendCrypto).stripZeroPlainString(), + feeFiat = feeFiat, + willSentFiat = convertToFiatPrecision(amountState.amountToSendCrypto), symbols = symbols ) } else { @@ -110,65 +106,73 @@ class ReceiptReducer : SendInternalReducer { amountCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), feeCrypto = feeCrypto.stripZeroPlainString(), totalCrypto = totalCrypto.stripZeroPlainString(), - feeFiat = feeFiat.stripZeroPlainString(), - willSentFiat = converter.toFiatWithPrecision(totalCrypto).stripZeroPlainString(), + feeFiat = feeFiat, + willSentFiat = convertToFiatPrecision(totalCrypto), symbols = symbols ) } } - private fun createTokenFiatType( - coinConverter: CurrencyConverter, - tokenConverter: CurrencyConverter, - amountState: AmountState, - feeState: FeeState, - symbols: ReceiptSymbols, - showBlank: Boolean - ): ReceiptTokenFiat { - val feeCoin = feeState.getCurrentFee() - val feeFiat = coinConverter.toFiatWithPrecision(feeCoin) + private fun createTokenFiatType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptTokenFiat { + val feeCoin = feeState.getCurrentFeeValue() if (showBlank) { - return ReceiptTokenFiat("0", feeFiat.stripZeroPlainString(), "0", "0", "0", symbols) + val feeFiat = convertToFiatPrecision(feeCoin) + return ReceiptTokenFiat("0", feeFiat, "0", "0", "0", symbols) } val tokensToSend = amountState.amountToSendCrypto - val amountFiat = tokenConverter.toFiatUnscaled(tokensToSend) - val totalFiat = amountFiat.plus(feeFiat) - return ReceiptTokenFiat( - amountFiat = amountFiat.scaleToFiat().stripZeroPlainString(), - feeFiat = feeFiat.stripZeroPlainString(), - totalFiat = totalFiat.scaleToFiat().stripZeroPlainString(), - willSentToken = tokensToSend.stripZeroPlainString(), - willSentFeeCoin = feeCoin.stripZeroPlainString(), - symbols = symbols - ) + return if (sendState.coinIsConvertible() && sendState.tokenIsConvertible()) { + val feeFiat = sendState.coinConverter!!.toFiatUnscaled(feeCoin) + val amountFiat = sendState.tokenConverter!!.toFiatUnscaled(tokensToSend) + val totalFiat = amountFiat.plus(feeFiat) + ReceiptTokenFiat( + amountFiat = amountFiat.scaleToFiat(true).stripZeroPlainString(), + feeFiat = feeFiat.scaleToFiat(true).stripZeroPlainString(), + totalFiat = totalFiat.scaleToFiat(true).stripZeroPlainString(), + willSentToken = tokensToSend.stripZeroPlainString(), + willSentFeeCoin = feeCoin.stripZeroPlainString(), + symbols = symbols + ) + } else { + ReceiptTokenFiat( + amountFiat = EMPTY, + feeFiat = EMPTY, + totalFiat = EMPTY, + willSentToken = tokensToSend.stripZeroPlainString(), + willSentFeeCoin = feeCoin.stripZeroPlainString(), + symbols = symbols + ) + } } - private fun createTokenCryptoType( - coinConverter: CurrencyConverter, - tokenConverter: CurrencyConverter, - amountState: AmountState, - feeState: FeeState, - symbols: ReceiptSymbols, - showBlank: Boolean - ): ReceiptTokenCrypto { - val feeCoin = feeState.getCurrentFee() + private fun createTokenCryptoType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptTokenCrypto { + val feeCoin = feeState.getCurrentFeeValue() if (showBlank) { 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 = tokensToSend.stripZeroPlainString(), - feeCoin = feeCoin.stripZeroPlainString(), - totalFiat = totalFiat.scaleToFiat().stripZeroPlainString(), - symbols = symbols - ) + return if (sendState.coinIsConvertible() && sendState.tokenIsConvertible()) { + val tokenFiat = sendState.tokenConverter!!.toFiatUnscaled(tokensToSend) + val feeFiat = sendState.coinConverter!!.toFiatUnscaled(feeCoin) + val totalFiat = tokenFiat.plus(feeFiat) + + ReceiptTokenCrypto( + amountToken = tokensToSend.stripZeroPlainString(), + feeCoin = feeCoin.stripZeroPlainString(), + totalFiat = totalFiat.scaleToFiat(true).stripZeroPlainString(), + symbols = symbols + ) + } else { + ReceiptTokenCrypto( + amountToken = tokensToSend.stripZeroPlainString(), + feeCoin = feeCoin.stripZeroPlainString(), + totalFiat = EMPTY, + symbols = symbols + ) + } + } private fun determineSymbols(wallet: Wallet): ReceiptSymbols { @@ -193,4 +197,12 @@ class ReceiptReducer : SendInternalReducer { } } } + + private fun convertToFiatPrecision(value: BigDecimal, isToken: Boolean = false): String { + return when { + !isToken && sendState.coinIsConvertible() -> sendState.coinConverter!!.toFiatWithPrecision(value).stripZeroPlainString() + isToken && sendState.tokenIsConvertible() -> sendState.tokenConverter!!.toFiatWithPrecision(value).stripZeroPlainString() + else -> EMPTY + } + } } \ No newline at end of file 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 8a18db9d5e..d09cb9b695 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 @@ -56,25 +56,22 @@ private class PrepareSendScreenStatesReducer : SendInternalReducer { override fun handle(action: SendScreenAction, sendState: SendState): SendState { val prepareAction = action as PrepareSendScreen val walletManager = store.state.globalState.scanNoteResponse!!.walletManager!! - val walletAmount = prepareAction.tokenAmount ?: prepareAction.coinAmount!! - val decimals = walletAmount.decimals + val amountToExtract = prepareAction.tokenAmount ?: prepareAction.coinAmount!! + val decimals = amountToExtract.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( walletManager = walletManager, coinConverter = coinConverter, - tokenConverter = tokenConverter ?: CurrencyConverter(BigDecimal.ONE, decimals), + tokenConverter = tokenConverter, amountState = sendState.amountState.copy( - walletAmount = walletAmount, - typeOfAmount = walletAmount.type, - balanceCrypto = walletAmount.value ?: BigDecimal.ZERO + amountToExtract = amountToExtract, + typeOfAmount = amountToExtract.type, + balanceCrypto = amountToExtract.value ?: BigDecimal.ZERO ), feeState = sendState.feeState.copy( - includeFeeSwitcherIsEnabled = walletAmount.type == AmountType.Coin + includeFeeSwitcherIsEnabled = amountToExtract.type == AmountType.Coin ) ) } 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 72575e6a6b..532031bbb4 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 @@ -27,5 +27,5 @@ data class FeeState( fun isReady(): Boolean = error == null && currentFee != null - fun getCurrentFee(): BigDecimal = currentFee?.value ?: BigDecimal.ZERO + fun getCurrentFeeValue(): 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/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index 5742c5c60d..0d8851abb2 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 @@ -27,15 +27,14 @@ interface SendScreenState : StateType, IdStateHolder data class SendState( val walletManager: WalletManager? = null, - val coinConverter: CurrencyConverter = CurrencyConverter(BigDecimal.ONE, 2), - val tokenConverter: CurrencyConverter = CurrencyConverter(BigDecimal.ONE, 2), + val coinConverter: CurrencyConverter? = null, + val tokenConverter: CurrencyConverter? = null, 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, - val hasInitializationError: Boolean = false + val sendButtonState: SendButtonState = SendButtonState.DISABLED ) : SendScreenState { override val stateId: StateId = StateId.SEND_SCREEN @@ -49,28 +48,61 @@ data class SendState( fun getDecimals(type: MainCurrencyType): Int = when (type) { MainCurrencyType.FIAT -> 2 - MainCurrencyType.CRYPTO -> amountState.walletAmount?.decimals ?: 0 + MainCurrencyType.CRYPTO -> amountState.amountToExtract?.decimals ?: 0 } - fun getConverter(): CurrencyConverter { - return if (amountState.typeOfAmount == AmountType.Coin) coinConverter - else tokenConverter + fun convertFiatToCoin(value: BigDecimal): BigDecimal { + return if (!this.coinIsConvertible()) value else coinConverter!!.toCrypto(value) } - fun convertToCrypto(value: BigDecimal): BigDecimal { - return getConverter().toCrypto(value) + fun convertFiatToToken(value: BigDecimal): BigDecimal { + return if (!this.tokenIsConvertible()) value else tokenConverter!!.toCrypto(value) } - fun convertToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { - val converter = getConverter() + fun convertCoinToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { + if (!this.coinIsConvertible()) return value + + val converter = coinConverter!! return if (!scaleWithPrecision) converter.toFiat(value) else converter.toFiatWithPrecision(value) } + fun convertTokenToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { + if (!this.tokenIsConvertible()) return value + + val converter = tokenConverter!! + return if (!scaleWithPrecision) converter.toFiat(value) else converter.toFiatWithPrecision(value) + } + + fun convertFiatToExtractCrypto(fiatValue: BigDecimal): BigDecimal = when (amountState.typeOfAmount) { + AmountType.Coin -> convertFiatToCoin(fiatValue) + AmountType.Token -> convertFiatToToken(fiatValue) + AmountType.Reserve -> fiatValue + } + + fun convertExtractCryptoToFiat(cryptoValue: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { + return when (amountState.typeOfAmount) { + AmountType.Coin -> convertCoinToFiat(cryptoValue, scaleWithPrecision) + AmountType.Token -> convertTokenToFiat(cryptoValue, scaleWithPrecision) + AmountType.Reserve -> cryptoValue + } + } + 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 + return if (needToExtractFee) value.minus(feeState.getCurrentFeeValue()) else value + } + + fun coinIsConvertible(): Boolean = coinConverter != null + fun tokenIsConvertible(): Boolean = tokenConverter != null + + fun mainCurrencyCanBeSwitched(): Boolean { + return when (amountState.typeOfAmount) { + AmountType.Coin -> coinIsConvertible() + AmountType.Token -> tokenIsConvertible() + AmountType.Reserve -> false + } } } @@ -79,7 +111,7 @@ enum class SendButtonState { } data class AmountState( - val walletAmount: Amount? = null, + val amountToExtract: Amount? = null, val typeOfAmount: AmountType = AmountType.Coin, val viewAmountValue: InputViewValue = InputViewValue(BigDecimal.ZERO.toPlainString()), val viewBalanceValue: String = BigDecimal.ZERO.toPlainString(), @@ -97,10 +129,11 @@ data class AmountState( fun isCoinAmount(): Boolean = typeOfAmount == AmountType.Coin - fun createMainCurrency(type: MainCurrencyType): MainCurrency { - return when (type) { + fun createMainCurrency(type: MainCurrencyType, canSwitched: Boolean): MainCurrency { + return if (!canSwitched) MainCurrency(type, amountToExtract?.currencySymbol ?: "NONE", false) + else when (type) { MainCurrencyType.FIAT -> MainCurrency(type, store.state.globalState.appCurrency) - MainCurrencyType.CRYPTO -> MainCurrency(type, walletAmount?.currencySymbol ?: "NONE") + MainCurrencyType.CRYPTO -> MainCurrency(type, amountToExtract?.currencySymbol ?: "NONE") } } } @@ -112,5 +145,6 @@ enum class MainCurrencyType { data class MainCurrency( val type: MainCurrencyType, - val currencySymbol: String + val currencySymbol: String, + val isEnabled: Boolean = true ) \ 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 de7856d3e6..ec5d1d707a 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,7 +16,6 @@ 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.* @@ -58,11 +57,6 @@ 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() @@ -193,8 +187,6 @@ 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 278b8d62b4..e9145ba4fb 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 @@ -15,6 +15,7 @@ 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.FeeAction +import com.tangem.tap.features.send.redux.reducers.ReceiptReducer import com.tangem.tap.features.send.redux.states.* import com.tangem.tap.features.send.ui.FeeUiHelper import com.tangem.tap.features.send.ui.SendFragment @@ -74,8 +75,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber Error.ADDRESS_SAME_AS_WALLET -> R.string.error_address_same_as_wallet else -> null } - return if (resId == null) null - else context.getString(resId) + return if (resId == null) null else context.getString(resId) } fg.imvPaste.isEnabled = state.pasteIsEnabled @@ -183,6 +183,8 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber fun getString(id: Int): String = mainLayout.context.getString(id) val rough = getString(R.string.sign_rough) + fun roughOrEmpty(value: String): String = if (value == ReceiptReducer.EMPTY) value else "$rough $value" + when (state.visibleTypeOfReceipt) { ReceiptLayoutType.FIAT -> { val receipt = state.fiat ?: return @@ -191,7 +193,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("$rough ${receipt.totalFiat} ${receipt.symbols.fiat}") + totalLayout.tvTotalValue.update("${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}") val willSent = SpannableStringBuilder() .bold { append(receipt.willSentCrypto) }.append(" ") @@ -211,8 +213,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val willSent = SpannableStringBuilder() .bold { - append(rough).append(" ") - append(receipt.willSentFiat).append(" ") + append(roughOrEmpty(receipt.willSentFiat)).append(" ") append(receipt.symbols.fiat) append(" (fee: ${receipt.feeFiat} ") append(receipt.symbols.fiat).append(")") @@ -226,7 +227,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("${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}") val willSent = SpannableStringBuilder() .bold { @@ -252,8 +253,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val willSent = SpannableStringBuilder() .bold { - append(getString(R.string.sign_rough)).append(" ") - append(receipt.totalFiat).append(" ") + append(roughOrEmpty(receipt.totalFiat)).append(" ") append(receipt.symbols.fiat) } totalTokenLayout.tvTotalTokenCryptoValue.update(willSent.toString())