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 e80abad6a3..774c582c4e 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,6 +1,7 @@ package com.tangem.tap.features.send.redux.middlewares import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.TransactionError import com.tangem.common.extensions.isZero import com.tangem.tap.common.redux.AppState @@ -40,7 +41,11 @@ class AmountMiddleware { if (inputValue.isZero() && amountState.amountToSendCrypto.isZero()) return val inputValueCrypto = if (amountState.mainCurrency.type == MainCurrencyType.CRYPTO) inputValue - else sendState.convertToCrypto(inputValue) + else when (amountState.typeOfAmount) { + AmountType.Coin -> sendState.convertFiatToCoin(inputValue) + AmountType.Token -> sendState.convertFiatToToken(inputValue) + else -> inputValue + } dispatch(AmountAction.SetAmount(inputValueCrypto, true)) dispatch(AmountActionUi.CheckAmountToSend) @@ -93,6 +98,7 @@ class AmountMiddleware { 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 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 ab2505b1ca..ebe5c2b7b9 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 @@ -25,16 +25,18 @@ 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) + else sendState.convertCoinToFiat(state.amountToSendCrypto) + val rescaledBalance = sendState.convertCoinToFiat(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 ) } @@ -42,8 +44,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,7 +61,7 @@ class AmountReducer : SendInternalReducer { val result = when (action) { is AmountAction.SetAmount -> { val amount = if (state.mainCurrency.type == MainCurrencyType.CRYPTO) action.amountCrypto - else sendState.convertToFiat(action.amountCrypto, true) + else sendState.convertCoinToFiat(action.amountCrypto, true) state.copy( viewAmountValue = InputViewValue(amount.stripZeroPlainString(), action.isUserInput), amountToSendCrypto = action.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 0d92ab9a57..323a34cf74 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,88 @@ 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 { + + private lateinit var sendState: SendState + private lateinit var amountState: AmountState + private lateinit var feeState: FeeState + private val EMPTY = "-" + 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 { + private fun createFiatType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptFiat { val feeCrypto = feeState.getCurrentFee() - val feeFiat = converter.toFiatWithPrecision(feeCrypto) + 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 { + private fun createCryptoType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptCrypto { val feeCrypto = feeState.getCurrentFee() - val feeFiat = converter.toFiatWithPrecision(feeCrypto) + val feeFiat = convertToFiatPrecision(feeCrypto) if (showBlank) { return ReceiptCrypto("0", feeCrypto.stripZeroPlainString(), "0", "0", "0", symbols) } @@ -100,8 +93,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 +103,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 { + private fun createTokenFiatType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptTokenFiat { val feeCoin = feeState.getCurrentFee() - val feeFiat = coinConverter.toFiatWithPrecision(feeCoin) 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().stripZeroPlainString(), + feeFiat = feeFiat.stripZeroPlainString(), + totalFiat = totalFiat.scaleToFiat().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 { + private fun createTokenCryptoType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptTokenCrypto { val feeCoin = feeState.getCurrentFee() 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().stripZeroPlainString(), + symbols = symbols + ) + } else { + ReceiptTokenCrypto( + amountToken = tokensToSend.stripZeroPlainString(), + feeCoin = feeCoin.stripZeroPlainString(), + totalFiat = EMPTY, + symbols = symbols + ) + } + } private fun determineSymbols(wallet: Wallet): ReceiptSymbols { @@ -193,4 +194,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..c9a36792e2 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 @@ -61,13 +61,10 @@ private class PrepareSendScreenStatesReducer : SendInternalReducer { 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, 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..911a0104a4 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 @@ -52,17 +51,27 @@ data class SendState( MainCurrencyType.CRYPTO -> amountState.walletAmount?.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 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 convertToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { - val converter = getConverter() + fun convertFiatToToken(value: BigDecimal): BigDecimal { + return if (!this.tokenIsConvertible()) value + else tokenConverter!!.toCrypto(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) } @@ -72,6 +81,17 @@ data class SendState( val needToExtractFee = amountState.isCoinAmount() && feeState.feeIsIncluded return if (needToExtractFee) value.minus(feeState.getCurrentFee()) 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 + } + } } enum class SendButtonState { @@ -97,8 +117,9 @@ 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, walletAmount?.currencySymbol ?: "NONE", false) + else when (type) { MainCurrencyType.FIAT -> MainCurrency(type, store.state.globalState.appCurrency) MainCurrencyType.CRYPTO -> MainCurrency(type, walletAmount?.currencySymbol ?: "NONE") } @@ -112,5 +133,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