Updated on 2026-08-14
This commit is contained in:
commit
65625b9f87
66 changed files with 759 additions and 649 deletions
|
|
@ -4,6 +4,7 @@ import android.content.Intent
|
|||
import android.content.pm.ActivityInfo
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsControllerCompat
|
||||
|
|
@ -85,6 +86,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler {
|
|||
}
|
||||
|
||||
private fun systemActions() {
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
val windowInsetsController = WindowInsetsControllerCompat(window, binding.root)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ fun Blockchain.getRoundIconRes(): Int {
|
|||
Blockchain.Dogecoin -> R.drawable.ic_dogecoin_round
|
||||
Blockchain.Tron, Blockchain.TronTestnet -> R.drawable.ic_tron_round
|
||||
Blockchain.Gnosis -> R.drawable.ic_gnosis_round
|
||||
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> R.drawable.ic_polkadot_round
|
||||
else -> R.drawable.ic_tangem_logo
|
||||
}
|
||||
}
|
||||
|
|
@ -55,6 +56,7 @@ fun Blockchain.getGreyedOutIconRes(): Int {
|
|||
Blockchain.Dogecoin -> R.drawable.ic_dogecoin_no_color
|
||||
Blockchain.Tron, Blockchain.TronTestnet -> R.drawable.ic_tron_no_color
|
||||
Blockchain.Gnosis -> R.drawable.ic_gnosis_no_color
|
||||
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> R.drawable.ic_polkadot_no_color
|
||||
else -> R.drawable.ic_tangem_logo
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,19 +5,23 @@ import androidx.annotation.ColorInt
|
|||
import androidx.core.graphics.luminance
|
||||
import androidx.core.graphics.toColorInt
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.wallet.R
|
||||
|
||||
@ColorInt
|
||||
fun Token.getColor(): Int {
|
||||
return try {
|
||||
("#" + this.contractAddress.subSequence(2..7).toString())
|
||||
.toColorInt()
|
||||
fun Token.getColor(isTestnet: Boolean = false): Int {
|
||||
val defaultColor = "#C7C7CC".toColorInt() // equivalent to R.color.lightGray4
|
||||
|
||||
return if (isTestnet)
|
||||
defaultColor
|
||||
else try {
|
||||
("#" + this.contractAddress.subSequence(2..7).toString()).toColorInt()
|
||||
} catch (exception: Exception) {
|
||||
R.color.lightGray4
|
||||
defaultColor
|
||||
}
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun Token.getTextColor(): Int {
|
||||
return if (this.getColor().luminance > 0.5) Color.BLACK else Color.WHITE
|
||||
fun Token.getTextColor(isTestnet: Boolean = false): Int = when {
|
||||
isTestnet -> Color.WHITE
|
||||
this.getColor().luminance > 0.5 -> Color.BLACK
|
||||
else -> Color.WHITE
|
||||
}
|
||||
|
|
@ -51,11 +51,10 @@ suspend fun WalletManager.safeUpdate(): Result<Wallet> = try {
|
|||
|
||||
fun WalletManager?.getToUpUrl(): String? {
|
||||
val globalState = store.state.globalState
|
||||
val exchangeManager = globalState.exchangeManager ?: return null
|
||||
val wallet = this?.wallet ?: return null
|
||||
|
||||
val defaultAddress = wallet.address
|
||||
return exchangeManager.getUrl(
|
||||
return globalState.exchangeManager.getUrl(
|
||||
action = CurrencyExchangeManager.Action.Buy,
|
||||
blockchain = wallet.blockchain,
|
||||
cryptoCurrencyName = wallet.blockchain.currency,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.tap.common.feature
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Feature {
|
||||
fun featureIsSwitchedOn():Boolean
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ data class GlobalState(
|
|||
val appCurrency: FiatCurrency = FiatCurrency.Default,
|
||||
val scanCardFailsCounter: Int = 0,
|
||||
val dialog: StateDialog? = null,
|
||||
val exchangeManager: CurrencyExchangeManager? = null,
|
||||
val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(),
|
||||
val resources: AndroidResources = AndroidResources(),
|
||||
val analyticsHandlers: AnalyticsHandler? = null,
|
||||
val userCountryCode: String? = null,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.features.details.ui.cardsettings
|
|||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
|
|
@ -10,6 +9,10 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -51,14 +54,12 @@ fun CardSettingsReadCard(
|
|||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
|
||||
) {
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 40.dp),
|
||||
) {
|
||||
Image(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -77,16 +78,13 @@ fun CardSettingsReadCard(
|
|||
contentDescription = "",
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 32.dp),
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.scan_card_settings_title),
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
|
|
@ -97,6 +95,9 @@ fun CardSettingsReadCard(
|
|||
text = stringResource(id = R.string.scan_card_settings_message),
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
style = TangemTypography.body1,
|
||||
modifier = modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.weight(weight = 1f, fill = false),
|
||||
)
|
||||
Spacer(modifier = modifier.size(29.dp))
|
||||
DetailsMainButton(
|
||||
|
|
@ -113,11 +114,13 @@ fun CardSettings(
|
|||
state: CardSettingsScreenState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
if (state.cardDetails == null) return
|
||||
|
||||
LazyColumn(
|
||||
modifier = modifier
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
state.cardDetails?.map {
|
||||
items(state.cardDetails) {
|
||||
val paddingBottom = when (it) {
|
||||
is CardInfo.CardId, is CardInfo.Issuer -> 12.dp
|
||||
is CardInfo.SignedHashes -> 14.dp
|
||||
|
|
@ -156,9 +159,7 @@ fun CardSettings(
|
|||
style = TangemTypography.body2,
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
package com.tangem.tap.features.details.ui.resetcard
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconToggleButton
|
||||
import androidx.compose.material.Text
|
||||
|
|
@ -36,18 +36,11 @@ fun ResetCardScreen(
|
|||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(modifier = modifier.background(colorResource(id = R.color.background_primary))) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_reset_background),
|
||||
contentDescription = "",
|
||||
modifier = modifier.offset(y = (-16).dp),
|
||||
)
|
||||
SettingsScreensScaffold(
|
||||
content = { ResetCardView(state = state, modifier = modifier) },
|
||||
onBackClick = onBackPressed,
|
||||
backgroundColor = Color.Transparent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -57,81 +50,91 @@ fun ResetCardView(
|
|||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_reset_background),
|
||||
contentDescription = "",
|
||||
modifier = modifier.offset(y = (-82).dp),
|
||||
)
|
||||
ScreenTitle(titleRes = R.string.reset_card_to_factory_navigation_title)
|
||||
}
|
||||
Spacer(
|
||||
modifier = modifier.weight(1f),
|
||||
)
|
||||
Column(
|
||||
modifier = modifier
|
||||
.defaultMinSize(20.dp)
|
||||
.weight(1f),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.common_attention),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
style = TangemTypography.headline3,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
|
||||
Spacer(modifier = modifier.size(24.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.reset_card_to_factory_message),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
style = TangemTypography.body1,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
)
|
||||
|
||||
Spacer(modifier = modifier.size(44.dp))
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = 20.dp),
|
||||
.offset(y = (-32).dp),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
) {
|
||||
IconToggleButton(
|
||||
checked = state.accepted,
|
||||
onCheckedChange = state.onAcceptWarningToggleClick,
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
if (state.accepted) {
|
||||
R.drawable.ic_accepted
|
||||
} else {
|
||||
R.drawable.ic_unticked
|
||||
},
|
||||
),
|
||||
contentDescription = null,
|
||||
tint = if (state.accepted) {
|
||||
colorResource(id = R.color.icon_accent)
|
||||
} else {
|
||||
colorResource(id = R.color.icon_secondary)
|
||||
},
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(id = R.string.reset_card_to_factory_warning_message),
|
||||
style = TangemTypography.body2,
|
||||
text = stringResource(id = R.string.common_attention),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
style = TangemTypography.headline3,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
|
||||
Spacer(modifier = modifier.size(24.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.reset_card_to_factory_message),
|
||||
modifier = modifier
|
||||
.padding(start = 20.dp, end = 20.dp),
|
||||
style = TangemTypography.body1,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = modifier.size(32.dp))
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(start = 16.dp, end = 16.dp, bottom = 32.dp),
|
||||
) {
|
||||
DetailsMainButton(
|
||||
title = stringResource(id = R.string.reset_card_to_factory_button_title),
|
||||
onClick = state.onResetButtonClick,
|
||||
enabled = state.resetButtonEnabled,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = modifier.size(44.dp))
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(end = 20.dp),
|
||||
) {
|
||||
IconToggleButton(
|
||||
checked = state.accepted,
|
||||
onCheckedChange = state.onAcceptWarningToggleClick,
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
if (state.accepted) {
|
||||
R.drawable.ic_accepted
|
||||
} else {
|
||||
R.drawable.ic_unticked
|
||||
},
|
||||
),
|
||||
contentDescription = null,
|
||||
tint = if (state.accepted) {
|
||||
colorResource(id = R.color.icon_accent)
|
||||
} else {
|
||||
colorResource(id = R.color.icon_secondary)
|
||||
},
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(id = R.string.reset_card_to_factory_warning_message),
|
||||
style = TangemTypography.body2,
|
||||
color = colorResource(id = R.color.text_secondary),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = modifier.size(32.dp))
|
||||
Box(
|
||||
modifier = modifier
|
||||
.padding(start = 16.dp, end = 16.dp),
|
||||
) {
|
||||
DetailsMainButton(
|
||||
title = stringResource(id = R.string.reset_card_to_factory_button_title),
|
||||
onClick = state.onResetButtonClick,
|
||||
enabled = state.resetButtonEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.material.RadioButtonDefaults
|
||||
import androidx.compose.material.Text
|
||||
|
|
@ -22,6 +23,7 @@ import androidx.compose.ui.unit.dp
|
|||
import com.tangem.tap.common.compose.TangemTypography
|
||||
import com.tangem.tap.features.details.redux.SecurityOption
|
||||
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
||||
import com.tangem.tap.features.details.ui.common.ScreenTitle
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
import com.tangem.wallet.R
|
||||
|
||||
|
|
@ -33,7 +35,7 @@ fun SecurityModeScreen(
|
|||
) {
|
||||
SettingsScreensScaffold(
|
||||
content = { SecurityModeOptions(state = state, modifier = modifier) },
|
||||
titleRes = R.string.card_settings_security_mode,
|
||||
// titleRes = R.string.card_settings_security_mode,
|
||||
onBackClick = onBackPressed,
|
||||
)
|
||||
}
|
||||
|
|
@ -47,10 +49,13 @@ fun SecurityModeOptions(
|
|||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 28.dp)
|
||||
.offset(y = (-16).dp),
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(bottom = 28.dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
ScreenTitle(titleRes = R.string.card_settings_security_mode, modifier.padding(bottom = 36.dp))
|
||||
|
||||
|
||||
state.availableOptions.map {
|
||||
SecurityOption(option = it, state = state, modifier = modifier)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.zxing.Result
|
||||
import com.otaliastudios.cameraview.CameraView
|
||||
|
|
@ -23,6 +24,7 @@ class QrScanFragment : Fragment(0), ZXingScannerView.ResultHandler {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.window?.let { WindowCompat.setDecorFitsSystemWindows(it, true) }
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
|
|
@ -54,7 +56,7 @@ class QrScanFragment : Fragment(0), ZXingScannerView.ResultHandler {
|
|||
|
||||
override fun handleResult(result: Result) {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
|
||||
activity?.window?.let { WindowCompat.setDecorFitsSystemWindows(it, false) }
|
||||
if (!result.text.isNullOrBlank()) {
|
||||
store.dispatch(WalletConnectAction.OpenSession(result.text))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ data class OnboardingNoteState(
|
|||
get() = steps.indexOf(currentStep)
|
||||
|
||||
val isBuyAllowed: Boolean by ReadOnlyProperty<Any, Boolean> { _, _ ->
|
||||
store.state.globalState.exchangeManager?.availableForBuy(walletBalance.currency) ?: false
|
||||
store.state.globalState.exchangeManager.availableForBuy(walletBalance.currency)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ data class TwinCardsState(
|
|||
get() = currentStep == TwinCardsStep.CreateSecondWallet || currentStep == TwinCardsStep.CreateThirdWallet
|
||||
|
||||
val isBuyAllowed: Boolean by ReadOnlyProperty<Any, Boolean> { _, _ ->
|
||||
store.state.globalState.exchangeManager?.availableForBuy(walletBalance.currency) ?: false
|
||||
store.state.globalState.exchangeManager.availableForBuy(walletBalance.currency)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -113,15 +113,11 @@ sealed class FeeActionUi : SendScreenActionUi {
|
|||
}
|
||||
|
||||
sealed class FeeAction : SendScreenAction {
|
||||
enum class Error {
|
||||
ADDRESS_OR_AMOUNT_IS_EMPTY,
|
||||
REQUEST_FAILED
|
||||
}
|
||||
|
||||
object RequestFee : FeeAction()
|
||||
sealed class FeeCalculation : FeeAction() {
|
||||
data class SetFeeResult(val fee: List<Amount>) : FeeCalculation()
|
||||
data class SetFeeError(val error: Error) : FeeCalculation()
|
||||
object ClearResult : FeeCalculation()
|
||||
}
|
||||
|
||||
data class ChangeLayoutVisibility(
|
||||
|
|
@ -160,6 +156,12 @@ sealed class SendAction : SendScreenAction {
|
|||
data class CardSdkError(val error: TangemSdkError): Dialog()
|
||||
data class BlockchainSdkError(val error: com.tangem.blockchain.common.BlockchainSdkError): Dialog()
|
||||
}
|
||||
|
||||
data class RequestFeeError(
|
||||
val error: com.tangem.blockchain.common.BlockchainSdkError,
|
||||
val onRetry: () -> Unit,
|
||||
) : Dialog()
|
||||
|
||||
object Hide : Dialog()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,17 @@ 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.features.send.redux.*
|
||||
import com.tangem.tap.features.send.redux.AmountAction
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi
|
||||
import com.tangem.tap.features.send.redux.FeeAction
|
||||
import com.tangem.tap.features.send.redux.FeeActionUi
|
||||
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.features.send.redux.states.SendState
|
||||
import org.rekotlin.Action
|
||||
import java.math.BigDecimal
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -62,18 +68,11 @@ class AmountMiddleware {
|
|||
|
||||
val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend(inputCrypto))
|
||||
val transactionErrors = walletManager.validateTransaction(amountToSend, sendState.feeState.currentFee)
|
||||
transactionErrors.remove(TransactionError.TezosSendAll)
|
||||
if (transactionErrors.isEmpty()) {
|
||||
val amountFieldErrors = filterErrorsForAmountField(transactionErrors)
|
||||
if (amountFieldErrors.isEmpty()) {
|
||||
dispatch(AmountAction.SetAmountError(null))
|
||||
} else {
|
||||
val amountErrors = extractErrorsForAmountField(transactionErrors)
|
||||
if (amountErrors.isNotEmpty()) {
|
||||
transactionErrors.removeAll(amountErrors)
|
||||
dispatch(AmountAction.SetAmountError(createValidateTransactionError(amountErrors, walletManager)))
|
||||
}
|
||||
if (transactionErrors.isNotEmpty()) {
|
||||
dispatch(SendAction.SendError(createValidateTransactionError(transactionErrors, walletManager)))
|
||||
}
|
||||
dispatch(AmountAction.SetAmountError(createValidateTransactionError(amountFieldErrors, walletManager)))
|
||||
}
|
||||
dispatch(ReceiptAction.RefreshReceipt)
|
||||
dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState()))
|
||||
|
|
@ -104,4 +103,28 @@ class AmountMiddleware {
|
|||
|
||||
dispatch(AmountActionUi.SetMainCurrency(type))
|
||||
}
|
||||
}
|
||||
|
||||
private fun filterErrorsForAmountField(errors: EnumSet<TransactionError>): EnumSet<TransactionError> {
|
||||
val showIntoAmountField = EnumSet.noneOf(TransactionError::class.java)
|
||||
errors.forEach {
|
||||
when (it) {
|
||||
TransactionError.AmountExceedsBalance -> {
|
||||
showIntoAmountField.remove(TransactionError.TotalExceedsBalance)
|
||||
showIntoAmountField.add(it)
|
||||
}
|
||||
TransactionError.FeeExceedsBalance -> {
|
||||
showIntoAmountField.remove(TransactionError.TotalExceedsBalance)
|
||||
showIntoAmountField.add(it)
|
||||
}
|
||||
TransactionError.TotalExceedsBalance -> {
|
||||
val notAcceptable = listOf(TransactionError.AmountExceedsBalance, TransactionError.FeeExceedsBalance)
|
||||
if (!showIntoAmountField.containsAll(notAcceptable)) showIntoAmountField.add(it)
|
||||
}
|
||||
else -> showIntoAmountField.add(it)
|
||||
}
|
||||
}
|
||||
showIntoAmountField.remove(TransactionError.TezosSendAll)
|
||||
|
||||
return showIntoAmountField
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.send.redux.middlewares
|
|||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.blockchain.common.TransactionSender
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.common.extensions.isZero
|
||||
|
|
@ -31,8 +32,7 @@ class RequestFeeMiddleware {
|
|||
val scanResponse = appState.globalState.scanResponse ?: return
|
||||
|
||||
if (!SendState.isReadyToRequestFee()) {
|
||||
dispatch(FeeAction.FeeCalculation.SetFeeError(FeeAction.Error.ADDRESS_OR_AMOUNT_IS_EMPTY))
|
||||
// dispatch(FeeAction.ChangeLayoutVisibility(main = false, chipGroup = true))
|
||||
dispatch(FeeAction.FeeCalculation.ClearResult)
|
||||
dispatch(ReceiptAction.RefreshReceipt)
|
||||
dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState()))
|
||||
return
|
||||
|
|
@ -66,14 +66,21 @@ class RequestFeeMiddleware {
|
|||
}
|
||||
}
|
||||
is Result.Failure -> {
|
||||
dispatch(FeeAction.FeeCalculation.SetFeeError(FeeAction.Error.REQUEST_FAILED))
|
||||
dispatch(FeeAction.FeeCalculation.ClearResult)
|
||||
dispatch(FeeAction.ChangeLayoutVisibility(main = false))
|
||||
|
||||
val blockchainSdkError = feeResult.error as? BlockchainSdkError ?: return@withContext
|
||||
dispatch(
|
||||
SendAction.Dialog.RequestFeeError(
|
||||
error = blockchainSdkError,
|
||||
onRetry = { dispatch(FeeAction.RequestFee) },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
dispatch(AmountActionUi.CheckAmountToSend)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,13 +100,13 @@ class FeeMock {
|
|||
suspend fun feeStandard(blockchain: Blockchain): List<Amount> = listOf(
|
||||
Amount(0.001500.toBigDecimal(), blockchain),
|
||||
Amount(0.0030.toBigDecimal(), blockchain),
|
||||
Amount(0.0045001.toBigDecimal(), blockchain)
|
||||
Amount(0.0045001.toBigDecimal(), blockchain),
|
||||
)
|
||||
|
||||
suspend fun feeStandardBig(blockchain: Blockchain): List<Amount> = listOf(
|
||||
Amount(1.76.toBigDecimal(), blockchain),
|
||||
Amount(2.30.toBigDecimal(), blockchain),
|
||||
Amount(3.45.toBigDecimal(), blockchain)
|
||||
Amount(3.45.toBigDecimal(), blockchain),
|
||||
)
|
||||
|
||||
suspend fun feeSingle(blockchain: Blockchain): List<Amount> = listOf(Amount(0.0015.toBigDecimal(), blockchain))
|
||||
|
|
|
|||
|
|
@ -114,9 +114,8 @@ private fun verifyAndSendTransaction(
|
|||
val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend())
|
||||
|
||||
val transactionErrors = walletManager.validateTransaction(amountToSend, feeAmount)
|
||||
val hadTezosError = transactionErrors.remove(TransactionError.TezosSendAll)
|
||||
when {
|
||||
hadTezosError -> {
|
||||
transactionErrors.contains(TransactionError.TezosSendAll) -> {
|
||||
val reduceAmount = walletManager.wallet.blockchain.minimalAmount()
|
||||
dispatch(
|
||||
SendAction.Dialog.TezosWarningDialog(
|
||||
|
|
@ -135,9 +134,6 @@ private fun verifyAndSendTransaction(
|
|||
),
|
||||
)
|
||||
}
|
||||
transactionErrors.isNotEmpty() -> {
|
||||
dispatch(SendAction.SendError(createValidateTransactionError(transactionErrors, walletManager)))
|
||||
}
|
||||
else -> {
|
||||
sendTransaction(
|
||||
action, walletManager, amountToSend, feeAmount, destinationAddress,
|
||||
|
|
@ -272,10 +268,8 @@ private fun sendTransaction(
|
|||
dispatch(SendAction.Dialog.SendTransactionFails.CardSdkError(tangemSdkError))
|
||||
}
|
||||
is BlockchainSdkError.CreateAccountUnderfunded -> {
|
||||
// from XLM, XRP
|
||||
val reserve = error.minReserve.value?.stripZeroPlainString() ?: "0"
|
||||
val symbol = error.minReserve.currencySymbol
|
||||
dispatch(SendAction.SendError(TapError.CreateAccountUnderfunded(listOf(reserve, symbol))))
|
||||
// from XLM, XRP, Polkadot
|
||||
dispatch(SendAction.Dialog.SendTransactionFails.BlockchainSdkError(error))
|
||||
}
|
||||
else -> {
|
||||
when {
|
||||
|
|
@ -300,29 +294,6 @@ private fun sendTransaction(
|
|||
}
|
||||
}
|
||||
|
||||
fun extractErrorsForAmountField(errors: EnumSet<TransactionError>): EnumSet<TransactionError> {
|
||||
val showIntoAmountField = EnumSet.noneOf(TransactionError::class.java)
|
||||
errors.forEach {
|
||||
when (it) {
|
||||
TransactionError.AmountExceedsBalance -> {
|
||||
showIntoAmountField.remove(TransactionError.TotalExceedsBalance)
|
||||
showIntoAmountField.add(it)
|
||||
}
|
||||
TransactionError.FeeExceedsBalance -> {
|
||||
showIntoAmountField.remove(TransactionError.TotalExceedsBalance)
|
||||
showIntoAmountField.add(it)
|
||||
}
|
||||
TransactionError.TotalExceedsBalance -> {
|
||||
val notAcceptable = listOf(TransactionError.AmountExceedsBalance, TransactionError.FeeExceedsBalance)
|
||||
if (!showIntoAmountField.containsAll(notAcceptable)) showIntoAmountField.add(it)
|
||||
}
|
||||
TransactionError.InvalidAmountValue -> showIntoAmountField.add(it)
|
||||
TransactionError.InvalidFeeValue -> showIntoAmountField.add(it)
|
||||
}
|
||||
}
|
||||
return showIntoAmountField
|
||||
}
|
||||
|
||||
fun createValidateTransactionError(
|
||||
errorList: EnumSet<TransactionError>,
|
||||
walletManager: WalletManager,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
package com.tangem.tap.features.send.redux.reducers
|
||||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.common.extensions.fullNameWithoutTestnet
|
||||
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.FeePrecision
|
||||
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
|
||||
|
|
@ -28,10 +24,9 @@ class FeeReducer : SendInternalReducer {
|
|||
is FeeActionUi.ChangeSelectedFee -> {
|
||||
val currentFee = createValueOfFeeAmount(action.feeType, state.feeList)
|
||||
state.copy(
|
||||
selectedFeeType = action.feeType,
|
||||
currentFee = currentFee
|
||||
selectedFeeType = action.feeType,
|
||||
currentFee = currentFee,
|
||||
)
|
||||
|
||||
}
|
||||
is FeeActionUi.ChangeIncludeFee -> state.copy(feeIsIncluded = action.isIncluded)
|
||||
}
|
||||
|
|
@ -41,14 +36,14 @@ class FeeReducer : SendInternalReducer {
|
|||
private fun handleAction(action: FeeAction, sendState: SendState, state: FeeState): SendState {
|
||||
val result = when (action) {
|
||||
is FeeAction.RequestFee -> {
|
||||
state.copy(error = null)
|
||||
state
|
||||
}
|
||||
is FeeAction.ChangeLayoutVisibility -> {
|
||||
fun getVisibility(current: Boolean, proposed: Boolean?): Boolean = proposed ?: current
|
||||
state.copy(
|
||||
mainLayoutIsVisible = getVisibility(state.mainLayoutIsVisible, action.main),
|
||||
controlsLayoutIsVisible = getVisibility(state.controlsLayoutIsVisible, action.controls),
|
||||
feeChipGroupIsVisible = getVisibility(state.feeChipGroupIsVisible, action.chipGroup)
|
||||
mainLayoutIsVisible = getVisibility(state.mainLayoutIsVisible, action.main),
|
||||
controlsLayoutIsVisible = getVisibility(state.controlsLayoutIsVisible, action.controls),
|
||||
feeChipGroupIsVisible = getVisibility(state.feeChipGroupIsVisible, action.chipGroup),
|
||||
)
|
||||
}
|
||||
is FeeAction.FeeCalculation.SetFeeResult -> {
|
||||
|
|
@ -58,30 +53,27 @@ class FeeReducer : SendInternalReducer {
|
|||
val currentFee = createValueOfFeeAmount(feeType, fees)
|
||||
|
||||
state.copy(
|
||||
selectedFeeType = feeType,
|
||||
feeList = fees,
|
||||
currentFee = currentFee,
|
||||
error = null,
|
||||
feePrecision = getFeePrecision(sendState)
|
||||
selectedFeeType = feeType,
|
||||
feeList = fees,
|
||||
currentFee = currentFee,
|
||||
feeIsApproximate = isFeeApproximate(sendState),
|
||||
)
|
||||
} else {
|
||||
val feeType = getCurrentFeeType(state)
|
||||
val currentFee = createValueOfFeeAmount(feeType, fees)
|
||||
|
||||
state.copy(
|
||||
selectedFeeType = feeType,
|
||||
feeList = fees,
|
||||
currentFee = currentFee,
|
||||
error = null,
|
||||
feePrecision = getFeePrecision(sendState)
|
||||
selectedFeeType = feeType,
|
||||
feeList = fees,
|
||||
currentFee = currentFee,
|
||||
feeIsApproximate = isFeeApproximate(sendState),
|
||||
)
|
||||
}
|
||||
}
|
||||
is FeeAction.FeeCalculation.SetFeeError -> {
|
||||
FeeAction.FeeCalculation.ClearResult -> {
|
||||
state.copy(
|
||||
feeList = null,
|
||||
currentFee = null,
|
||||
error = action.error
|
||||
feeList = null,
|
||||
currentFee = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -104,20 +96,12 @@ class FeeReducer : SendInternalReducer {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getFeePrecision(sendState: SendState): FeePrecision {
|
||||
val blockchain = sendState.walletManager?.wallet?.blockchain
|
||||
return if (
|
||||
(blockchain?.fullNameWithoutTestnet == Blockchain.Arbitrum.fullName ||
|
||||
blockchain?.fullNameWithoutTestnet == Blockchain.Tron.fullName ||
|
||||
blockchain?.fullNameWithoutTestnet == Blockchain.Gnosis.fullName) &&
|
||||
sendState.amountState.typeOfAmount is AmountType.Token
|
||||
) {
|
||||
FeePrecision.CAN_BE_LOWER
|
||||
} else {
|
||||
FeePrecision.PRECISE
|
||||
}
|
||||
}
|
||||
private fun isFeeApproximate(sendState: SendState): Boolean {
|
||||
val blockchain = sendState.walletManager?.wallet?.blockchain ?: return false
|
||||
|
||||
val amountType = sendState.amountState.typeOfAmount
|
||||
return blockchain.isFeeApproximate(amountType)
|
||||
}
|
||||
|
||||
private fun getCurrentFeeType(state: FeeState): FeeType {
|
||||
return if (state.selectedFeeType == FeeType.SINGLE) FeeType.NORMAL else state.selectedFeeType
|
||||
|
|
|
|||
|
|
@ -6,7 +6,18 @@ 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.features.send.redux.states.AmountState
|
||||
import com.tangem.tap.features.send.redux.states.FeeState
|
||||
import com.tangem.tap.features.send.redux.states.MainCurrencyType
|
||||
import com.tangem.tap.features.send.redux.states.ReceiptCrypto
|
||||
import com.tangem.tap.features.send.redux.states.ReceiptFiat
|
||||
import com.tangem.tap.features.send.redux.states.ReceiptLayoutType
|
||||
import com.tangem.tap.features.send.redux.states.ReceiptState
|
||||
import com.tangem.tap.features.send.redux.states.ReceiptSymbols
|
||||
import com.tangem.tap.features.send.redux.states.ReceiptTokenCrypto
|
||||
import com.tangem.tap.features.send.redux.states.ReceiptTokenFiat
|
||||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
import com.tangem.tap.features.wallet.redux.WalletState.Companion.CAN_BE_LOWER_SIGN
|
||||
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
|
||||
import com.tangem.tap.store
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -91,7 +102,7 @@ class ReceiptReducer : SendInternalReducer {
|
|||
if (feeState.feeIsIncluded) {
|
||||
return ReceiptCrypto(
|
||||
amountCrypto = amountState.amountToSendCrypto.minus(feeCrypto).stripZeroPlainString(),
|
||||
feeCrypto = feeCrypto.stripZeroPlainString(),
|
||||
feeCrypto = feeCrypto.stripZeroPlainString().addPrecisionSign(),
|
||||
totalCrypto = amountState.amountToSendCrypto.stripZeroPlainString(),
|
||||
feeFiat = feeFiat,
|
||||
willSentFiat = convertToFiatPrecision(amountState.amountToSendCrypto),
|
||||
|
|
@ -101,7 +112,7 @@ class ReceiptReducer : SendInternalReducer {
|
|||
val totalCrypto = amountState.amountToSendCrypto.plus(feeCrypto)
|
||||
return ReceiptCrypto(
|
||||
amountCrypto = amountState.amountToSendCrypto.stripZeroPlainString(),
|
||||
feeCrypto = feeCrypto.stripZeroPlainString(),
|
||||
feeCrypto = feeCrypto.stripZeroPlainString().addPrecisionSign(),
|
||||
totalCrypto = totalCrypto.stripZeroPlainString(),
|
||||
feeFiat = feeFiat,
|
||||
willSentFiat = convertToFiatPrecision(totalCrypto),
|
||||
|
|
@ -125,8 +136,7 @@ class ReceiptReducer : SendInternalReducer {
|
|||
val totalFiat = amountFiat.plus(feeFiat)
|
||||
ReceiptTokenFiat(
|
||||
amountFiat = amountFiat.scaleToFiat(true).stripZeroPlainString(),
|
||||
feeFiat = feeFiat.scaleToFiat(true)
|
||||
.stripZeroPlainString().addPrecisionSign(),
|
||||
feeFiat = feeFiat.scaleToFiat(true).stripZeroPlainString().addPrecisionSign(),
|
||||
totalFiat = totalFiat.scaleToFiat(true).stripZeroPlainString(),
|
||||
willSentToken = tokensToSend.stripZeroPlainString(),
|
||||
willSentFeeCoin = feeCoin.stripZeroPlainString(),
|
||||
|
|
@ -207,6 +217,8 @@ class ReceiptReducer : SendInternalReducer {
|
|||
}
|
||||
}
|
||||
|
||||
private fun String.addPrecisionSign(): String =
|
||||
("${feeState.feePrecision.symbol} $this").trim()
|
||||
private fun String.addPrecisionSign(): String {
|
||||
val result = if (feeState.feeIsApproximate) "$CAN_BE_LOWER_SIGN $this" else ""
|
||||
return result.trim()
|
||||
}
|
||||
}
|
||||
|
|
@ -59,10 +59,10 @@ private class SendReducer : SendInternalReducer {
|
|||
is SendAction.Dialog.TezosWarningDialog -> sendState.copy(dialog = action)
|
||||
is SendAction.Dialog.SendTransactionFails.CardSdkError -> sendState.copy(dialog = action)
|
||||
is SendAction.Dialog.SendTransactionFails.BlockchainSdkError -> sendState.copy(dialog = action)
|
||||
is SendAction.Dialog.RequestFeeError -> sendState.copy(dialog = action)
|
||||
is SendAction.Dialog.Hide -> sendState.copy(dialog = null)
|
||||
is SendAction.Warnings.Set -> sendState.copy(sendWarningsList = action.warningList)
|
||||
is SendAction.SendSpecificTransaction ->
|
||||
handleSendSpecificTransactionAction(action, sendState)
|
||||
is SendAction.SendSpecificTransaction -> handleSendSpecificTransactionAction(action, sendState)
|
||||
else -> return sendState
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,19 @@ package com.tangem.tap.features.send.redux.reducers
|
|||
import com.tangem.blockchain.blockchains.stellar.StellarMemo
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.features.send.redux.SendScreenAction
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.*
|
||||
import com.tangem.tap.features.send.redux.states.*
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.BinanceMemo
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.Prepare
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.Release
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.XlmMemo
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction.XrpDestinationTag
|
||||
import com.tangem.tap.features.send.redux.states.BinanceMemoState
|
||||
import com.tangem.tap.features.send.redux.states.InputViewValue
|
||||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtraError
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
|
||||
import com.tangem.tap.features.send.redux.states.XlmMemoState
|
||||
import com.tangem.tap.features.send.redux.states.XlmMemoType
|
||||
import com.tangem.tap.features.send.redux.states.XrpDestinationTagState
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -53,42 +64,33 @@ class TransactionExtrasReducer : SendInternalReducer {
|
|||
}
|
||||
|
||||
private fun handleXlmMemo(
|
||||
action: XlmMemo,
|
||||
sendState: SendState,
|
||||
infoState: TransactionExtrasState,
|
||||
action: XlmMemo,
|
||||
sendState: SendState,
|
||||
infoState: TransactionExtrasState,
|
||||
): SendState {
|
||||
fun clearMemo(memo: XlmMemoState): XlmMemoState = memo.copy(text = null, id = null, error = null)
|
||||
|
||||
val result = when (action) {
|
||||
// is XlmMemo.ChangeSelectedMemo -> {
|
||||
// val inputViewValue = InputViewValue("", false)
|
||||
// val memo = infoState.xlmMemo?.copy(
|
||||
// viewFieldValue = inputViewValue,
|
||||
// selectedMemoType = action.memoType,
|
||||
// ) ?: XlmMemoState(inputViewValue, action.memoType)
|
||||
//
|
||||
// infoState.copy(xlmMemo = clearMemo(memo))
|
||||
// }
|
||||
is XlmMemo.HandleUserInput -> {
|
||||
val inputViewValue = InputViewValue(action.data, true)
|
||||
var memo = infoState.xlmMemo?.copy(viewFieldValue = inputViewValue)
|
||||
?: XlmMemoState(inputViewValue)
|
||||
?: XlmMemoState(inputViewValue)
|
||||
memo = clearMemo(memo)
|
||||
memo = when (infoState.xlmMemo?.selectedMemoType) {
|
||||
XlmMemoType.TEXT -> memo.copy(text = StellarMemo.Text(action.data))
|
||||
XlmMemoType.ID -> {
|
||||
val id = action.data.toBigIntegerOrNull()
|
||||
if (id != null) {
|
||||
if (id > XlmMemoState.MAX_NUMBER) {
|
||||
memo.copy(error = TransactionExtraError.INVALID_XLM_MEMO)
|
||||
} else {
|
||||
memo.copy(id = StellarMemo.Id(id))
|
||||
}
|
||||
memo = when (memo.selectedMemoType) {
|
||||
XlmMemoType.TEXT -> {
|
||||
if (XlmMemoState.isAssignableValue(action.data)) {
|
||||
memo.copy(text = StellarMemo.Text(action.data))
|
||||
} else {
|
||||
memo
|
||||
memo.copy(error = TransactionExtraError.INVALID_XLM_MEMO)
|
||||
}
|
||||
}
|
||||
XlmMemoType.ID -> {
|
||||
if (XlmMemoState.isAssignableValue(action.data)) {
|
||||
memo.copy(id = StellarMemo.Id(action.data.toBigInteger()))
|
||||
} else {
|
||||
memo.copy(error = TransactionExtraError.INVALID_XLM_MEMO)
|
||||
}
|
||||
}
|
||||
null -> memo
|
||||
}
|
||||
infoState.copy(xlmMemo = memo)
|
||||
}
|
||||
|
|
@ -117,16 +119,16 @@ class TransactionExtrasReducer : SendInternalReducer {
|
|||
}
|
||||
|
||||
private fun handleXrpTag(
|
||||
action: XrpDestinationTag,
|
||||
sendState: SendState,
|
||||
infoState: TransactionExtrasState,
|
||||
action: XrpDestinationTag,
|
||||
sendState: SendState,
|
||||
infoState: TransactionExtrasState,
|
||||
): SendState {
|
||||
val result = when (action) {
|
||||
is XrpDestinationTag.HandleUserInput -> {
|
||||
val tag = action.data.toLongOrNull()
|
||||
if (tag != null) {
|
||||
val input = InputViewValue(action.data, true)
|
||||
val tagState = if (tag <= XrpDestinationTagState.MAX_NUMBER){
|
||||
val tagState = if (tag <= XrpDestinationTagState.MAX_NUMBER) {
|
||||
XrpDestinationTagState(input, tag)
|
||||
} else {
|
||||
XrpDestinationTagState(input, error = TransactionExtraError.INVALID_DESTINATION_TAG)
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
package com.tangem.tap.features.send.redux.states
|
||||
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import com.tangem.blockchain.blockchains.stellar.StellarMemo
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction
|
||||
import java.math.BigInteger
|
||||
|
||||
data class AddressPayIdState(
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val normalFieldValue: String? = null,
|
||||
val truncatedFieldValue: String? = null,
|
||||
val destinationWalletAddress: String? = null,
|
||||
val error: AddressPayIdVerifyAction.Error? = null,
|
||||
val truncateHandler: ((String) -> String)? = null,
|
||||
val sendingToPayIdEnabled: Boolean = false,
|
||||
val pasteIsEnabled: Boolean = false,
|
||||
val inputIsEnabled: Boolean = true
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val normalFieldValue: String? = null,
|
||||
val truncatedFieldValue: String? = null,
|
||||
val destinationWalletAddress: String? = null,
|
||||
val error: AddressPayIdVerifyAction.Error? = null,
|
||||
val truncateHandler: ((String) -> String)? = null,
|
||||
val sendingToPayIdEnabled: Boolean = false,
|
||||
val pasteIsEnabled: Boolean = false,
|
||||
val inputIsEnabled: Boolean = true,
|
||||
) : SendScreenState {
|
||||
|
||||
override val stateId: StateId = StateId.ADDRESS_PAY_ID
|
||||
|
|
@ -26,9 +27,9 @@ data class AddressPayIdState(
|
|||
}
|
||||
|
||||
data class TransactionExtrasState(
|
||||
val xlmMemo: XlmMemoState? = null,
|
||||
val binanceMemo: BinanceMemoState? = null,
|
||||
val xrpDestinationTag: XrpDestinationTagState? = null
|
||||
val xlmMemo: XlmMemoState? = null,
|
||||
val binanceMemo: BinanceMemoState? = null,
|
||||
val xrpDestinationTag: XrpDestinationTagState? = null,
|
||||
) : IdStateHolder {
|
||||
override val stateId: StateId = StateId.TRANSACTION_EXTRAS
|
||||
}
|
||||
|
|
@ -38,11 +39,10 @@ enum class XlmMemoType {
|
|||
}
|
||||
|
||||
data class XlmMemoState(
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val selectedMemoType: XlmMemoType = XlmMemoType.ID,
|
||||
val text: StellarMemo.Text? = null,
|
||||
val id: StellarMemo.Id? = null,
|
||||
val error: TransactionExtraError? = null,
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val text: StellarMemo.Text? = null,
|
||||
val id: StellarMemo.Id? = null,
|
||||
val error: TransactionExtraError? = null,
|
||||
) {
|
||||
val memo: StellarMemo?
|
||||
get() = when (selectedMemoType) {
|
||||
|
|
@ -50,16 +50,37 @@ data class XlmMemoState(
|
|||
XlmMemoType.ID -> id
|
||||
}
|
||||
|
||||
val selectedMemoType: XlmMemoType
|
||||
get() = determineMemoType(viewFieldValue.value)
|
||||
|
||||
companion object {
|
||||
val MAX_NUMBER: BigInteger = BigInteger("FFFFFFFFFFFFFFFF", 16)
|
||||
fun determineMemoType(value: String): XlmMemoType = when {
|
||||
value.isNotEmpty() && value.isDigitsOnly() -> XlmMemoType.ID
|
||||
else -> XlmMemoType.TEXT
|
||||
}
|
||||
|
||||
fun isAssignableValue(value: String): Boolean = when (determineMemoType(value)) {
|
||||
XlmMemoType.TEXT -> {
|
||||
// from org.stellar.sdk.MemoText
|
||||
value.toByteArray().size <= 28
|
||||
}
|
||||
XlmMemoType.ID -> {
|
||||
try {
|
||||
// from com.tangem.blockchain.blockchains.stellar.StellarMemo.toStellarSdkMemo
|
||||
value.toBigInteger() in BigInteger.ZERO..(Long.MAX_VALUE.toBigInteger() * 2.toBigInteger())
|
||||
} catch (ex: NumberFormatException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class BinanceMemoState(
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val memo: BigInteger? = null,
|
||||
val error: TransactionExtraError? = null
|
||||
) {
|
||||
val error: TransactionExtraError? = null,
|
||||
) {
|
||||
companion object {
|
||||
val MAX_NUMBER: BigInteger = BigInteger("FFFFFFFFFFFFFFFF", 16)
|
||||
}
|
||||
|
|
@ -67,9 +88,9 @@ data class BinanceMemoState(
|
|||
|
||||
// tag must contains only digits
|
||||
data class XrpDestinationTagState(
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val tag: Long? = null,
|
||||
val error: TransactionExtraError? = null
|
||||
val viewFieldValue: InputViewValue = InputViewValue(""),
|
||||
val tag: Long? = null,
|
||||
val error: TransactionExtraError? = null,
|
||||
) {
|
||||
companion object {
|
||||
const val MAX_NUMBER: Long = 4294967295
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.features.send.redux.states
|
||||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.tap.features.send.redux.FeeAction
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
|
|
@ -11,26 +10,21 @@ enum class FeeType {
|
|||
SINGLE, LOW, NORMAL, PRIORITY
|
||||
}
|
||||
|
||||
enum class FeePrecision(val symbol: String) {
|
||||
PRECISE(""), CAN_BE_LOWER("<")
|
||||
}
|
||||
|
||||
data class FeeState(
|
||||
val selectedFeeType: FeeType = FeeType.NORMAL,
|
||||
val feeList: List<Amount>? = null,
|
||||
val currentFee: Amount? = null,
|
||||
val feeIsIncluded: Boolean = false,
|
||||
val mainLayoutIsVisible: Boolean = false,
|
||||
val controlsLayoutIsVisible: Boolean = false,
|
||||
val feeChipGroupIsVisible: Boolean = true,
|
||||
val includeFeeSwitcherIsEnabled: Boolean = true,
|
||||
val error: FeeAction.Error? = null,
|
||||
val feePrecision: FeePrecision = FeePrecision.PRECISE
|
||||
val selectedFeeType: FeeType = FeeType.NORMAL,
|
||||
val feeList: List<Amount>? = null,
|
||||
val currentFee: Amount? = null,
|
||||
val feeIsIncluded: Boolean = false,
|
||||
val feeIsApproximate: Boolean = false,
|
||||
val mainLayoutIsVisible: Boolean = false,
|
||||
val controlsLayoutIsVisible: Boolean = false,
|
||||
val feeChipGroupIsVisible: Boolean = true,
|
||||
val includeFeeSwitcherIsEnabled: Boolean = true,
|
||||
) : SendScreenState {
|
||||
|
||||
override val stateId: StateId = StateId.FEE
|
||||
|
||||
fun isReady(): Boolean = error == null && currentFee != null
|
||||
fun isReady(): Boolean = currentFee != null
|
||||
|
||||
fun getCurrentFeeValue(): BigDecimal = currentFee?.value ?: BigDecimal.ZERO
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.tap.features.send.ui.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.tangem.tap.common.feedback.SendTransactionFailedEmail
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class RequestFeeErrorDialog {
|
||||
|
||||
companion object {
|
||||
fun create(context: Context, dialog: SendAction.Dialog.RequestFeeError): AlertDialog {
|
||||
val errorMessage = dialog.error.customMessage
|
||||
|
||||
return AlertDialog.Builder(context).apply {
|
||||
setTitle(R.string.send_error_fee_request_failed)
|
||||
setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage))
|
||||
setNegativeButton(R.string.alert_button_send_feedback) { _, _ ->
|
||||
store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage)))
|
||||
}
|
||||
setPositiveButton(R.string.common_retry) { _, _ -> dialog.onRetry() }
|
||||
setNeutralButton(R.string.common_cancel) { _, _ -> }
|
||||
setOnDismissListener { store.dispatch(SendAction.Dialog.Hide) }
|
||||
}.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,10 @@ package com.tangem.tap.features.send.ui.dialogs
|
|||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.common.module.ModuleMessageConverter
|
||||
import com.tangem.tangem_sdk_new.extensions.localizedDescription
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.feedback.SendTransactionFailedEmail
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
|
|
@ -13,14 +16,14 @@ import com.tangem.wallet.R
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SendTransactionFailsDialog {
|
||||
|
||||
companion object {
|
||||
fun create(context: Context, dialog: SendAction.Dialog.SendTransactionFails.CardSdkError): AlertDialog {
|
||||
return create(context, dialog.error.localizedDescription(context))
|
||||
}
|
||||
|
||||
fun create(context: Context, dialog: SendAction.Dialog.SendTransactionFails.BlockchainSdkError): AlertDialog {
|
||||
return create(context, dialog.error.customMessage)
|
||||
val errorConverter = BlockchainSdkErrorConverter(context)
|
||||
return create(context, errorConverter.convert(dialog.error))
|
||||
}
|
||||
|
||||
private fun create(context: Context, errorMessage: String): AlertDialog {
|
||||
|
|
@ -35,4 +38,20 @@ class SendTransactionFailsDialog {
|
|||
}.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BlockchainSdkErrorConverter(
|
||||
private val context: Context,
|
||||
) : ModuleMessageConverter<BlockchainSdkError, String> {
|
||||
|
||||
override fun convert(message: BlockchainSdkError): String {
|
||||
return when (message) {
|
||||
is BlockchainSdkError.CreateAccountUnderfunded -> {
|
||||
val reserve = message.minReserve.value?.stripZeroPlainString() ?: "0"
|
||||
val symbol = message.minReserve.currencySymbol
|
||||
context.getString(R.string.send_error_no_target_account, reserve, symbol)
|
||||
}
|
||||
else -> message.customMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.features.send.ui.stateSubscribers
|
|||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.text.InputType
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
|
@ -20,7 +19,6 @@ import com.tangem.tap.domain.MultiMessageError
|
|||
import com.tangem.tap.domain.assembleErrors
|
||||
import com.tangem.tap.features.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.SendAction
|
||||
import com.tangem.tap.features.send.redux.states.AddressPayIdState
|
||||
import com.tangem.tap.features.send.redux.states.AmountState
|
||||
|
|
@ -32,15 +30,14 @@ import com.tangem.tap.features.send.redux.states.SendState
|
|||
import com.tangem.tap.features.send.redux.states.StateId
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtraError
|
||||
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
|
||||
import com.tangem.tap.features.send.redux.states.XlmMemoType
|
||||
import com.tangem.tap.features.send.ui.FeeUiHelper
|
||||
import com.tangem.tap.features.send.ui.SendFragment
|
||||
import com.tangem.tap.features.send.ui.dialogs.RequestFeeErrorDialog
|
||||
import com.tangem.tap.features.send.ui.dialogs.SendTransactionFailsDialog
|
||||
import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog
|
||||
import com.tangem.tap.features.wallet.redux.WalletState.Companion.ROUGH_SIGN
|
||||
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
|
||||
import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
|
|
@ -62,10 +59,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
|
|||
when (it) {
|
||||
StateId.SEND_SCREEN -> handleSendScreen(fg, state)
|
||||
StateId.ADDRESS_PAY_ID -> handleAddressPayIdState(fg, state.addressPayIdState)
|
||||
StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState(
|
||||
fg,
|
||||
state.transactionExtrasState
|
||||
)
|
||||
StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState(fg, state.transactionExtrasState)
|
||||
StateId.AMOUNT -> handleAmountState(fg, state.amountState)
|
||||
StateId.FEE -> handleFeeState(fg, state.feeState)
|
||||
StateId.RECEIPT -> handleReceiptState(fg, state.receiptState)
|
||||
|
|
@ -84,14 +78,10 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
|
|||
showView(binanceMemoContainer, infoState.binanceMemo)
|
||||
|
||||
infoState.xlmMemo?.let {
|
||||
etXlmMemo.inputType = when (it.selectedMemoType) {
|
||||
XlmMemoType.TEXT -> InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
|
||||
XlmMemoType.ID -> InputType.TYPE_CLASS_NUMBER
|
||||
}
|
||||
if (!it.viewFieldValue.isFromUserInput) etXlmMemo.setText(it.viewFieldValue.value)
|
||||
if (it.error != null) {
|
||||
if (it.error == TransactionExtraError.INVALID_XLM_MEMO) {
|
||||
tilXlmMemo.error = fg.getText(R.string.send_error_invalid_memo_id)
|
||||
tilXlmMemo.error = fg.getText(R.string.send_error_invalid_memo)
|
||||
}
|
||||
} else {
|
||||
tilXlmMemo.error = null
|
||||
|
|
@ -113,7 +103,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
|
|||
infoState.binanceMemo?.let {
|
||||
if (infoState.binanceMemo.error != null) {
|
||||
if (infoState.binanceMemo.error == TransactionExtraError.INVALID_BINANCE_MEMO) {
|
||||
tilBinanceMemo.error = fg.getText(R.string.send_error_invalid_memo_id)
|
||||
tilBinanceMemo.error = fg.getText(R.string.send_error_invalid_memo)
|
||||
}
|
||||
} else {
|
||||
tilBinanceMemo.error = null
|
||||
|
|
@ -144,6 +134,12 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
|
|||
dialog?.show()
|
||||
}
|
||||
}
|
||||
is SendAction.Dialog.RequestFeeError -> {
|
||||
if (dialog == null) {
|
||||
dialog = RequestFeeErrorDialog.create(fg.requireContext(), state.dialog)
|
||||
dialog?.show()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
dialog?.dismiss()
|
||||
dialog = null
|
||||
|
|
@ -161,7 +157,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
|
|||
|
||||
toolbar.title = fg.getString(
|
||||
R.string.send_title_currency_format,
|
||||
state.amountState.mainCurrency.currencySymbol
|
||||
state.amountState.mainCurrency.currencySymbol,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -233,11 +229,11 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
|
|||
val balanceText = when (state.mainCurrency.type) {
|
||||
MainCurrencyType.FIAT -> fg.getString(
|
||||
R.string.send_balance_subtitle_format,
|
||||
state.viewBalanceValue, state.mainCurrency.currencySymbol
|
||||
state.viewBalanceValue, state.mainCurrency.currencySymbol,
|
||||
).remove(":")
|
||||
MainCurrencyType.CRYPTO -> fg.getString(
|
||||
R.string.send_balance_subtitle_format,
|
||||
state.mainCurrency.currencySymbol, state.viewBalanceValue
|
||||
state.mainCurrency.currencySymbol, state.viewBalanceValue,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -263,14 +259,6 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
|
|||
swIncludeFee.isChecked = state.feeIsIncluded
|
||||
}
|
||||
|
||||
if (state.error == FeeAction.Error.REQUEST_FAILED) {
|
||||
fg.showRetrySnackbar(
|
||||
fg.requireContext().getString(R.string.send_error_fee_request_failed)
|
||||
) {
|
||||
store.dispatch(FeeAction.RequestFee)
|
||||
}
|
||||
}
|
||||
|
||||
val chipId = FeeUiHelper.toId(state.selectedFeeType)
|
||||
if (chipGroup.checkedChipId != chipId && chipId != View.NO_ID) chipGroup.check(chipId)
|
||||
}
|
||||
|
|
@ -299,7 +287,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
|
|||
|
||||
val willSent = getString(
|
||||
R.string.send_total_subtitle_format,
|
||||
receipt.willSentCrypto, receipt.symbols.crypto
|
||||
receipt.willSentCrypto, receipt.symbols.crypto,
|
||||
)
|
||||
llTotalContainer.tvWillBeSentValue.update(willSent)
|
||||
|
||||
|
|
@ -334,7 +322,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
|
|||
val willSent = getString(
|
||||
R.string.send_total_subtitle_asset_format,
|
||||
receipt.symbols.token ?: "", receipt.willSentToken,
|
||||
receipt.symbols.crypto, receipt.willSentFeeCoin
|
||||
receipt.symbols.crypto, receipt.willSentFeeCoin,
|
||||
)
|
||||
llTotalContainer.tvWillBeSentValue.update(willSent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
package com.tangem.tap.features.wallet.models
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionStatus
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.blockchain.extensions.isAboveZero
|
||||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -10,8 +15,8 @@ data class PendingTransaction(
|
|||
val type: PendingTransactionType,
|
||||
) {
|
||||
val address: String? = when (type) {
|
||||
PendingTransactionType.Incoming -> transactionData.sourceAddress
|
||||
PendingTransactionType.Outgoing -> transactionData.destinationAddress
|
||||
PendingTransactionType.Incoming -> nullIfUnknown(transactionData.sourceAddress)
|
||||
PendingTransactionType.Outgoing -> nullIfUnknown(transactionData.destinationAddress)
|
||||
PendingTransactionType.Unknown -> null
|
||||
}
|
||||
|
||||
|
|
@ -20,6 +25,8 @@ data class PendingTransaction(
|
|||
val amountValueUi: String? = amountValue?.toFormattedString(transactionData.amount.decimals)
|
||||
|
||||
val currency: String = transactionData.amount.currencySymbol
|
||||
|
||||
fun nullIfUnknown(address: String):String? = if (address == "unknown") null else address
|
||||
}
|
||||
|
||||
enum class PendingTransactionType { Incoming, Outgoing, Unknown }
|
||||
|
|
|
|||
|
|
@ -3,6 +3,12 @@ package com.tangem.tap.features.wallet.models
|
|||
sealed class WalletWarning(
|
||||
val showingPosition: Int,
|
||||
) {
|
||||
data class ExistentialDeposit(
|
||||
val currencyName: String,
|
||||
val currencySymbols: String,
|
||||
val existentialDepositString: String,
|
||||
) : WalletWarning(1)
|
||||
|
||||
object TransactionInProgress : WalletWarning(10)
|
||||
data class BalanceNotEnoughForFee(val blockchainFullName: String) : WalletWarning(30)
|
||||
data class Rent(val walletRent: WalletRent) : WalletWarning(40)
|
||||
|
|
|
|||
|
|
@ -131,9 +131,7 @@ sealed class WalletAction : Action {
|
|||
|
||||
object Scan : WalletAction()
|
||||
|
||||
data class Send(val amount: Amount? = null) : WalletAction() {
|
||||
data class ChooseCurrency(val amounts: List<Amount>?) : WalletAction()
|
||||
}
|
||||
data class Send(val amount: Amount? = null) : WalletAction()
|
||||
|
||||
object EmptyField : WalletAction(), ErrorAction {
|
||||
override val error = TapError.PayIdEmptyField
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@ import com.tangem.blockchain.common.Wallet
|
|||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.domain.common.extensions.canHandleToken
|
||||
import com.tangem.domain.common.extensions.toCoinId
|
||||
import com.tangem.domain.features.addCustomToken.CustomCurrency
|
||||
import com.tangem.tap.common.entities.Button
|
||||
import com.tangem.tap.common.extensions.toQrCode
|
||||
import com.tangem.tap.common.redux.global.CryptoCurrencyName
|
||||
|
|
@ -17,12 +14,18 @@ import com.tangem.tap.common.toggleWidget.WidgetState
|
|||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.features.wallet.models.*
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
import com.tangem.tap.features.wallet.models.WalletRent
|
||||
import com.tangem.tap.features.wallet.models.WalletWarning
|
||||
import com.tangem.tap.features.wallet.models.hasPendingTransactions
|
||||
import com.tangem.tap.features.wallet.models.hasSendableAmounts
|
||||
import com.tangem.tap.features.wallet.models.isSendableAmount
|
||||
import com.tangem.tap.features.wallet.redux.reducers.calculateTotalFiatAmount
|
||||
import com.tangem.tap.features.wallet.redux.reducers.findProgressState
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StateType
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -48,21 +51,15 @@ data class WalletState(
|
|||
|
||||
// if you do not delegate - the application crashes on startup,
|
||||
// because twinCardsState has not been created yet
|
||||
val twinCardsState: TwinCardsState by ReadOnlyProperty<Any, TwinCardsState> { thisRef, property ->
|
||||
val twinCardsState: TwinCardsState by ReadOnlyProperty<Any, TwinCardsState> { _, _ ->
|
||||
store.state.twinCardsState
|
||||
}
|
||||
|
||||
val isTangemTwins: Boolean
|
||||
get() = store.state.globalState.scanResponse?.isTangemTwins() == true
|
||||
|
||||
val primaryWallet: WalletData? = wallets.firstOrNull()
|
||||
?.walletsData?.firstOrNull()
|
||||
val primaryWalletManager: WalletManager? =
|
||||
if (wallets.isNotEmpty()) wallets[0].walletManager else null
|
||||
|
||||
val shouldShowDetails: Boolean =
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain
|
||||
val isExchangeServiceFeatureOn: Boolean
|
||||
get() = store.state.globalState.exchangeManager.featureIsSwitchedOn()
|
||||
|
||||
val blockchains: List<Blockchain>
|
||||
get() = wallets.mapNotNull { it.walletManager?.wallet?.blockchain }
|
||||
|
|
@ -76,6 +73,14 @@ data class WalletState(
|
|||
val walletManagers: List<WalletManager>
|
||||
get() = wallets.mapNotNull { it.walletManager }
|
||||
|
||||
val primaryWallet: WalletData? = wallets.firstOrNull()?.walletsData?.firstOrNull()
|
||||
|
||||
val primaryWalletManager: WalletManager? = if (wallets.isNotEmpty()) wallets[0].walletManager else null
|
||||
|
||||
val shouldShowDetails: Boolean =
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain
|
||||
|
||||
fun getWalletManager(currency: Currency?): WalletManager? {
|
||||
if (currency?.blockchain == null) return null
|
||||
return getWalletStore(currency)?.walletManager
|
||||
|
|
@ -248,32 +253,6 @@ data class WalletState(
|
|||
return updatedWallets + remainingWallets
|
||||
}
|
||||
|
||||
fun updateTradeCryptoState(
|
||||
exchangeManager: CurrencyExchangeManager?,
|
||||
walletData: WalletData
|
||||
): WalletData {
|
||||
return walletData.copy(
|
||||
tradeCryptoState = TradeCryptoState.from(
|
||||
exchangeManager,
|
||||
walletData
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun updateTradeCryptoState(
|
||||
exchangeManager: CurrencyExchangeManager?,
|
||||
walletDataList: List<WalletData>
|
||||
): List<WalletData> {
|
||||
return walletDataList.map {
|
||||
it.copy(
|
||||
tradeCryptoState = TradeCryptoState.from(
|
||||
exchangeManager,
|
||||
it
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTotalBalance(): WalletState {
|
||||
val walletsData = this.wallets
|
||||
.flatMap(WalletStore::walletsData)
|
||||
|
|
@ -308,6 +287,7 @@ data class WalletState(
|
|||
companion object {
|
||||
const val UNKNOWN_AMOUNT_SIGN = "—"
|
||||
const val ROUGH_SIGN = "≈"
|
||||
const val CAN_BE_LOWER_SIGN = "<"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -346,29 +326,6 @@ data class Artwork(
|
|||
const val MARTA_CARD_ID = "BC02"
|
||||
const val TWIN_CARD_1 = "https://app.tangem.com/cards/card_tg085.png"
|
||||
const val TWIN_CARD_2 = "https://app.tangem.com/cards/card_tg086.png"
|
||||
|
||||
const val TEMP_CARDANO =
|
||||
"https://verify.tangem.com/card/artwork?artworkId=card_ru039&CID=CB19000000040976&publicKey=0416E29423A6CC77CD07CBA52873E8F6F894B1AFB18EB3688ACC2C8D8E5AC84B80B0BA1B17B85E578E47044CE96BCFF3FB4499FA4941CAD3C1EF300A492B5B9659"
|
||||
}
|
||||
}
|
||||
|
||||
data class TradeCryptoState(
|
||||
val isAvailableToSell: () -> Boolean = { false },
|
||||
val isAvailableToBuy: () -> Boolean = { false },
|
||||
) {
|
||||
companion object {
|
||||
fun from(
|
||||
exchangeManager: CurrencyExchangeManager?,
|
||||
walletData: WalletData
|
||||
): TradeCryptoState {
|
||||
val exchanger = exchangeManager ?: return walletData.tradeCryptoState
|
||||
val currency = walletData.currency
|
||||
|
||||
return TradeCryptoState(
|
||||
isAvailableToSell = { exchanger.availableForSell(currency) },
|
||||
isAvailableToBuy = { exchanger.availableForBuy(currency) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -378,13 +335,19 @@ data class WalletData(
|
|||
val walletAddresses: WalletAddresses? = null,
|
||||
val currencyData: BalanceWidgetData = BalanceWidgetData(),
|
||||
val updatingWallet: Boolean = false,
|
||||
val tradeCryptoState: TradeCryptoState = TradeCryptoState(),
|
||||
val fiatRateString: String? = null,
|
||||
val fiatRate: BigDecimal? = null,
|
||||
val mainButton: WalletMainButton = WalletMainButton.SendButton(false),
|
||||
val currency: Currency,
|
||||
val walletRent: WalletRent? = null,
|
||||
val existentialDepositString: String? = null,
|
||||
) {
|
||||
val isAvailableToBuy: Boolean
|
||||
get() = store.state.globalState.exchangeManager.availableForBuy(currency)
|
||||
|
||||
val isAvailableToSell: Boolean
|
||||
get() = store.state.globalState.exchangeManager.availableForSell(currency)
|
||||
|
||||
fun shouldShowMultipleAddress(): Boolean {
|
||||
val listOfAddresses = walletAddresses?.list ?: return false
|
||||
return listOfAddresses.size > 1
|
||||
|
|
@ -398,18 +361,42 @@ data class WalletData(
|
|||
|
||||
fun assembleWarnings(): List<WalletWarning> {
|
||||
val walletWarnings = mutableListOf<WalletWarning>()
|
||||
assembleNonTypedWarnings(walletWarnings)
|
||||
assembleBlockchainWarnings(walletWarnings)
|
||||
assembleTokenWarnings(walletWarnings)
|
||||
|
||||
return walletWarnings.sortedBy { it.showingPosition }
|
||||
}
|
||||
|
||||
private fun assembleNonTypedWarnings(walletWarnings: MutableList<WalletWarning>) {
|
||||
if (currencyData.status == BalanceStatus.SameCurrencyTransactionInProgress) {
|
||||
walletWarnings.add(WalletWarning.TransactionInProgress)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assembleBlockchainWarnings(walletWarnings: MutableList<WalletWarning>) {
|
||||
if (!currency.isBlockchain()) return
|
||||
|
||||
if (existentialDepositString != null) {
|
||||
val warning = WalletWarning.ExistentialDeposit(
|
||||
currencyName = currency.currencyName,
|
||||
currencySymbols = currency.currencySymbol,
|
||||
existentialDepositString = existentialDepositString,
|
||||
)
|
||||
walletWarnings.add(warning)
|
||||
}
|
||||
if (walletRent != null) {
|
||||
walletWarnings.add(WalletWarning.Rent(walletRent))
|
||||
}
|
||||
if (!currency.isBlockchain() && (blockchainAmountIsEmpty() && !tokenAmountIsEmpty())) {
|
||||
val fullName = currency.blockchain.fullName
|
||||
walletWarnings.add(WalletWarning.BalanceNotEnoughForFee(fullName))
|
||||
}
|
||||
}
|
||||
|
||||
return walletWarnings.sortedBy { it.showingPosition }
|
||||
private fun assembleTokenWarnings(walletWarnings: MutableList<WalletWarning>){
|
||||
if (!currency.isToken()) return
|
||||
|
||||
val blockchainFullName = currency.blockchain.fullName
|
||||
if ((blockchainAmountIsEmpty() && !tokenAmountIsEmpty())) {
|
||||
walletWarnings.add(WalletWarning.BalanceNotEnoughForFee(blockchainFullName))
|
||||
}
|
||||
}
|
||||
|
||||
private fun blockchainAmountIsEmpty(): Boolean = currencyData.blockchainAmount?.isZero() == true
|
||||
|
|
|
|||
|
|
@ -37,20 +37,18 @@ class TradeCryptoMiddleware {
|
|||
action: WalletAction.TradeCryptoAction.Buy,
|
||||
) {
|
||||
if (action.checkUserLocation && state()?.globalState?.userCountryCode == RUSSIA_COUNTRY_CODE) {
|
||||
store.dispatchOnMain(
|
||||
WalletAction.DialogAction.RussianCardholdersWarningDialog
|
||||
)
|
||||
store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog)
|
||||
return
|
||||
}
|
||||
|
||||
val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return
|
||||
val exchangeManager = store.state.globalState.exchangeManager ?: return
|
||||
val card = store.state.globalState.scanResponse?.card ?: return
|
||||
val appCurrency = store.state.globalState.appCurrency
|
||||
|
||||
val addresses = selectedWalletData.walletAddresses?.list.orEmpty()
|
||||
if (addresses.isEmpty()) return
|
||||
|
||||
val exchangeManager = store.state.globalState.exchangeManager
|
||||
val appCurrency = store.state.globalState.appCurrency
|
||||
val currency = selectedWalletData.currency
|
||||
|
||||
if (currency is Currency.Token && currency.blockchain.isTestnet()) {
|
||||
|
|
@ -81,15 +79,13 @@ class TradeCryptoMiddleware {
|
|||
|
||||
private fun proceedSellAction() {
|
||||
val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return
|
||||
val exchangeManager = store.state.globalState.exchangeManager ?: return
|
||||
val appCurrency = store.state.globalState.appCurrency
|
||||
|
||||
val appCurrency = store.state.globalState.appCurrency
|
||||
val addresses = selectedWalletData.walletAddresses?.list.orEmpty()
|
||||
if (addresses.isEmpty()) return
|
||||
|
||||
val currency = selectedWalletData.currency
|
||||
|
||||
exchangeManager.getUrl(
|
||||
store.state.globalState.exchangeManager.getUrl(
|
||||
action = CurrencyExchangeManager.Action.Sell,
|
||||
blockchain = currency.blockchain,
|
||||
cryptoCurrencyName = currency.currencySymbol,
|
||||
|
|
@ -100,8 +96,8 @@ class TradeCryptoMiddleware {
|
|||
|
||||
private fun preconfigureAndOpenSendScreen(action: WalletAction.TradeCryptoAction.SendCrypto) {
|
||||
val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return
|
||||
val walletManager =
|
||||
store.state.walletState.getWalletManager(selectedWalletData.currency)
|
||||
|
||||
val walletManager = store.state.walletState.getWalletManager(selectedWalletData.currency)
|
||||
store.dispatchOnMain(PrepareSendScreen(
|
||||
coinAmount = walletManager?.wallet?.amounts?.get(AmountType.Coin),
|
||||
coinRate = selectedWalletData.fiatRate,
|
||||
|
|
@ -116,11 +112,10 @@ class TradeCryptoMiddleware {
|
|||
}
|
||||
|
||||
private fun openReceiptUrl(transactionId: String) {
|
||||
val exchangeManager = store.state.globalState.exchangeManager ?: return
|
||||
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
exchangeManager.getSellCryptoReceiptUrl(CurrencyExchangeManager.Action.Sell, transactionId)?.let {
|
||||
store.dispatchOnMain(NavigationAction.OpenUrl(it))
|
||||
}
|
||||
store.state.globalState.exchangeManager.getSellCryptoReceiptUrl(
|
||||
action = CurrencyExchangeManager.Action.Sell,
|
||||
transactionId = transactionId,
|
||||
)?.let { store.dispatchOnMain(NavigationAction.OpenUrl(it)) }
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,13 @@ import com.tangem.domain.common.extensions.withMainContext
|
|||
import com.tangem.operations.attestation.Attestation
|
||||
import com.tangem.operations.attestation.OnlineCardVerifier
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.extensions.copyToClipboard
|
||||
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.extensions.shareText
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
|
|
@ -22,11 +28,11 @@ import com.tangem.tap.domain.loadedRates
|
|||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.PendingTransactionType
|
||||
import com.tangem.tap.features.wallet.models.filterByCoin
|
||||
import com.tangem.tap.features.wallet.models.getPendingTransactions
|
||||
import com.tangem.tap.features.wallet.models.getSendableAmounts
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
|
|
@ -36,7 +42,6 @@ import com.tangem.tap.preferencesStorage
|
|||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import java.math.BigDecimal
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -44,6 +49,7 @@ import org.rekotlin.Action
|
|||
import org.rekotlin.DispatchFunction
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
class WalletMiddleware {
|
||||
private val tradeCryptoMiddleware = TradeCryptoMiddleware()
|
||||
|
|
@ -276,7 +282,7 @@ class WalletMiddleware {
|
|||
}
|
||||
} else {
|
||||
if (amounts?.size ?: 0 > 1) {
|
||||
WalletAction.Send.ChooseCurrency(amounts)
|
||||
WalletAction.DialogAction.ChooseCurrency(amounts)
|
||||
} else {
|
||||
val amountToSend = amounts?.first()
|
||||
PrepareSendScreen(
|
||||
|
|
|
|||
|
|
@ -1,15 +1,25 @@
|
|||
package com.tangem.tap.features.wallet.redux.reducers
|
||||
|
||||
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.tap.common.extensions.toFiatString
|
||||
import com.tangem.tap.common.extensions.toFormattedCurrencyString
|
||||
import com.tangem.tap.domain.getFirstToken
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.*
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.WalletRent
|
||||
import com.tangem.tap.features.wallet.models.filterByToken
|
||||
import com.tangem.tap.features.wallet.models.getPendingTransactions
|
||||
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletMainButton
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
|
||||
import com.tangem.tap.features.wallet.redux.WalletStore
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.features.wallet.ui.TokenData
|
||||
|
|
@ -25,6 +35,7 @@ class MultiWalletReducer {
|
|||
it.wallet.blockchain == blockchain.blockchain &&
|
||||
(it.wallet.publicKey.derivationPath?.rawPath == blockchain.derivationPath)
|
||||
} ?: return@mapNotNull null
|
||||
|
||||
val wallet = walletManager.wallet
|
||||
val cardToken = if (!state.isMultiwalletAllowed) {
|
||||
wallet.getFirstToken()?.symbol?.let { TokenData("", tokenSymbol = it) }
|
||||
|
|
@ -44,6 +55,7 @@ class MultiWalletReducer {
|
|||
blockchain.blockchain,
|
||||
blockchain.derivationPath
|
||||
),
|
||||
existentialDepositString = getExistentialDeposit(walletManager),
|
||||
)
|
||||
|
||||
WalletStore(
|
||||
|
|
@ -79,6 +91,7 @@ class MultiWalletReducer {
|
|||
action.blockchain.blockchain,
|
||||
action.blockchain.derivationPath
|
||||
),
|
||||
existentialDepositString = getExistentialDeposit(walletManager),
|
||||
)
|
||||
val walletStore = WalletStore(
|
||||
walletManager = walletManager,
|
||||
|
|
@ -166,6 +179,10 @@ class MultiWalletReducer {
|
|||
it.walletRent != null
|
||||
}?.walletRent
|
||||
}
|
||||
|
||||
private fun getExistentialDeposit(walletManager: WalletManager?): String? {
|
||||
return (walletManager as? ExistentialDepositProvider)?.getExistentialDeposit()?.toPlainString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addTokens(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ import com.tangem.tap.features.wallet.models.Currency
|
|||
import com.tangem.tap.features.wallet.models.filterByToken
|
||||
import com.tangem.tap.features.wallet.models.getPendingTransactions
|
||||
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletMainButton
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
|
|
@ -38,8 +40,6 @@ class OnWalletLoadedReducer {
|
|||
val walletData = walletState.getWalletData(blockchainNetwork) ?: return walletState
|
||||
|
||||
val fiatCurrency = store.state.globalState.appCurrency
|
||||
val exchangeManager = store.state.globalState.exchangeManager
|
||||
|
||||
val coinAmountValue = wallet.amounts[AmountType.Coin]?.value
|
||||
val formattedAmount = coinAmountValue?.toFormattedCurrencyString(
|
||||
wallet.blockchain.decimals(),
|
||||
|
|
@ -71,7 +71,6 @@ class OnWalletLoadedReducer {
|
|||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(isCoinSendButtonEnabled),
|
||||
currency = Currency.fromBlockchainNetwork(blockchainNetwork),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletData),
|
||||
)
|
||||
|
||||
val tokens = wallet.getTokens().mapNotNull { token ->
|
||||
|
|
@ -104,7 +103,6 @@ class OnWalletLoadedReducer {
|
|||
),
|
||||
pendingTransactions = tokenPendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(isTokenSendButtonEnabled),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, tokenWalletData),
|
||||
)
|
||||
}
|
||||
val newWallets = tokens + newWalletData
|
||||
|
|
@ -118,8 +116,6 @@ class OnWalletLoadedReducer {
|
|||
if (wallet.blockchain != walletState.primaryBlockchain) return walletState
|
||||
|
||||
val fiatCurrencyName = store.state.globalState.appCurrency.code
|
||||
val exchangeManager = store.state.globalState.exchangeManager
|
||||
|
||||
val token = wallet.getFirstToken()
|
||||
val tokenData = if (token != null) {
|
||||
val tokenAmount = wallet.getTokenAmount(token)
|
||||
|
|
@ -167,7 +163,6 @@ class OnWalletLoadedReducer {
|
|||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletState.primaryWallet),
|
||||
)
|
||||
val wallets = listOfNotNull(walletData)
|
||||
val updatedStore = walletState.getWalletStore(walletData?.currency)?.updateWallets(wallets)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,11 @@ import com.tangem.blockchain.common.Wallet
|
|||
import com.tangem.common.extensions.mapNotNullValues
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.extensions.toFiatRateString
|
||||
import com.tangem.tap.common.extensions.toFiatString
|
||||
import com.tangem.tap.common.extensions.toFiatValue
|
||||
import com.tangem.tap.common.extensions.toFormattedCurrencyString
|
||||
import com.tangem.tap.common.extensions.toFormattedFiatValue
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.getArtworkUrl
|
||||
|
|
@ -14,10 +18,18 @@ import com.tangem.tap.domain.getFirstToken
|
|||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.WalletRent
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import com.tangem.tap.features.wallet.redux.ErrorType
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAddresses
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletMainButton
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.WalletStore
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.Action
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -35,7 +47,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
|
||||
if (action !is WalletAction) return state.walletState
|
||||
|
||||
val exchangeManager = store.state.globalState.exchangeManager
|
||||
var newState = state.walletState
|
||||
|
||||
when (action) {
|
||||
|
|
@ -144,10 +155,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
currencySymbol = walletData.currencyData.currencySymbol,
|
||||
),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
tradeCryptoState = TradeCryptoState.from(
|
||||
exchangeManager,
|
||||
walletData
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
@ -171,13 +178,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
currencySymbol = wallet.currencyData.currencySymbol,
|
||||
),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, wallet)
|
||||
)
|
||||
}
|
||||
val wallets = newState.updateTradeCryptoState(
|
||||
exchangeManager,
|
||||
newState.replaceSomeWallets(newWallets)
|
||||
)
|
||||
val wallets = newState.replaceSomeWallets(newWallets)
|
||||
val walletStore = newState.getWalletStore(action.blockchain)?.updateWallets(wallets)
|
||||
newState = newState.updateWalletStore(walletStore)
|
||||
}
|
||||
|
|
@ -210,14 +213,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
)
|
||||
)
|
||||
}
|
||||
var updatedWalletStore = newState.getWalletStore(action.blockchain)
|
||||
val updatedWalletStore = newState.getWalletStore(action.blockchain)
|
||||
?.updateWallets(listOfNotNull(walletData))
|
||||
|
||||
updatedWalletStore =
|
||||
updatedWalletStore?.updateWallets(
|
||||
newState.updateTradeCryptoState(exchangeManager, updatedWalletStore.walletsData)
|
||||
)
|
||||
|
||||
newState = newState.updateWalletStore(updatedWalletStore)
|
||||
}
|
||||
|
||||
|
|
@ -248,11 +246,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
)
|
||||
)
|
||||
}
|
||||
val updatedWallets =
|
||||
newState.updateTradeCryptoState(
|
||||
exchangeManager,
|
||||
walletStore!!.updateWallets(listOfNotNull(newWalletData) + tokenWallets).walletsData
|
||||
)
|
||||
val updatedWallets = walletStore!!.updateWallets(listOfNotNull(newWalletData) + tokenWallets).walletsData
|
||||
|
||||
newState = newState.updateWalletsData(updatedWallets)
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import com.tangem.tap.features.wallet.redux.WalletState
|
|||
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
|
||||
import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
|
||||
import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter
|
||||
import com.tangem.tap.features.wallet.ui.images.load
|
||||
import com.tangem.tap.features.wallet.ui.test.TestWalletDetails
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -140,7 +141,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
setupAddressCard(selectedWallet)
|
||||
setupNoInternetHandling(state)
|
||||
setupBalanceData(selectedWallet.currencyData)
|
||||
setupButtons(selectedWallet)
|
||||
setupButtons(selectedWallet, state.isExchangeServiceFeatureOn)
|
||||
|
||||
handleCurrencyIcon(selectedWallet)
|
||||
handleWarnings(selectedWallet)
|
||||
|
|
@ -186,7 +187,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
)
|
||||
}
|
||||
|
||||
private fun setupButtons(selectedWallet: WalletData) = with(binding) {
|
||||
private fun setupButtons(selectedWallet: WalletData, isExchangeServiceFeatureOn: Boolean) = with(binding) {
|
||||
lWalletDetails.btnCopy.setOnClickListener {
|
||||
selectedWallet.walletAddresses?.selectedAddress?.address?.let { addressString ->
|
||||
store.dispatch(WalletAction.CopyAddress(addressString, requireContext()))
|
||||
|
|
@ -199,8 +200,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
}
|
||||
|
||||
rowButtons.updateButtonsVisibility(
|
||||
buyAllowed = selectedWallet.tradeCryptoState.isAvailableToBuy(),
|
||||
sellAllowed = selectedWallet.tradeCryptoState.isAvailableToSell(),
|
||||
exchangeServiceFeatureOn = isExchangeServiceFeatureOn,
|
||||
buyAllowed = selectedWallet.isAvailableToBuy,
|
||||
sellAllowed = selectedWallet.isAvailableToSell,
|
||||
sendAllowed = selectedWallet.mainButton.enabled,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,16 @@ class WalletWarningConverter(
|
|||
|
||||
override fun convert(message: WalletWarning): WalletWarningDescription {
|
||||
val warningMessage = when (message) {
|
||||
is WalletWarning.ExistentialDeposit -> {
|
||||
context.getString(
|
||||
R.string.warning_existential_deposit_message,
|
||||
message.currencyName, message.currencySymbols, message.existentialDepositString,
|
||||
)
|
||||
}
|
||||
is WalletWarning.BalanceNotEnoughForFee -> {
|
||||
context.getString(
|
||||
R.string.token_details_send_blocked_fee_format,
|
||||
message.blockchainFullName, message.blockchainFullName
|
||||
message.blockchainFullName, message.blockchainFullName,
|
||||
)
|
||||
}
|
||||
WalletWarning.TransactionInProgress -> {
|
||||
|
|
@ -27,7 +33,7 @@ class WalletWarningConverter(
|
|||
is WalletWarning.Rent -> {
|
||||
context.getString(
|
||||
R.string.solana_rent_warning,
|
||||
message.walletRent.minRentValue, message.walletRent.rentExemptValue
|
||||
message.walletRent.minRentValue, message.walletRent.rentExemptValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.tangem.tap.common.extensions.show
|
|||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.images.load
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.ItemCurrencyWalletBinding
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ private const val VOYR = "VOYRME"
|
|||
|
||||
class CurrencyIconRequest(
|
||||
private val currencyImageView: ImageFilterView,
|
||||
private val currencyTextView: TextView,
|
||||
private val currencyTextView: TextView?,
|
||||
private val token: Token?,
|
||||
private val blockchain: Blockchain,
|
||||
) {
|
||||
|
|
@ -38,7 +38,7 @@ class CurrencyIconRequest(
|
|||
loadBlockchainIconBase(
|
||||
onStart = {
|
||||
currencyImageView.colorFilter = null
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ class CurrencyIconRequest(
|
|||
loadBlockchainIconBase(
|
||||
onStart = {
|
||||
currencyImageView.saturation = 0f
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +57,11 @@ class CurrencyIconRequest(
|
|||
},
|
||||
onSuccess = {
|
||||
currencyImageView.colorFilter = null
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
currencyImageView.setColorFilter(it.getColor())
|
||||
currencyTextView?.setTextColor(it.getTextColor())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -65,7 +69,12 @@ class CurrencyIconRequest(
|
|||
loadTokenIconBase(
|
||||
onStart = {
|
||||
currencyImageView.saturation = 0f
|
||||
}
|
||||
},
|
||||
onError = {
|
||||
currencyImageView.saturation = 0f
|
||||
currencyImageView.setColorFilter(it.getColor(true))
|
||||
currencyTextView?.setTextColor(it.getTextColor(true))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -94,15 +103,19 @@ class CurrencyIconRequest(
|
|||
data = getTokenIcon(token, blockchain),
|
||||
placeholderRes = R.drawable.shape_circle,
|
||||
onStart = {
|
||||
currencyTextView.text = token.symbol.take(1)
|
||||
currencyTextView.setTextColor(token.getTextColor())
|
||||
currencyTextView?.text = token.name.take(1)
|
||||
currencyTextView?.setTextColor(token.getTextColor())
|
||||
onStart(token)
|
||||
},
|
||||
onSuccess = {
|
||||
currencyTextView.text = null
|
||||
currencyTextView?.text = null
|
||||
onSuccess(token)
|
||||
},
|
||||
onError = { onError(token) },
|
||||
onError = {
|
||||
// for some reason the onStart doesn't call if an error occurs
|
||||
currencyTextView?.text = token.name.take(1)
|
||||
onError(token)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -122,7 +135,7 @@ private inline fun ImageView.loadIcon(
|
|||
.listener(
|
||||
onStart = { onStart() },
|
||||
onSuccess = { _, _ -> onSuccess() },
|
||||
onError = { _, _ -> onError() }
|
||||
onError = { _, _ -> onError() },
|
||||
)
|
||||
.target(imageView = this)
|
||||
.build()
|
||||
|
|
|
|||
|
|
@ -4,13 +4,11 @@ import android.content.Context
|
|||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.isVisible
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.tangem_sdk_new.extensions.dpToPx
|
||||
import com.tangem.tap.common.extensions.getRoundIconRes
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.wallet.databinding.ViewCurrencyIconBinding
|
||||
import kotlin.math.roundToInt
|
||||
|
|
@ -25,46 +23,50 @@ class CurrencyIconView @JvmOverloads constructor(
|
|||
this,
|
||||
)
|
||||
|
||||
private val currencyImageView: ImageFilterView
|
||||
val currencyImageView: ImageFilterView
|
||||
get() = binding.ivCurrency
|
||||
|
||||
private val currencyTextView: TextView
|
||||
val currencyTextView: TextView
|
||||
get() = binding.tvTokenLetter
|
||||
|
||||
private var isBlockchainIconVisible: Boolean
|
||||
get() = binding.ivBlockchain.isVisible
|
||||
set(value) = binding.ivBlockchain::isVisible.set(value)
|
||||
val blockchainBadge: ImageFilterView
|
||||
get() = binding.ivBlockchainBadge
|
||||
|
||||
private var isBadgeVisible: Boolean
|
||||
get() = binding.badge.isVisible
|
||||
set(value) = binding.badge::isVisible.set(value)
|
||||
var isBlockchainBadgeVisible: Boolean
|
||||
get() = binding.ivBlockchainBadge.isVisible
|
||||
set(value) = binding.ivBlockchainBadge::isVisible.set(value)
|
||||
|
||||
@DrawableRes
|
||||
private var blockchainIconRes: Int? = null
|
||||
set(value) {
|
||||
if (value != null && value != field && isBlockchainIconVisible) {
|
||||
binding.ivBlockchain.setImageResource(value)
|
||||
field = value
|
||||
}
|
||||
}
|
||||
var isCustomCurrencyBadgeVisible: Boolean
|
||||
get() = binding.customBadge.isVisible
|
||||
set(value) = binding.customBadge::isVisible.set(value)
|
||||
|
||||
init {
|
||||
minWidth = dpToPx(48f).roundToInt()
|
||||
minHeight = dpToPx(48f).roundToInt()
|
||||
}
|
||||
}
|
||||
|
||||
fun load(
|
||||
currency: Currency,
|
||||
derivationStyle: DerivationStyle?,
|
||||
) {
|
||||
isBlockchainIconVisible = currency.isToken()
|
||||
isBadgeVisible = currency.isCustomCurrency(derivationStyle)
|
||||
blockchainIconRes = currency.blockchain.getRoundIconRes()
|
||||
fun CurrencyIconView.load(
|
||||
currency: Currency,
|
||||
derivationStyle: DerivationStyle?,
|
||||
) {
|
||||
isCustomCurrencyBadgeVisible = currency.isCustomCurrency(derivationStyle)
|
||||
|
||||
CurrencyIconRequest(
|
||||
currencyImageView = currencyImageView,
|
||||
currencyTextView = currencyTextView,
|
||||
token = (currency as? Currency.Token)?.token,
|
||||
blockchain = currency.blockchain,
|
||||
).load()
|
||||
|
||||
if (currency.isToken()) {
|
||||
// load a blockchain icon into the blockchain badge
|
||||
isBlockchainBadgeVisible = true
|
||||
|
||||
CurrencyIconRequest(
|
||||
currencyImageView = currencyImageView,
|
||||
currencyTextView = currencyTextView,
|
||||
token = (currency as? Currency.Token)?.token,
|
||||
currencyImageView = blockchainBadge,
|
||||
currencyTextView = null,
|
||||
token = null,
|
||||
blockchain = currency.blockchain,
|
||||
).load()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,10 @@ internal class WalletDetailsButtonsRow @JvmOverloads constructor(
|
|||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private val binding = ViewWalletDetailsButtonsRowBinding.inflate(
|
||||
LayoutInflater.from(context),
|
||||
this,
|
||||
)
|
||||
|
||||
var onBuyClick: (() -> Unit)? = null
|
||||
var onSellClick: (() -> Unit)? = null
|
||||
var onTradeClick: (() -> Unit)? = null
|
||||
|
|
@ -35,10 +33,13 @@ internal class WalletDetailsButtonsRow @JvmOverloads constructor(
|
|||
}
|
||||
|
||||
fun updateButtonsVisibility(
|
||||
exchangeServiceFeatureOn: Boolean,
|
||||
buyAllowed: Boolean,
|
||||
sellAllowed: Boolean,
|
||||
sendAllowed: Boolean,
|
||||
) = with(binding) {
|
||||
containerExchangeButtons.isVisible = exchangeServiceFeatureOn
|
||||
|
||||
btnBuy.isVisible = (buyAllowed && !sellAllowed) || (!buyAllowed && !sellAllowed)
|
||||
btnBuy.isEnabled = buyAllowed
|
||||
btnSell.isVisible = !buyAllowed && sellAllowed
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class SingleWalletView : WalletView() {
|
|||
state.primaryWallet ?: return
|
||||
|
||||
setupTwinCards(state.twinCardsState, binding)
|
||||
setupButtons(state.primaryWallet, binding)
|
||||
setupButtons(state.primaryWallet, binding, state.isExchangeServiceFeatureOn)
|
||||
setupAddressCard(state.primaryWallet, binding)
|
||||
showPendingTransactionsIfPresent(state.primaryWallet.pendingTransactions)
|
||||
setupBalance(state, state.primaryWallet)
|
||||
|
|
@ -97,18 +97,23 @@ class SingleWalletView : WalletView() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupButtons(state: WalletData, binding: FragmentWalletBinding) = with(binding) {
|
||||
setupRowButtons(state, rowButtons)
|
||||
private fun setupButtons(
|
||||
walletData: WalletData,
|
||||
binding: FragmentWalletBinding,
|
||||
isExchangeServiceFeatureEnabled: Boolean,
|
||||
) = with(binding) {
|
||||
setupRowButtons(walletData, rowButtons, isExchangeServiceFeatureEnabled)
|
||||
|
||||
lAddress.btnCopy.setOnClickListener {
|
||||
state.walletAddresses?.selectedAddress?.address?.let { addressString ->
|
||||
walletData.walletAddresses?.selectedAddress?.address?.let { addressString ->
|
||||
store.dispatch(WalletAction.CopyAddress(addressString, fragment!!.requireContext()))
|
||||
}
|
||||
}
|
||||
lAddress.btnShowQr.setOnClickListener {
|
||||
state.walletAddresses?.selectedAddress?.let { selectedAddress ->
|
||||
walletData.walletAddresses?.selectedAddress?.let { selectedAddress ->
|
||||
store.dispatch(
|
||||
WalletAction.DialogAction.QrCode(
|
||||
currency = state.currency,
|
||||
currency = walletData.currency,
|
||||
selectedAddress = selectedAddress,
|
||||
),
|
||||
)
|
||||
|
|
@ -116,13 +121,16 @@ class SingleWalletView : WalletView() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupRowButtons(state: WalletData, rowButtons: WalletDetailsButtonsRow) {
|
||||
val allowedToBuy = state.tradeCryptoState.isAvailableToBuy()
|
||||
val allowedToSell = state.tradeCryptoState.isAvailableToSell()
|
||||
private fun setupRowButtons(
|
||||
walletData: WalletData,
|
||||
rowButtons: WalletDetailsButtonsRow,
|
||||
isExchangeServiceFeatureEnabled: Boolean,
|
||||
) {
|
||||
rowButtons.updateButtonsVisibility(
|
||||
buyAllowed = allowedToBuy,
|
||||
sellAllowed = allowedToSell,
|
||||
sendAllowed = state.mainButton.enabled,
|
||||
exchangeServiceFeatureOn = isExchangeServiceFeatureEnabled,
|
||||
buyAllowed = walletData.isAvailableToBuy,
|
||||
sellAllowed = walletData.isAvailableToSell,
|
||||
sendAllowed = walletData.mainButton.enabled,
|
||||
)
|
||||
|
||||
rowButtons.onBuyClick = { store.dispatch(WalletAction.TradeCryptoAction.Buy()) }
|
||||
|
|
@ -130,7 +138,7 @@ class SingleWalletView : WalletView() {
|
|||
rowButtons.onTradeClick = { store.dispatch(WalletAction.DialogAction.ChooseTradeActionDialog) }
|
||||
|
||||
rowButtons.onSendClick = {
|
||||
when (state.mainButton) {
|
||||
when (walletData.mainButton) {
|
||||
is WalletMainButton.SendButton -> store.dispatch(WalletAction.Send())
|
||||
is WalletMainButton.CreateWalletButton -> store.dispatch(WalletAction.CreateWallet)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,17 @@ class CardExchangeRules(
|
|||
val cardProvider: () -> Card?,
|
||||
) : ExchangeRules {
|
||||
|
||||
override fun featureIsSwitchedOn(): Boolean {
|
||||
val card = cardProvider() ?: return false
|
||||
|
||||
return !card.isStart2Coin
|
||||
}
|
||||
|
||||
override fun isBuyAllowed(): Boolean {
|
||||
val card = cardProvider() ?: return false
|
||||
|
||||
return when {
|
||||
card.isDemoCard() -> false
|
||||
card.isDemoCard() -> true
|
||||
card.isStart2Coin -> false
|
||||
else -> true
|
||||
}
|
||||
|
|
@ -36,7 +42,7 @@ class CardExchangeRules(
|
|||
val card = cardProvider() ?: return false
|
||||
|
||||
return when {
|
||||
card.isDemoCard() -> false
|
||||
card.isDemoCard() -> true
|
||||
card.isStart2Coin -> false
|
||||
else -> true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ class CurrencyExchangeManager(
|
|||
private val primaryRules: ExchangeRules,
|
||||
) : ExchangeService, ExchangeUrlBuilder {
|
||||
|
||||
override fun featureIsSwitchedOn(): Boolean = primaryRules.featureIsSwitchedOn()
|
||||
|
||||
override suspend fun update() {
|
||||
buyService.update()
|
||||
sellService.update()
|
||||
|
|
@ -74,6 +76,14 @@ class CurrencyExchangeManager(
|
|||
}
|
||||
|
||||
enum class Action { Buy, Sell }
|
||||
|
||||
companion object {
|
||||
fun dummy(): CurrencyExchangeManager = CurrencyExchangeManager(
|
||||
buyService = ExchangeService.dummy(),
|
||||
sellService = ExchangeService.dummy(),
|
||||
primaryRules = ExchangeRules.dummy(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun CurrencyExchangeManager.buyErc20TestnetTokens(
|
||||
|
|
|
|||
|
|
@ -1,19 +1,44 @@
|
|||
package com.tangem.tap.network.exchangeServices
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.common.feature.Feature
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
|
||||
interface ExchangeService: ExchangeRules {
|
||||
suspend fun update()
|
||||
}
|
||||
|
||||
interface ExchangeRules {
|
||||
interface Exchanger {
|
||||
fun isBuyAllowed(): Boolean
|
||||
fun isSellAllowed(): Boolean
|
||||
fun availableForBuy(currency: Currency):Boolean
|
||||
fun availableForSell(currency: Currency):Boolean
|
||||
}
|
||||
|
||||
interface ExchangeService: Feature, Exchanger {
|
||||
suspend fun update()
|
||||
|
||||
companion object {
|
||||
fun dummy(): ExchangeService = object : ExchangeService {
|
||||
override fun featureIsSwitchedOn(): Boolean = false
|
||||
override suspend fun update() {}
|
||||
override fun isBuyAllowed(): Boolean = false
|
||||
override fun isSellAllowed(): Boolean = false
|
||||
override fun availableForBuy(currency: Currency): Boolean = false
|
||||
override fun availableForSell(currency: Currency): Boolean = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ExchangeRules: Feature, Exchanger {
|
||||
|
||||
companion object {
|
||||
fun dummy(): ExchangeRules = object : ExchangeRules {
|
||||
override fun featureIsSwitchedOn(): Boolean = false
|
||||
override fun isBuyAllowed(): Boolean = false
|
||||
override fun isSellAllowed(): Boolean = false
|
||||
override fun availableForBuy(currency: Currency): Boolean = false
|
||||
override fun availableForSell(currency: Currency): Boolean = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ExchangeUrlBuilder {
|
||||
fun getUrl(
|
||||
action: CurrencyExchangeManager.Action,
|
||||
|
|
|
|||
|
|
@ -4,15 +4,6 @@ import com.squareup.moshi.Json
|
|||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
|
||||
|
||||
private val CurrenciesUrl = "https://api.mercuryo.io/v1.6/lib/currencies"
|
||||
|
||||
|
||||
|
||||
interface MercuryoApi {
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ class MercuryoService(
|
|||
private val blockchainsAvailableToBuy = mutableListOf<Blockchain>()
|
||||
private val tokensAvailableToBy = mutableMapOf<String, MutableList<Blockchain>>()
|
||||
|
||||
override fun featureIsSwitchedOn(): Boolean = true
|
||||
|
||||
override suspend fun update() {
|
||||
when (val result = performRequest { api.currencies(apiVersion) }) {
|
||||
is Result.Success -> {
|
||||
|
|
@ -130,6 +132,7 @@ class MercuryoService(
|
|||
private fun blockchainFromCurrencyName(currencyName: String): Blockchain? = when (currencyName) {
|
||||
"BNB" -> Blockchain.BSC
|
||||
"ETH" -> Blockchain.Ethereum
|
||||
"ADA" -> Blockchain.CardanoShelley
|
||||
else -> Blockchain.values().find { it.currency.lowercase() == currencyName.lowercase() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ class MoonPayService(
|
|||
|
||||
private var status: MoonPayStatus? = null
|
||||
|
||||
override fun featureIsSwitchedOn(): Boolean = true
|
||||
|
||||
override suspend fun update() {
|
||||
withIOContext {
|
||||
performRequest {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue