Updated on 2026-08-14

This commit is contained in:
Tangem 2020-09-22 08:03:54 +00:00
commit 1dd8c44e35
12 changed files with 220 additions and 196 deletions

View file

@ -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()
}

View file

@ -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) {

View file

@ -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))
}

View file

@ -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<AppState> = { 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))
}
}

View file

@ -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

View file

@ -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)

View file

@ -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
}
}
}

View file

@ -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
)
)
}

View file

@ -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
}

View file

@ -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<StateId> = 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
)

View file

@ -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

View file

@ -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())