Updated on 2026-08-14
This commit is contained in:
commit
4de32b1119
34 changed files with 660 additions and 368 deletions
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.blockchain.common.BlockchainError
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.AnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.AnalyticsParam
|
||||
import com.tangem.tap.features.demo.DemoTransactionSender
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun AnalyticsHandler.logSendTransactionError(
|
||||
error: BlockchainError,
|
||||
action: Analytics.ActionToLog,
|
||||
parameters: Map<AnalyticsParam, String>? = mapOf(),
|
||||
card: Card? = null,
|
||||
) {
|
||||
when (val blockchainSdkError = (error as BlockchainSdkError)) {
|
||||
is BlockchainSdkError.WrappedTangemError -> {
|
||||
val tangemSdkError = (blockchainSdkError.tangemError as? TangemSdkError) ?: return
|
||||
|
||||
logCardSdkError(
|
||||
error = tangemSdkError,
|
||||
actionToLog = action,
|
||||
parameters = parameters,
|
||||
card = card,
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
when {
|
||||
blockchainSdkError.customMessage.contains(DemoTransactionSender.ID) -> return
|
||||
else -> {
|
||||
val params = parameters?.toMutableMap() ?: mutableMapOf()
|
||||
params[AnalyticsParam.ACTION] = action.key
|
||||
params[AnalyticsParam.ERROR_CODE] = error.code.toString()
|
||||
params[AnalyticsParam.ERROR_DESCRIPTION] = "${error.javaClass.simpleName}: ${error.customMessage}"
|
||||
params[AnalyticsParam.ERROR_KEY] = "BlockchainSdkError"
|
||||
|
||||
FirebaseCrashlytics.getInstance().apply {
|
||||
params.forEach { setCustomKey(it.key.param, it.value) }
|
||||
recordException(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,10 +44,6 @@ sealed class TapError(
|
|||
val customMessage: String = "Unsupported state:"
|
||||
) : TapError(R.string.common_custom_string, listOf("$customMessage $stateError"))
|
||||
|
||||
sealed class XmlError {
|
||||
object AssetAccountNotCreated : TapError(R.string.send_error_no_account_xlm)
|
||||
}
|
||||
|
||||
sealed class WalletManager {
|
||||
object CreationError : CustomError("Can't create wallet manager")
|
||||
class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.blockchain.blockchains.ethereum.EthereumUtils
|
|||
import com.tangem.blockchain.blockchains.ethereum.EthereumUtils.Companion.toKeccak
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.BlockchainSdkError
|
||||
import com.tangem.blockchain.common.CommonSigner
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionSender
|
||||
|
|
@ -24,6 +25,7 @@ import com.tangem.common.extensions.toHexString
|
|||
import com.tangem.crypto.CryptoUtils
|
||||
import com.tangem.operations.sign.SignHashCommand
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.extensions.logSendTransactionError
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
|
|
@ -38,8 +40,8 @@ import com.tangem.tap.tangemSdk
|
|||
import com.tangem.tap.tangemSdkManager
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage
|
||||
import com.trustwallet.walletconnect.models.ethereum.WCEthereumTransaction
|
||||
import java.math.BigDecimal
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
class WalletConnectSdkHelper {
|
||||
|
||||
|
|
@ -69,7 +71,7 @@ class WalletConnectSdkHelper {
|
|||
(walletManager as? EthereumGasLoader)?.getGasPrice()) {
|
||||
is Result.Success -> result.data.toBigDecimal()
|
||||
is Result.Failure -> {
|
||||
Timber.e(result.error)
|
||||
(result.error as? Throwable)?.let { Timber.e(it) }
|
||||
return null
|
||||
}
|
||||
null -> return null
|
||||
|
|
@ -143,13 +145,11 @@ class WalletConnectSdkHelper {
|
|||
HEX_PREFIX + data.walletManager.wallet.recentTransactions.last().hash
|
||||
}
|
||||
is SimpleResult.Failure -> {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
store.state.globalState.analyticsHandlers?.logCardSdkError(
|
||||
error,
|
||||
Analytics.ActionToLog.WalletConnectTransaction,
|
||||
)
|
||||
}
|
||||
Timber.e(result.error)
|
||||
store.state.globalState.analyticsHandlers?.logSendTransactionError(
|
||||
result.error,
|
||||
Analytics.ActionToLog.WalletConnectTransaction,
|
||||
)
|
||||
Timber.e(result.error as BlockchainSdkError)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
package com.tangem.tap.features.demo
|
||||
|
||||
import com.tangem.blockchain.blockchains.bitcoin.BitcoinWalletManager
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionSender
|
||||
import com.tangem.blockchain.common.TransactionSigner
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.toBlockchainCustomError
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.common.CompletionResult
|
||||
|
|
@ -476,7 +482,7 @@ class DemoTransactionSender(
|
|||
publicKey = walletManager.wallet.publicKey
|
||||
)
|
||||
return when (signerResponse) {
|
||||
is CompletionResult.Success -> SimpleResult.Failure(Exception(ID))
|
||||
is CompletionResult.Success -> SimpleResult.Failure(Exception(ID).toBlockchainCustomError())
|
||||
is CompletionResult.Failure -> SimpleResult.fromTangemSdkError(signerResponse.error)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.features.details.redux
|
||||
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.domain.common.TapWorkarounds.isSaltPay
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.domain.common.TapWorkarounds.isTangemNote
|
||||
import com.tangem.domain.common.isTangemTwin
|
||||
|
|
@ -19,7 +20,6 @@ class DetailsReducer {
|
|||
|
||||
private fun internalReduce(action: Action, state: AppState): DetailsState {
|
||||
if (action !is DetailsAction) return state.detailsState
|
||||
|
||||
val detailsState = state.detailsState
|
||||
return when (action) {
|
||||
is DetailsAction.PrepareScreen -> {
|
||||
|
|
@ -40,7 +40,6 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
|
|||
}
|
||||
is DetailsAction.ChangeAppCurrency ->
|
||||
detailsState.copy(appCurrency = action.fiatCurrency)
|
||||
|
||||
else -> detailsState
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +47,6 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
|
|||
private fun handlePrepareScreen(
|
||||
action: DetailsAction.PrepareScreen,
|
||||
): DetailsState {
|
||||
|
||||
return DetailsState(
|
||||
scanResponse = action.scanResponse,
|
||||
wallets = action.wallets,
|
||||
|
|
@ -99,7 +97,8 @@ private fun prepareSecurityOptions(card: Card): ManageSecurityState {
|
|||
private fun isResetToFactoryAllowedByCard(card: Card): Boolean {
|
||||
val notAllowedByAnyWallet = card.wallets.any { it.settings.isPermanent }
|
||||
val notAllowedByCard = notAllowedByAnyWallet ||
|
||||
(card.isWalletDataSupported && (!card.isTangemNote() && !card.settings.isBackupAllowed))
|
||||
(card.isWalletDataSupported && (!card.isTangemNote() && !card.settings.isBackupAllowed)) ||
|
||||
card.isSaltPay
|
||||
return !notAllowedByCard
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +139,6 @@ private fun handleSecurityAction(
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ fun AppSettingsScreen(
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun AppSettings(
|
||||
private fun AppSettings(
|
||||
state: AppSettingsScreenState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
|
@ -69,13 +69,15 @@ fun AppSettings(
|
|||
.fillMaxSize(),
|
||||
) {
|
||||
AppSettingsElement(
|
||||
state = state, setting = PrivacySetting.SaveWallets,
|
||||
state = state,
|
||||
setting = PrivacySetting.SaveWallets,
|
||||
onDialogStateChange = onDialogStateChange,
|
||||
modifier = modifier,
|
||||
)
|
||||
Spacer(modifier = Modifier.size(32.dp))
|
||||
AppSettingsElement(
|
||||
state = state, setting = PrivacySetting.SaveAccessCode,
|
||||
state = state,
|
||||
setting = PrivacySetting.SaveAccessCode,
|
||||
onDialogStateChange = onDialogStateChange,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
|
@ -83,7 +85,7 @@ fun AppSettings(
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun AppSettingsElement(
|
||||
private fun AppSettingsElement(
|
||||
state: AppSettingsScreenState,
|
||||
setting: PrivacySetting,
|
||||
onDialogStateChange: (PrivacySetting?) -> Unit,
|
||||
|
|
@ -103,7 +105,6 @@ fun AppSettingsElement(
|
|||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 20.dp),
|
||||
// .clickable { state.onSettingToggled(element, !checked) },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(
|
||||
|
|
@ -140,7 +141,7 @@ fun AppSettingsElement(
|
|||
}
|
||||
}
|
||||
|
||||
fun onCheckedChange(
|
||||
private fun onCheckedChange(
|
||||
element: PrivacySetting, enabled: Boolean,
|
||||
onSettingToggled: (PrivacySetting, Boolean) -> Unit,
|
||||
onDialogStateChange: (PrivacySetting?) -> Unit,
|
||||
|
|
@ -154,7 +155,7 @@ fun onCheckedChange(
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsAlertDialog(
|
||||
private fun SettingsAlertDialog(
|
||||
element: PrivacySetting,
|
||||
onDialogStateChange: (PrivacySetting?) -> Unit,
|
||||
onSettingToggled: (PrivacySetting, Boolean) -> Unit,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import androidx.compose.animation.core.LinearOutSlowInEasing
|
|||
import androidx.compose.animation.core.animateDp
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.indication
|
||||
|
|
@ -19,7 +18,6 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -44,35 +42,26 @@ fun TangemSwitch(
|
|||
val transition = updateTransition(checked, label = "SwitchState")
|
||||
val color by transition.animateColor(
|
||||
transitionSpec = {
|
||||
tween(200, easing = FastOutLinearInEasing)
|
||||
tween(durationMillis = 200, easing = FastOutLinearInEasing)
|
||||
},
|
||||
label = "",
|
||||
) {
|
||||
when (it) {
|
||||
true -> enabledColor
|
||||
false -> disabledColor
|
||||
}
|
||||
) { enabled ->
|
||||
if (enabled) enabledColor else disabledColor
|
||||
}
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val clickable = Modifier.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
) {
|
||||
if (!checked) {
|
||||
onCheckedChange(true)
|
||||
} else {
|
||||
onCheckedChange(false)
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.then(clickable)
|
||||
modifier = modifier
|
||||
.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null,
|
||||
) {
|
||||
onCheckedChange(!checked)
|
||||
}
|
||||
.indication(
|
||||
interactionSource = MutableInteractionSource(),
|
||||
interactionSource = interactionSource,
|
||||
indication = rememberRipple(
|
||||
bounded = true,
|
||||
radius = 100.dp,
|
||||
bounded = false,
|
||||
color = Color.Transparent,
|
||||
),
|
||||
),
|
||||
|
|
@ -81,36 +70,27 @@ fun TangemSwitch(
|
|||
modifier = modifier
|
||||
.width(size)
|
||||
.height(size / 2)
|
||||
.indication(MutableInteractionSource(), null)
|
||||
.indication(interactionSource, null)
|
||||
.background(color = color, shape = RoundedCornerShape(100)),
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
) {
|
||||
val roundCardSize = this.maxWidth / 2
|
||||
val xOffset by transition.animateDp(
|
||||
transitionSpec = {
|
||||
tween(150, easing = LinearOutSlowInEasing)
|
||||
tween(durationMillis = 150, easing = LinearOutSlowInEasing)
|
||||
},
|
||||
label = "xOffset",
|
||||
) { state ->
|
||||
when (state) {
|
||||
false -> 0.dp
|
||||
true -> this.maxWidth - roundCardSize
|
||||
}
|
||||
) { enabled ->
|
||||
if (enabled) this.maxWidth - roundCardSize else 0.dp
|
||||
}
|
||||
|
||||
Card(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(this.maxWidth / 2)
|
||||
.offset(x = xOffset, y = 0.dp)
|
||||
.padding(3.dp),
|
||||
shape = RoundedCornerShape(100),
|
||||
backgroundColor = Color.White,
|
||||
border = BorderStroke(
|
||||
if (!checked) 0.5.dp else 0.dp,
|
||||
color = Color.LightGray,
|
||||
),
|
||||
) {
|
||||
}
|
||||
.padding(3.dp)
|
||||
.background(color = Color.White, shape = RoundedCornerShape(100)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.Message
|
|||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.tap.common.redux.ErrorAction
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
import com.tangem.tap.common.redux.ToastNotificationAction
|
||||
|
|
@ -155,7 +156,10 @@ sealed class SendAction : SendScreenAction {
|
|||
val reduceAmount: BigDecimal,
|
||||
) : Dialog()
|
||||
|
||||
data class SendTransactionFails(val errorMessage: String) : Dialog()
|
||||
sealed class SendTransactionFails : Dialog() {
|
||||
data class CardSdkError(val error: TangemSdkError): Dialog()
|
||||
data class BlockchainSdkError(val error: com.tangem.blockchain.common.BlockchainSdkError): Dialog()
|
||||
}
|
||||
object Hide : Dialog()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import com.tangem.tap.common.analytics.AnalyticsParam
|
|||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.logSendTransactionError
|
||||
import com.tangem.tap.common.extensions.safeUpdate
|
||||
import com.tangem.tap.common.extensions.stripZeroPlainString
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
|
|
@ -52,14 +53,13 @@ import com.tangem.tap.scope
|
|||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdk
|
||||
import com.tangem.wallet.R
|
||||
import java.util.EnumSet
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -79,13 +79,19 @@ class SendMiddleware {
|
|||
is SendActionUi.CheckIfTransactionDataWasProvided -> {
|
||||
val transactionData = appState()?.sendState?.externalTransactionData
|
||||
if (transactionData != null) {
|
||||
store.dispatchOnMain(AddressPayIdVerifyAction.AddressVerification.SetWalletAddress(
|
||||
transactionData.destinationAddress, false
|
||||
))
|
||||
store.dispatchOnMain(
|
||||
AddressPayIdVerifyAction.AddressVerification.SetWalletAddress(
|
||||
transactionData.destinationAddress, false,
|
||||
),
|
||||
)
|
||||
store.dispatchOnMain(AmountActionUi.SetMainCurrency(MainCurrencyType.CRYPTO))
|
||||
store.dispatchOnMain(AmountActionUi.HandleUserInput(transactionData.amount))
|
||||
store.dispatchOnMain(AmountAction.SetAmount(transactionData.amount.toBigDecimal(),
|
||||
false))
|
||||
store.dispatchOnMain(
|
||||
AmountAction.SetAmount(
|
||||
transactionData.amount.toBigDecimal(),
|
||||
false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -93,7 +99,6 @@ class SendMiddleware {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun verifyAndSendTransaction(
|
||||
|
|
@ -113,23 +118,31 @@ private fun verifyAndSendTransaction(
|
|||
when {
|
||||
hadTezosError -> {
|
||||
val reduceAmount = walletManager.wallet.blockchain.minimalAmount()
|
||||
dispatch(SendAction.Dialog.TezosWarningDialog(reduceCallback = {
|
||||
dispatch(AmountAction.SetAmount(typedAmount.value!!.minus(reduceAmount), false))
|
||||
dispatch(AmountActionUi.CheckAmountToSend)
|
||||
}, sendAllCallback = {
|
||||
sendTransaction(
|
||||
action, walletManager, amountToSend, feeAmount, destinationAddress,
|
||||
sendState.transactionExtrasState, card, sendState.externalTransactionData,
|
||||
dispatch
|
||||
)
|
||||
}, reduceAmount))
|
||||
dispatch(
|
||||
SendAction.Dialog.TezosWarningDialog(
|
||||
reduceCallback = {
|
||||
dispatch(AmountAction.SetAmount(typedAmount.value!!.minus(reduceAmount), false))
|
||||
dispatch(AmountActionUi.CheckAmountToSend)
|
||||
},
|
||||
sendAllCallback = {
|
||||
sendTransaction(
|
||||
action, walletManager, amountToSend, feeAmount, destinationAddress,
|
||||
sendState.transactionExtrasState, card, sendState.externalTransactionData,
|
||||
dispatch,
|
||||
)
|
||||
},
|
||||
reduceAmount,
|
||||
),
|
||||
)
|
||||
}
|
||||
transactionErrors.isNotEmpty() -> {
|
||||
dispatch(SendAction.SendError(createValidateTransactionError(transactionErrors, walletManager)))
|
||||
}
|
||||
else -> {
|
||||
sendTransaction(action, walletManager, amountToSend, feeAmount, destinationAddress,
|
||||
sendState.transactionExtrasState, card, sendState.externalTransactionData, dispatch)
|
||||
sendTransaction(
|
||||
action, walletManager, amountToSend, feeAmount, destinationAddress,
|
||||
sendState.transactionExtrasState, card, sendState.externalTransactionData, dispatch,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -150,7 +163,9 @@ private fun sendTransaction(
|
|||
|
||||
transactionExtras.xlmMemo?.memo?.let { txData = txData.copy(extras = StellarTransactionExtras(it)) }
|
||||
transactionExtras.binanceMemo?.memo?.let { txData = txData.copy(extras = BinanceTransactionExtras(it.toString())) }
|
||||
transactionExtras.xrpDestinationTag?.tag?.let { txData = txData.copy(extras = XrpTransactionBuilder.XrpTransactionExtras(it)) }
|
||||
transactionExtras.xrpDestinationTag?.tag?.let {
|
||||
txData = txData.copy(extras = XrpTransactionBuilder.XrpTransactionExtras(it))
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
val updateWalletResult = walletManager.safeUpdate()
|
||||
|
|
@ -178,14 +193,14 @@ private fun sendTransaction(
|
|||
val signer = TangemSigner(
|
||||
card = card,
|
||||
tangemSdk = tangemSdk,
|
||||
initialMessage = action.messageForSigner
|
||||
initialMessage = action.messageForSigner,
|
||||
) { signResponse ->
|
||||
store.dispatch(
|
||||
GlobalAction.UpdateWalletSignedHashes(
|
||||
walletSignedHashes = signResponse.totalSignedHashes,
|
||||
walletPublicKey = walletManager.wallet.publicKey.seedKey,
|
||||
remainingSignatures = signResponse.remainingSignatures
|
||||
)
|
||||
remainingSignatures = signResponse.remainingSignatures,
|
||||
),
|
||||
)
|
||||
}
|
||||
val sendResult = try {
|
||||
|
|
@ -211,7 +226,7 @@ private fun sendTransaction(
|
|||
store.state.globalState.analyticsHandlers?.triggerEvent(
|
||||
event = AnalyticsEvent.TRANSACTION_IS_SENT,
|
||||
card = card,
|
||||
blockchain = walletManager.wallet.blockchain.currency
|
||||
blockchain = walletManager.wallet.blockchain.currency,
|
||||
)
|
||||
dispatch(SendAction.SendSuccess)
|
||||
|
||||
|
|
@ -231,69 +246,48 @@ private fun sendTransaction(
|
|||
}
|
||||
}
|
||||
is SimpleResult.Failure -> {
|
||||
when (sendResult.error) {
|
||||
store.state.globalState.feedbackManager?.infoHolder?.updateOnSendError(
|
||||
wallet = walletManager.wallet,
|
||||
host = walletManager.currentHost,
|
||||
amountToSend = amountToSend,
|
||||
feeAmount = feeAmount,
|
||||
destinationAddress = destinationAddress,
|
||||
)
|
||||
store.state.globalState.analyticsHandlers?.logSendTransactionError(
|
||||
error = sendResult.error,
|
||||
action = Analytics.ActionToLog.SendTransaction,
|
||||
parameters = mapOf(AnalyticsParam.BLOCKCHAIN to walletManager.wallet.blockchain.currency),
|
||||
card = card,
|
||||
)
|
||||
|
||||
val error = (sendResult.error as? BlockchainSdkError) ?: return@withContext
|
||||
|
||||
when (error) {
|
||||
is BlockchainSdkError.WrappedTangemError -> {
|
||||
val tangemSdkError = (error.tangemError as? TangemSdkError) ?: return@withContext
|
||||
if (tangemSdkError is TangemSdkError.UserCancelled) return@withContext
|
||||
|
||||
dispatch(SendAction.Dialog.SendTransactionFails.CardSdkError(tangemSdkError))
|
||||
}
|
||||
is BlockchainSdkError.CreateAccountUnderfunded -> {
|
||||
val error = sendResult.error as 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))))
|
||||
}
|
||||
is BlockchainSdkError.SendException -> {
|
||||
sendResult.error?.let { FirebaseCrashlytics.getInstance().recordException(it) }
|
||||
}
|
||||
is Throwable -> {
|
||||
val throwable = sendResult.error as Throwable
|
||||
val message = throwable.message
|
||||
val infoHolder = store.state.globalState.feedbackManager?.infoHolder
|
||||
else -> {
|
||||
when {
|
||||
message == null -> {
|
||||
dispatch(SendAction.SendError(TapError.UnknownError))
|
||||
infoHolder?.updateOnSendError(
|
||||
wallet = walletManager.wallet,
|
||||
host = walletManager.currentHost,
|
||||
amountToSend = amountToSend,
|
||||
feeAmount = feeAmount,
|
||||
destinationAddress = destinationAddress
|
||||
error.customMessage.contains(DemoTransactionSender.ID) -> {
|
||||
store.dispatchDialogShow(
|
||||
AppDialog.SimpleOkDialogRes(
|
||||
headerId = R.string.common_done,
|
||||
messageId = R.string.alert_demo_feature_disabled,
|
||||
onOk = { dispatch(NavigationAction.PopBackTo()) },
|
||||
),
|
||||
)
|
||||
dispatch(SendAction.Dialog.SendTransactionFails("unknown error"))
|
||||
}
|
||||
message.contains("50002") -> {
|
||||
// user was cancelled the operation by closing the Sdk bottom sheet
|
||||
}
|
||||
// make it easier latter by handling an appropriate enumError or, like on iOS,
|
||||
// accept a string identifier of the error message
|
||||
message.contains("Target account is not created. To create account send 1+ XLM.") -> {
|
||||
dispatch(SendAction.SendError(TapError.XmlError.AssetAccountNotCreated))
|
||||
}
|
||||
message.contains(DemoTransactionSender.ID) -> {
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
store.dispatchDialogShow(AppDialog.SimpleOkDialogRes(
|
||||
R.string.common_done,
|
||||
R.string.alert_demo_feature_disabled
|
||||
) { dispatch(NavigationAction.PopBackTo()) })
|
||||
}
|
||||
else -> {
|
||||
(sendResult.error as? TangemSdkError)?.let { error ->
|
||||
store.state.globalState.analyticsHandlers?.logCardSdkError(
|
||||
error,
|
||||
Analytics.ActionToLog.SendTransaction,
|
||||
mapOf(
|
||||
AnalyticsParam.BLOCKCHAIN
|
||||
to walletManager.wallet.blockchain.currency),
|
||||
card = card,
|
||||
)
|
||||
}
|
||||
Timber.e(throwable)
|
||||
FirebaseCrashlytics.getInstance().recordException(throwable)
|
||||
dispatch(SendAction.SendError(TapError.CustomError(message)))
|
||||
infoHolder?.updateOnSendError(
|
||||
wallet = walletManager.wallet,
|
||||
host = walletManager.currentHost,
|
||||
amountToSend = amountToSend,
|
||||
feeAmount = feeAmount,
|
||||
destinationAddress = destinationAddress
|
||||
)
|
||||
dispatch(SendAction.Dialog.SendTransactionFails(message))
|
||||
dispatch(SendAction.Dialog.SendTransactionFails.BlockchainSdkError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -328,7 +322,10 @@ fun extractErrorsForAmountField(errors: EnumSet<TransactionError>): EnumSet<Tran
|
|||
return showIntoAmountField
|
||||
}
|
||||
|
||||
fun createValidateTransactionError(errorList: EnumSet<TransactionError>, walletManager: WalletManager): TapError.ValidateTransactionErrors {
|
||||
fun createValidateTransactionError(
|
||||
errorList: EnumSet<TransactionError>,
|
||||
walletManager: WalletManager,
|
||||
): TapError.ValidateTransactionErrors {
|
||||
val tapErrors = errorList.map {
|
||||
when (it) {
|
||||
TransactionError.AmountExceedsBalance -> TapError.AmountExceedsBalance
|
||||
|
|
|
|||
|
|
@ -106,8 +106,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) &&
|
||||
sendState.amountState.typeOfAmount is AmountType.Token) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.send.ui.dialogs
|
|||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.tangem.tangem_sdk_new.extensions.localizedDescription
|
||||
import com.tangem.tap.common.feedback.SendTransactionFailedEmail
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
|
|
@ -14,14 +15,22 @@ import com.tangem.wallet.R
|
|||
class SendTransactionFailsDialog {
|
||||
|
||||
companion object {
|
||||
fun create(context: Context, dialog: SendAction.Dialog.SendTransactionFails): AlertDialog {
|
||||
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)
|
||||
}
|
||||
|
||||
private fun create(context: Context, errorMessage: String): AlertDialog {
|
||||
return AlertDialog.Builder(context).apply {
|
||||
setTitle(R.string.alert_failed_to_send_transaction_title)
|
||||
setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, dialog.errorMessage))
|
||||
setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage))
|
||||
setNeutralButton(R.string.alert_button_send_feedback) { _, _ ->
|
||||
store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(dialog.errorMessage)))
|
||||
store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage)))
|
||||
}
|
||||
setPositiveButton(R.string.common_no) { _, _ -> }
|
||||
setPositiveButton(R.string.common_cancel) { _, _ -> }
|
||||
setOnDismissListener { store.dispatch(SendAction.Dialog.Hide) }
|
||||
}.create()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,12 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.core.text.bold
|
||||
import com.tangem.common.extensions.remove
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.extensions.beginDelayedTransition
|
||||
import com.tangem.tap.common.extensions.enableError
|
||||
import com.tangem.tap.common.extensions.getColor
|
||||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.extensions.update
|
||||
import com.tangem.tap.common.redux.getMessageString
|
||||
import com.tangem.tap.common.text.DecimalDigitsInputFilter
|
||||
import com.tangem.tap.domain.MultiMessageError
|
||||
|
|
@ -17,7 +22,17 @@ 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.*
|
||||
import com.tangem.tap.features.send.redux.states.AddressPayIdState
|
||||
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.ReceiptLayoutType
|
||||
import com.tangem.tap.features.send.redux.states.ReceiptState
|
||||
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.SendTransactionFailsDialog
|
||||
|
|
@ -117,7 +132,13 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
|
|||
dialog?.show()
|
||||
}
|
||||
}
|
||||
is SendAction.Dialog.SendTransactionFails -> {
|
||||
is SendAction.Dialog.SendTransactionFails.CardSdkError -> {
|
||||
if (dialog == null) {
|
||||
dialog = SendTransactionFailsDialog.create(fg.requireContext(), state.dialog)
|
||||
dialog?.show()
|
||||
}
|
||||
}
|
||||
is SendAction.Dialog.SendTransactionFails.BlockchainSdkError -> {
|
||||
if (dialog == null) {
|
||||
dialog = SendTransactionFailsDialog.create(fg.requireContext(), state.dialog)
|
||||
dialog?.show()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
package com.tangem.tap.features.wallet.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.annotation.ColorRes
|
||||
|
|
@ -11,21 +15,32 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.transition.TransitionInflater
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.tangem_sdk_new.extensions.dpToPx
|
||||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.TestActions
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.extensions.appendIfNotNull
|
||||
import com.tangem.tap.common.extensions.beginDelayedTransition
|
||||
import com.tangem.tap.common.extensions.fitChipsByGroupWidth
|
||||
import com.tangem.tap.common.extensions.getColor
|
||||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.extensions.toQrCode
|
||||
import com.tangem.tap.common.recyclerView.SpaceItemDecoration
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.onboarding.getQRReceiveMessage
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
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.WalletData
|
||||
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.loadCurrencyIcon
|
||||
import com.tangem.tap.features.wallet.ui.test.TestWalletDetails
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -199,11 +214,12 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
}
|
||||
|
||||
private fun handleCurrencyIcon(wallet: WalletData) = with(binding.lWalletDetails.lBalance) {
|
||||
loadCurrencyIcon(
|
||||
currencyImageView = ivCurrency,
|
||||
currencyTextView = tvTokenLetter,
|
||||
blockchain = wallet.currency.blockchain,
|
||||
token = (wallet.currency as? Currency.Token)?.token
|
||||
ivCurrency.load(
|
||||
currency = wallet.currency,
|
||||
derivationStyle = store.state.globalState
|
||||
.scanResponse
|
||||
?.card
|
||||
?.derivationStyle,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import androidx.transition.TransitionInflater
|
|||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import coil.load
|
||||
import coil.size.Scale
|
||||
import com.tangem.domain.common.TapWorkarounds.isSaltPay
|
||||
import com.tangem.tap.MainActivity
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.recyclerView.SpaceItemDecoration
|
||||
|
|
@ -32,6 +33,7 @@ import com.tangem.tap.features.wallet.redux.WalletAction
|
|||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
|
||||
import com.tangem.tap.features.wallet.ui.wallet.MultiWalletView
|
||||
import com.tangem.tap.features.wallet.ui.wallet.SaltPaySingleWalletView
|
||||
import com.tangem.tap.features.wallet.ui.wallet.SingleWalletView
|
||||
import com.tangem.tap.features.wallet.ui.wallet.WalletView
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -112,17 +114,22 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
|
||||
override fun newState(state: WalletState) {
|
||||
if (activity == null || view == null) return
|
||||
|
||||
if (state.isMultiwalletAllowed &&
|
||||
state.primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
walletView is SingleWalletView
|
||||
) {
|
||||
walletView = MultiWalletView()
|
||||
walletView.changeWalletView(this, binding)
|
||||
} else if (!state.isMultiwalletAllowed && walletView is MultiWalletView) {
|
||||
walletView = SingleWalletView()
|
||||
walletView.changeWalletView(this, binding)
|
||||
val isSaltPay = store.state.globalState.scanResponse?.card?.isSaltPay == true
|
||||
when {
|
||||
isSaltPay -> {
|
||||
walletView = SaltPaySingleWalletView()
|
||||
}
|
||||
state.isMultiwalletAllowed &&
|
||||
state.primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
walletView is SingleWalletView -> {
|
||||
walletView = MultiWalletView()
|
||||
}
|
||||
!state.isMultiwalletAllowed && walletView is MultiWalletView -> {
|
||||
walletView = SingleWalletView()
|
||||
}
|
||||
}
|
||||
|
||||
walletView.changeWalletView(this, binding)
|
||||
walletView.onNewState(state)
|
||||
|
||||
if (!state.shouldShowDetails) {
|
||||
|
|
@ -134,7 +141,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
setupNoInternetHandling(state)
|
||||
setupCardImage(state.cardImage)
|
||||
|
||||
showWarningsIfPresent(state.mainWarningsList)
|
||||
if (!isSaltPay) showWarningsIfPresent(state.mainWarningsList)
|
||||
|
||||
binding.srlWallet.isRefreshing = state.state == ProgressState.Refreshing
|
||||
binding.srlWallet.setOnRefreshListener {
|
||||
|
|
|
|||
|
|
@ -9,15 +9,12 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.tap.common.extensions.getRoundIconRes
|
||||
import com.tangem.tap.common.extensions.getString
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
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.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.images.loadCurrencyIcon
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.ItemCurrencyWalletBinding
|
||||
|
|
@ -67,12 +64,6 @@ class WalletAdapter
|
|||
// Skip changes when on refreshing status
|
||||
if (status == BalanceStatus.Refreshing) return@with
|
||||
|
||||
val isCustomCurrency = wallet.currency.isCustomCurrency(
|
||||
derivationStyle = store.state.globalState
|
||||
.scanResponse
|
||||
?.card
|
||||
?.derivationStyle
|
||||
)
|
||||
val statusMessage = when (status) {
|
||||
BalanceStatus.TransactionInProgress -> {
|
||||
root.getString(R.string.wallet_balance_tx_in_progress)
|
||||
|
|
@ -87,7 +78,8 @@ class WalletAdapter
|
|||
BalanceStatus.UnknownBlockchain,
|
||||
BalanceStatus.Loading,
|
||||
BalanceStatus.Refreshing,
|
||||
null -> null
|
||||
null,
|
||||
-> null
|
||||
}
|
||||
|
||||
if (status == null || status == BalanceStatus.Loading) {
|
||||
|
|
@ -98,11 +90,12 @@ class WalletAdapter
|
|||
lContent.root.show()
|
||||
}
|
||||
|
||||
loadCurrencyIcon(
|
||||
currencyImageView = ivCurrency,
|
||||
currencyTextView = tvTokenLetter,
|
||||
token = (wallet.currency as? Currency.Token)?.token,
|
||||
blockchain = wallet.currency.blockchain,
|
||||
ivCurrency.load(
|
||||
currency = wallet.currency,
|
||||
derivationStyle = store.state.globalState
|
||||
.scanResponse
|
||||
?.card
|
||||
?.derivationStyle,
|
||||
)
|
||||
|
||||
lContent.tvCurrency.text = wallet.currencyData.currency
|
||||
|
|
@ -116,10 +109,6 @@ class WalletAdapter
|
|||
lContent.tvExchangeRate.text = wallet.fiatRateString
|
||||
?: root.getString(id = R.string.token_item_no_rate)
|
||||
|
||||
badgeCustomBalance.isVisible = isCustomCurrency
|
||||
ivBlockchain.isVisible = wallet.currency.isToken()
|
||||
ivBlockchain.setImageResource(wallet.currency.blockchain.getRoundIconRes())
|
||||
|
||||
cardWallet.setOnClickListener {
|
||||
store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,22 +19,7 @@ import com.tangem.wallet.R
|
|||
private const val QCX = "QCX"
|
||||
private const val VOYR = "VOYRME"
|
||||
|
||||
fun loadCurrencyIcon(
|
||||
currencyImageView: CurrencyIconView,
|
||||
currencyTextView: TextView,
|
||||
token: Token?,
|
||||
blockchain: Blockchain,
|
||||
) {
|
||||
CurrencyIconLoader(
|
||||
currencyImageView = currencyImageView.imageView,
|
||||
currencyTextView = currencyTextView,
|
||||
token = token,
|
||||
blockchain = blockchain
|
||||
)
|
||||
.load()
|
||||
}
|
||||
|
||||
private class CurrencyIconLoader(
|
||||
class CurrencyIconRequest(
|
||||
private val currencyImageView: ImageFilterView,
|
||||
private val currencyTextView: TextView,
|
||||
private val token: Token?,
|
||||
|
|
|
|||
|
|
@ -3,27 +3,69 @@ package com.tangem.tap.features.wallet.ui.images
|
|||
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 com.google.android.material.card.MaterialCardView
|
||||
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
|
||||
|
||||
class CurrencyIconView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
) : MaterialCardView(context, attrs, defStyleAttr) {
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
private val binding = ViewCurrencyIconBinding.inflate(
|
||||
LayoutInflater.from(context),
|
||||
this
|
||||
this,
|
||||
)
|
||||
|
||||
val imageView: ImageFilterView
|
||||
get() = binding.iv
|
||||
private val currencyImageView: ImageFilterView
|
||||
get() = binding.ivCurrency
|
||||
|
||||
private val currencyTextView: TextView
|
||||
get() = binding.tvTokenLetter
|
||||
|
||||
private var isBlockchainIconVisible: Boolean
|
||||
get() = binding.ivBlockchain.isVisible
|
||||
set(value) = binding.ivBlockchain::isVisible.set(value)
|
||||
|
||||
private var isBadgeVisible: Boolean
|
||||
get() = binding.badge.isVisible
|
||||
set(value) = binding.badge::isVisible.set(value)
|
||||
|
||||
@DrawableRes
|
||||
private var blockchainIconRes: Int? = null
|
||||
set(value) {
|
||||
if (value != null && value != field && isBlockchainIconVisible) {
|
||||
binding.ivBlockchain.setImageResource(value)
|
||||
field = value
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
elevation = 0f
|
||||
cardElevation = 0f
|
||||
radius = dpToPx(6f)
|
||||
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()
|
||||
|
||||
CurrencyIconRequest(
|
||||
currencyImageView = currencyImageView,
|
||||
currencyTextView = currencyTextView,
|
||||
token = (currency as? Currency.Token)?.token,
|
||||
blockchain = currency.blockchain,
|
||||
).load()
|
||||
}
|
||||
}
|
||||
|
|
@ -25,14 +25,8 @@ import com.tangem.wallet.R
|
|||
import com.tangem.wallet.databinding.FragmentWalletBinding
|
||||
|
||||
|
||||
class MultiWalletView : WalletView {
|
||||
|
||||
private var fragment: WalletFragment? = null
|
||||
private var binding: FragmentWalletBinding? = null
|
||||
|
||||
class MultiWalletView : WalletView() {
|
||||
private lateinit var walletsAdapter: WalletAdapter
|
||||
|
||||
|
||||
override fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding) {
|
||||
setFragment(fragment, binding)
|
||||
onViewCreated()
|
||||
|
|
@ -47,6 +41,7 @@ class MultiWalletView : WalletView {
|
|||
lAddress.root.hide()
|
||||
lButtonsShort.root.hide()
|
||||
lButtonsLong.root.hide()
|
||||
lSingleWalletBalance.root.hide()
|
||||
rvMultiwallet.show()
|
||||
btnAddToken.show()
|
||||
setupWalletCardNumber(binding)
|
||||
|
|
@ -64,15 +59,6 @@ class MultiWalletView : WalletView {
|
|||
}
|
||||
}
|
||||
|
||||
override fun setFragment(fragment: WalletFragment, binding: FragmentWalletBinding) {
|
||||
this.fragment = fragment
|
||||
this.binding = binding
|
||||
}
|
||||
|
||||
override fun removeFragment() {
|
||||
this.fragment = null
|
||||
this.binding = null
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
setupWalletsRecyclerView()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.tap.features.wallet.ui.wallet
|
||||
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.extensions.animateVisibility
|
||||
import com.tangem.tap.common.extensions.formatAmountAsSpannedString
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.databinding.LayoutSingleWalletBalanceBinding
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class SaltPayBalanceWidgetData(
|
||||
val state: ProgressState? = null,
|
||||
val currencySymbol: String? = null,
|
||||
val currency: String? = null,
|
||||
val fiatAmount: BigDecimal? = null,
|
||||
val fiatCurrency: FiatCurrency? = null,
|
||||
)
|
||||
|
||||
class SaltPayBalanceWidget(
|
||||
private val binding: LayoutSingleWalletBalanceBinding,
|
||||
private val data: SaltPayBalanceWidgetData,
|
||||
) {
|
||||
fun setup(): Unit = with(binding) {
|
||||
if (data.state == ProgressState.Loading) {
|
||||
veilBalance.veil()
|
||||
veilBalanceCrypto.veil()
|
||||
} else {
|
||||
veilBalance.unVeil()
|
||||
veilBalanceCrypto.unVeil()
|
||||
}
|
||||
tvProcessing.animateVisibility(
|
||||
show = data.state == ProgressState.Error,
|
||||
)
|
||||
veilBalanceCrypto.animateVisibility(
|
||||
show = data.state != ProgressState.Error,
|
||||
)
|
||||
tvBalance.text = data.fiatAmount?.formatAmountAsSpannedString(
|
||||
currencySymbol = data.fiatCurrency?.symbol ?: "",
|
||||
)
|
||||
tvBalanceCrypto.text = data.currency
|
||||
|
||||
tvCurrencyName.text = data.fiatCurrency?.code
|
||||
|
||||
tvCurrencyName.setOnClickListener {
|
||||
store.dispatch(WalletAction.AppCurrencyAction.ChooseAppCurrency)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.tap.features.wallet.ui.wallet
|
||||
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.ui.WalletFragment
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.databinding.FragmentWalletBinding
|
||||
|
||||
class SaltPaySingleWalletView : WalletView() {
|
||||
override fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding) {
|
||||
setFragment(fragment, binding)
|
||||
onViewCreated()
|
||||
showSingleWalletView(binding)
|
||||
}
|
||||
|
||||
private fun showSingleWalletView(binding: FragmentWalletBinding) = with(binding) {
|
||||
rvMultiwallet.hide()
|
||||
btnAddToken.hide()
|
||||
rvPendingTransaction.hide()
|
||||
tvTwinCardNumber.hide()
|
||||
lCardBalance.root.hide()
|
||||
lAddress.root.hide()
|
||||
lSingleWalletBalance.root.show()
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
}
|
||||
|
||||
override fun onNewState(state: WalletState) {
|
||||
val binding = binding ?: return
|
||||
state.primaryWallet ?: return
|
||||
|
||||
setupBalance(state, state.primaryWallet, binding)
|
||||
}
|
||||
|
||||
private fun setupBalance(state: WalletState, primaryWallet: WalletData, binding: FragmentWalletBinding) {
|
||||
binding.lSingleWalletBalance.root.show()
|
||||
SaltPayBalanceWidget(
|
||||
binding = binding.lSingleWalletBalance,
|
||||
data = SaltPayBalanceWidgetData(
|
||||
state = state.state,
|
||||
currencySymbol = primaryWallet.currencyData.currencySymbol,
|
||||
currency = primaryWallet.currencyData.amountFormatted,
|
||||
fiatAmount = primaryWallet.currencyData.fiatAmount,
|
||||
fiatCurrency = store.state.globalState.appCurrency,
|
||||
),
|
||||
).setup()
|
||||
}
|
||||
}
|
||||
|
|
@ -21,22 +21,8 @@ import com.tangem.tap.store
|
|||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentWalletBinding
|
||||
|
||||
class SingleWalletView : WalletView {
|
||||
|
||||
class SingleWalletView : WalletView() {
|
||||
private lateinit var pendingTransactionAdapter: PendingTransactionsAdapter
|
||||
private var fragment: WalletFragment? = null
|
||||
private var binding: FragmentWalletBinding? = null
|
||||
|
||||
override fun setFragment(fragment: WalletFragment, binding: FragmentWalletBinding) {
|
||||
this.fragment = fragment
|
||||
this.binding = binding
|
||||
}
|
||||
|
||||
override fun removeFragment() {
|
||||
fragment = null
|
||||
binding = null
|
||||
}
|
||||
|
||||
override fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding) {
|
||||
setFragment(fragment, binding)
|
||||
onViewCreated()
|
||||
|
|
@ -49,13 +35,13 @@ class SingleWalletView : WalletView {
|
|||
rvPendingTransaction.hide()
|
||||
lCardBalance.root.show()
|
||||
lAddress.root.show()
|
||||
lSingleWalletBalance.root.hide()
|
||||
}
|
||||
|
||||
override fun onViewCreated() {
|
||||
setupTransactionsRecyclerView()
|
||||
}
|
||||
|
||||
|
||||
private fun setupTransactionsRecyclerView() {
|
||||
val fragment = fragment ?: return
|
||||
pendingTransactionAdapter = PendingTransactionsAdapter()
|
||||
|
|
@ -65,7 +51,6 @@ class SingleWalletView : WalletView {
|
|||
}
|
||||
|
||||
override fun onNewState(state: WalletState) {
|
||||
val fragment = fragment ?: return
|
||||
val binding = binding ?: return
|
||||
state.primaryWallet ?: return
|
||||
|
||||
|
|
@ -81,7 +66,6 @@ class SingleWalletView : WalletView {
|
|||
binding?.rvPendingTransaction?.show(pendingTransactions.isNotEmpty())
|
||||
}
|
||||
|
||||
|
||||
private fun setupBalance(state: WalletState, primaryWallet: WalletData) {
|
||||
val fragment = fragment ?: return
|
||||
binding?.apply {
|
||||
|
|
@ -90,13 +74,13 @@ class SingleWalletView : WalletView {
|
|||
binding = this.lCardBalance,
|
||||
fragment = fragment,
|
||||
data = primaryWallet.currencyData,
|
||||
isTwinCard = state.isTangemTwins
|
||||
isTwinCard = state.isTangemTwins,
|
||||
).setup()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTwinCards(
|
||||
twinCardsState: TwinCardsState?, binding: FragmentWalletBinding
|
||||
twinCardsState: TwinCardsState?, binding: FragmentWalletBinding,
|
||||
) = with(binding) {
|
||||
twinCardsState?.cardNumber?.let { cardNumber ->
|
||||
tvTwinCardNumber.show()
|
||||
|
|
@ -113,11 +97,9 @@ class SingleWalletView : WalletView {
|
|||
}
|
||||
|
||||
private fun setupButtons(
|
||||
state: WalletData, isTwinsWallet: Boolean, binding: FragmentWalletBinding
|
||||
state: WalletData, isTwinsWallet: Boolean, binding: FragmentWalletBinding,
|
||||
) = with(binding) {
|
||||
|
||||
setupButtonsType(state, binding)
|
||||
|
||||
val tradeState = state.tradeCryptoState
|
||||
val btnConfirm = if (tradeState.isAvailableToSell() || tradeState.isAvailableToBuy()) {
|
||||
lButtonsShort.btnConfirm
|
||||
|
|
@ -137,8 +119,8 @@ class SingleWalletView : WalletView {
|
|||
store.dispatch(
|
||||
WalletAction.DialogAction.QrCode(
|
||||
currency = state.currency,
|
||||
selectedAddress = selectedAddress
|
||||
)
|
||||
selectedAddress = selectedAddress,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -207,7 +189,6 @@ class SingleWalletView : WalletView {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private fun setupAddressCard(state: WalletData, binding: FragmentWalletBinding) = with(binding.lAddress) {
|
||||
if (state.walletAddresses != null && state.currency is Currency.Blockchain) {
|
||||
binding.lAddress.root.show()
|
||||
|
|
@ -215,7 +196,6 @@ class SingleWalletView : WalletView {
|
|||
(binding.lAddress.root as? ViewGroup)?.beginDelayedTransition()
|
||||
chipGroupAddressType.show()
|
||||
chipGroupAddressType.fitChipsByGroupWidth()
|
||||
|
||||
val checkedId =
|
||||
MultipleAddressUiHelper.typeToId(state.walletAddresses.selectedAddress.type)
|
||||
if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId)
|
||||
|
|
@ -234,8 +214,8 @@ class SingleWalletView : WalletView {
|
|||
store.dispatch(
|
||||
WalletAction.ExploreAddress(
|
||||
state.walletAddresses.selectedAddress.exploreUrl,
|
||||
fragment!!.requireContext()
|
||||
)
|
||||
fragment!!.requireContext(),
|
||||
),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -4,16 +4,20 @@ import com.tangem.tap.features.wallet.redux.WalletState
|
|||
import com.tangem.tap.features.wallet.ui.WalletFragment
|
||||
import com.tangem.wallet.databinding.FragmentWalletBinding
|
||||
|
||||
interface WalletView {
|
||||
abstract class WalletView {
|
||||
protected var fragment: WalletFragment? = null
|
||||
protected var binding: FragmentWalletBinding? = null
|
||||
fun setFragment(fragment: WalletFragment, binding: FragmentWalletBinding) {
|
||||
this.fragment = fragment
|
||||
this.binding = binding
|
||||
}
|
||||
|
||||
fun setFragment(fragment: WalletFragment, binding: FragmentWalletBinding)
|
||||
|
||||
fun removeFragment()
|
||||
|
||||
fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding)
|
||||
|
||||
fun onViewCreated()
|
||||
|
||||
fun onNewState(state: WalletState)
|
||||
fun removeFragment() {
|
||||
fragment = null
|
||||
binding = null
|
||||
}
|
||||
|
||||
abstract fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding)
|
||||
abstract fun onViewCreated()
|
||||
abstract fun onNewState(state: WalletState)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue