diff --git a/app/build.gradle b/app/build.gradle index ce5b525f66..c37a4da509 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -92,9 +92,9 @@ dependencies { implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation "com.tangem:blockchain:${versions.tamgem_blockchain_sdk}" - implementation "com.tangem.tangem-sdk-kotlin:core:${versions.tamgem_card_sdk}" - implementation "com.tangem.tangem-sdk-kotlin:android:${versions.tamgem_card_sdk}" + implementation "com.tangem:blockchain:${versions.tangem_blockchain_sdk}" + implementation "com.tangem.tangem-sdk-kotlin:core:${versions.tangem_card_sdk}" + implementation "com.tangem.tangem-sdk-kotlin:android:${versions.tangem_card_sdk}" // WebView implementation "androidx.browser:browser:1.3.0" diff --git a/app/src/main/java/com/tangem/tap/common/extensions/AnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/extensions/AnalyticsHandler.kt new file mode 100644 index 0000000000..f2897e2b63 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/AnalyticsHandler.kt @@ -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? = 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) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index ff19d10171..9d3cc79eee 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -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) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index a3b0d31a89..04f1ec8141 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -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 } } diff --git a/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt index afbbbbb166..53a0878b74 100644 --- a/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt @@ -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) } } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index a85b15b857..b327e24452 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -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 } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt index 9b1dd4af03..34018707cf 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsScreen.kt @@ -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, diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/common/TangemSwitch.kt b/app/src/main/java/com/tangem/tap/features/details/ui/common/TangemSwitch.kt index 3132bb6fcc..bf5268bf6f 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/common/TangemSwitch.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/common/TangemSwitch.kt @@ -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)), + ) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 2ed9fe33e0..8c76739e6b 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -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() } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 4a1f2df785..5eaa250690 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -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): EnumSet, walletManager: WalletManager): TapError.ValidateTransactionErrors { +fun createValidateTransactionError( + errorList: EnumSet, + walletManager: WalletManager, +): TapError.ValidateTransactionErrors { val tapErrors = errorList.map { when (it) { TransactionError.AmountExceedsBalance -> TapError.AmountExceedsBalance diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt index edb435953f..fd653557c7 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt index 6eccfef665..a01c048058 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt @@ -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() } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 2b71c1ba31..4496bd570b 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -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() diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index 73eb9ea393..0f54d5b13a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -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, ) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 73f315e29b..fe3c9131e4 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -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 { + 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 { 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)) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt index 41076760dc..cd7959319e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt @@ -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?, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt index b973c9cc52..ec9ff38519 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt @@ -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() } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt index 95560bd43d..4267ad7ebd 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt @@ -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() diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SaltPayBalanceWidget.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SaltPayBalanceWidget.kt new file mode 100644 index 0000000000..95509f5b37 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SaltPayBalanceWidget.kt @@ -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) + } + } +} + + diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SaltPaySingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SaltPaySingleWalletView.kt new file mode 100644 index 0000000000..09d00b294e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SaltPaySingleWalletView.kt @@ -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() + } +} diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index dadfaa1bc2..bcbb6a06ff 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -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 { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/WalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/WalletView.kt index 1d9ea0eac0..a3b09aab12 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/WalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/WalletView.kt @@ -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) } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml index 102ae199f4..b2118902ec 100644 --- a/app/src/main/res/layout/fragment_wallet.xml +++ b/app/src/main/res/layout/fragment_wallet.xml @@ -148,6 +148,16 @@ android:visibility="gone" app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" /> + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/view_currency_icon.xml b/app/src/main/res/layout/view_currency_icon.xml index 6859d75600..801ac76c97 100644 --- a/app/src/main/res/layout/view_currency_icon.xml +++ b/app/src/main/res/layout/view_currency_icon.xml @@ -2,20 +2,65 @@ + android:layout_width="48dp" + android:layout_height="48dp" + tools:layout_gravity="center" + tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> - + + + + + + + + + + + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index db78a57ae5..1bdf655a71 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -160,7 +160,7 @@ Не удалось отправить электронное письмо Причина: %s Не могу отправить транзакцию - Причина: %s. Хотите отправить отзыв? + Причина: %s У Вас возникли трудности со сканированием вашей карты? Пожалуйста, попробуйте приложить карту точно так, как показано на анимации, или обратитесь в поддержку Отмена diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index 119c258d60..405518a258 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -55,7 +55,7 @@ Failed to send email Reason: %s Can\'t send a transaction - Reason: %s. Do you want to send feedback? + Reason: %s Are you having difficulty scanning your card? Please try to tap the card exactly as shown in the animation or request support. I\'m okay diff --git a/dependencies.gradle b/dependencies.gradle index cbd16111f5..ab3dab0c54 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,8 +1,9 @@ ext.versions = [ kotlin : '1.6.10', build_gradle : '7.1.3', - tamgem_card_sdk : 'develop-157', - tamgem_blockchain_sdk: 'develop-99', + tangem_card_sdk : 'develop-159', + tangem_blockchain_sdk: 'develop-100', + // tangem_blockchain_sdk: '0.0.1', ] ext.environmentConfig = [ diff --git a/domain/build.gradle b/domain/build.gradle index d7a8832fd4..83c9fe6ff1 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -59,9 +59,9 @@ dependencies { implementation implementation(project(path: ':network')) implementation implementation(project(path: ':common')) - implementation "com.tangem:blockchain:${versions.tamgem_blockchain_sdk}" - implementation "com.tangem.tangem-sdk-kotlin:core:${versions.tamgem_card_sdk}" - implementation "com.tangem.tangem-sdk-kotlin:android:${versions.tamgem_card_sdk}" + implementation "com.tangem:blockchain:${versions.tangem_blockchain_sdk}" + implementation "com.tangem.tangem-sdk-kotlin:core:${versions.tangem_card_sdk}" + implementation "com.tangem.tangem-sdk-kotlin:android:${versions.tangem_card_sdk}" // Kotlin coroutines implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' diff --git a/domain/src/main/java/com/tangem/domain/common/TapWorkarounds.kt b/domain/src/main/java/com/tangem/domain/common/TapWorkarounds.kt index e273a4dae2..a11441c51c 100644 --- a/domain/src/main/java/com/tangem/domain/common/TapWorkarounds.kt +++ b/domain/src/main/java/com/tangem/domain/common/TapWorkarounds.kt @@ -9,20 +9,18 @@ import java.util.* [REDACTED_AUTHOR] */ object TapWorkarounds { - fun isStart2CoinIssuer(cardIssuer: String?): Boolean { return cardIssuer?.lowercase(Locale.US) == START_2_COIN_ISSUER } val Card.isStart2Coin: Boolean get() = isStart2CoinIssuer(issuer.name) - + val Card.isSaltPay: Boolean + get() = false //TODO fix when we know which cards are SaltPay cards val Card.isTestCard: Boolean get() = batchId == TEST_CARD_BATCH && cardId.startsWith(TEST_CARD_ID_STARTS_WITH) - val Card.useOldStyleDerivation: Boolean get() = batchId == "AC01" || batchId == "AC02" || batchId == "CB95" - val Card.derivationStyle: DerivationStyle? get() = if (!settings.isHDWalletAllowed) { null @@ -42,21 +40,19 @@ object TapWorkarounds { return false } - fun Card.isTangemNote(): Boolean = tangemNoteBatches.contains(batchId) - + fun Card.isTangemNote(): Boolean = tangemNoteBatches.contains(batchId) || isSaltPay fun isTangemWalletBatch(card: Card): Boolean = tangemWalletBatches.contains(card.batchId) - - fun Card.getTangemNoteBlockchain(): Blockchain? = tangemNoteBatches[batchId] + fun Card.getTangemNoteBlockchain(): Blockchain? = + tangemNoteBatches[batchId] ?: if (isSaltPay) Blockchain.Gnosis else null private const val START_2_COIN_ISSUER = "start2coin" private const val TEST_CARD_BATCH = "99FF" private const val TEST_CARD_ID_STARTS_WITH = "FF99" - private val excludedBatches = listOf( "0027", "0030", "0031", - "0035" + "0035", ) private val excludedIssuers = listOf( diff --git a/network/build.gradle b/network/build.gradle index dce3040c09..e7fccceda6 100644 --- a/network/build.gradle +++ b/network/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation implementation(project(path: ':common')) // Tangem sdk's - implementation "com.tangem.tangem-sdk-kotlin:core:${versions.tamgem_card_sdk}" + implementation "com.tangem.tangem-sdk-kotlin:core:${versions.tangem_card_sdk}" // Kotlin coroutines implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'