From 15b5d45d3532914ba66bf59ddde21533634d14a8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 17 May 2022 16:33:42 +0300 Subject: [PATCH 01/10] Updated on 2026-08-14 --- app/build.gradle | 3 +- domain/build.gradle | 3 +- .../domain/common/extensions/CardSdk.kt | 46 +++++++++++- .../addCustomToken/redux/AddCustomTokenHub.kt | 21 +++--- .../redux/AddCustomTokenState.kt | 73 +++++++------------ 5 files changed, 81 insertions(+), 65 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 550a83ec13..d69b335737 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,7 +93,8 @@ 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:develop-77' + implementation 'com.tangem:blockchain:develop-78' +// implementation 'com.tangem:blockchain:0.0.1' implementation 'com.tangem.tangem-sdk-kotlin:core:develop-142' implementation 'com.tangem.tangem-sdk-kotlin:android:develop-142' diff --git a/domain/build.gradle b/domain/build.gradle index b156723a15..8dd3f9a54c 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -60,7 +60,8 @@ dependencies { implementation implementation(project(path: ':common')) // Tangem sdk's - implementation 'com.tangem:blockchain:develop-77' + implementation 'com.tangem:blockchain:develop-78' +// implementation 'com.tangem:blockchain:0.0.1' implementation 'com.tangem.tangem-sdk-kotlin:core:develop-142' implementation 'com.tangem.tangem-sdk-kotlin:android:develop-142' diff --git a/domain/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt b/domain/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt index ca37d132ba..ea805497c6 100644 --- a/domain/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt +++ b/domain/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt @@ -1,12 +1,50 @@ package com.tangem.domain.common.extensions +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.card.Card +import com.tangem.common.card.EllipticCurve import com.tangem.common.card.FirmwareVersion +import com.tangem.domain.common.TapWorkarounds.isTestCard /** [REDACTED_AUTHOR] */ -val FirmwareVersion.Companion.SolanaAvailable - get() = FirmwareVersion(4, 12) - val FirmwareVersion.Companion.SolanaTokensAvailable - get() = FirmwareVersion(4, 52) \ No newline at end of file + get() = FirmwareVersion(4, 52) + +fun Card.supportedBlockchains(): List { + val supportedBlockchains = when { + firmwareVersion < FirmwareVersion.MultiWalletAvailable -> { + Blockchain.fromCurve(EllipticCurve.Secp256k1) + } + else -> { + Blockchain.fromCurve(EllipticCurve.Secp256k1) + + Blockchain.fromCurve(EllipticCurve.Ed25519) + } + } + val filtered = supportedBlockchains.filter { isTestCard == it.isTestnet() } + return filtered +} + +fun Card.supportedTokens(): List { + val tokensSupportedByBlockchain = supportedBlockchains().filter { it.canHandleTokens() }.toMutableList() + val tokensSupportedByCard = when { + firmwareVersion >= FirmwareVersion.SolanaTokensAvailable -> tokensSupportedByBlockchain + else -> { + tokensSupportedByBlockchain.apply { + remove(Blockchain.Solana) + remove(Blockchain.SolanaTestnet) + } + } + } + val filtered = tokensSupportedByCard.filter { isTestCard == it.isTestnet() } + return filtered +} + +fun Card.canHandleBlockchain(blockchain: Blockchain): Boolean { + return this.supportedBlockchains().contains(blockchain) +} + +fun Card.canHandleToken(blockchain: Blockchain): Boolean { + return this.supportedTokens().contains(blockchain) +} \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt index 73db7237db..82aadfae67 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt @@ -13,6 +13,7 @@ import com.tangem.domain.DomainDialog import com.tangem.domain.DomainWrapped import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.extensions.fromNetworkId +import com.tangem.domain.common.extensions.supportedTokens import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.form.* import com.tangem.domain.features.addCustomToken.* @@ -141,16 +142,11 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT } } - //TODO: Solana token - /** - * This feature is only needed until Solana coins are added. - * While they are not there - this function excludes the Solana blockchain if the user has - * filled in at least one field of the token. - */ private suspend fun changeBlockchainNetworkList() { val state = hubState val networkBlockchainList = Network.getField().itemList - val newNetworkBlockchainList: List = state.getNetworks(state.getCustomTokenType()) + val card = requireNotNull(globalState.scanResponse?.card) + val newNetworkBlockchainList: List = state.getNetworks(card, state.getCustomTokenType()) val listsIdentical = newNetworkBlockchainList.toSet() == networkBlockchainList.toSet() if (listsIdentical) return @@ -537,9 +533,7 @@ private class AddCustomTokenReducer( } is OnCreate -> { val card = requireNotNull(globalState.scanResponse?.card) - val supportedTokenNetworkIds = AddCustomTokenState.getSupportedTokensBlockchain().map { - it.toNetworkId() - } + val supportedTokenNetworkIds = card.supportedTokens().map { it.toNetworkId() } val tangemTechServiceManager = AddCustomTokenService( tangemTechService = globalState.networkServices.tangemTechService, supportedTokenNetworkIds = supportedTokenNetworkIds @@ -550,7 +544,7 @@ private class AddCustomTokenReducer( DerivationStyle.LEGACY -> derivationPathState.copy(isVisible = true) null, DerivationStyle.NEW -> derivationPathState.copy(isVisible = false) } - val form = Form(AddCustomTokenState.createFormFields(CustomTokenType.Blockchain)) + val form = Form(AddCustomTokenState.createFormFields(card, CustomTokenType.Blockchain)) state.copy( cardDerivationStyle = card.derivationStyle, form = form, @@ -558,7 +552,10 @@ private class AddCustomTokenReducer( screenState = state.screenState.copy(derivationPath = derivationPathState) ) } - is OnDestroy -> state.reset() + is OnDestroy -> { + val card = requireNotNull(globalState.scanResponse?.card) + state.reset(card) + } is UpdateForm -> { updateFormState(action.state) } diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt index 8d9714ce91..2a5f43a3fd 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt @@ -2,17 +2,17 @@ package com.tangem.domain.features.addCustomToken.redux import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.DerivationStyle -import com.tangem.common.card.FirmwareVersion +import com.tangem.common.card.Card import com.tangem.common.json.MoshiJsonConverter import com.tangem.domain.AddCustomTokenError import com.tangem.domain.DomainWrapped -import com.tangem.domain.common.extensions.SolanaAvailable -import com.tangem.domain.common.extensions.SolanaTokensAvailable +import com.tangem.domain.common.TapWorkarounds.isTestCard +import com.tangem.domain.common.extensions.supportedBlockchains +import com.tangem.domain.common.extensions.supportedTokens import com.tangem.domain.common.form.* import com.tangem.domain.features.addCustomToken.* import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* import com.tangem.domain.redux.DomainState -import com.tangem.domain.redux.domainStore import com.tangem.domain.redux.state.StringActionStateConverter import org.rekotlin.Action import org.rekotlin.StateType @@ -109,12 +109,12 @@ data class AddCustomTokenState( null } - fun reset(): AddCustomTokenState { + fun reset(card: Card): AddCustomTokenState { return this.copy( appSavedCurrencies = null, onTokenAddCallback = null, cardDerivationStyle = null, - form = Form(createFormFields(CustomTokenType.Blockchain)), + form = Form(createFormFields(card, CustomTokenType.Blockchain)), formErrors = emptyMap(), tokenId = null, warnings = emptySet(), @@ -135,8 +135,8 @@ data class AddCustomTokenState( .getConvertedData() } - fun getNetworks(type: CustomTokenType): List { - return getNetworksList(type) + fun getNetworks(card: Card, type: CustomTokenType): List { + return getNetworksList(card, type) } companion object { @@ -154,66 +154,45 @@ data class AddCustomTokenState( else -> derivationNetwork }.derivationPath(derivationStyle) - internal fun createFormFields(type: CustomTokenType): List> { + internal fun createFormFields(card: Card, type: CustomTokenType): List> { return listOf( TokenField(ContractAddress), - TokenBlockchainField(Network, getNetworksList(type)), + TokenBlockchainField(Network, getNetworksList(card, type)), TokenField(Name), TokenField(Symbol), TokenField(Decimals), - TokenDerivationPathField(DerivationPath, getSupportedDerivations()), + TokenDerivationPathField(DerivationPath, getSupportedDerivations(card)), ) } - /** - * Serves to determine the tokens that can be received from the TangemTech service - */ - internal fun getSupportedTokensBlockchain(): List { - return Blockchain.values() - .filter { !it.isTestnet() } - .filter { it.canHandleTokens() } - } - /** * Serves to determine the networks (blockchains & tokens) that can be selected by Form.Networks. * Blockchain.Unknown - is the default selection */ - private fun getNetworksList(type: CustomTokenType): List { + private fun getNetworksList(card: Card, type: CustomTokenType): List { val evmBlockchains = Blockchain.values() .filter { it.isEvm() } - .filter { !it.isTestnet() } + .filter { card.isTestCard == it.isTestnet() } val additionalBlockchains = listOf( - Blockchain.Binance, - Blockchain.Solana, + Blockchain.Binance, Blockchain.BinanceTestnet, + Blockchain.Solana, Blockchain.SolanaTestnet, ) - val networks = (evmBlockchains + additionalBlockchains).toMutableList() - - //life hack - val fwCardVersion = domainStore.state.globalState.scanResponse?.card?.firmwareVersion - ?: FirmwareVersion(0, 0) - - val solanaUnsupportedByCard = fwCardVersion < FirmwareVersion.SolanaAvailable - val solanaTokensUnsupportedByCard = fwCardVersion < FirmwareVersion.SolanaTokensAvailable - if (solanaUnsupportedByCard || solanaTokensUnsupportedByCard) networks.remove(Blockchain.Solana) - - if (type == CustomTokenType.Token) networks.removeAll(getUnsupportedTokensBlockchain()) + val supportedByCard = when (type) { + CustomTokenType.Blockchain -> card.supportedBlockchains() + CustomTokenType.Token -> card.supportedTokens() + } + val typedNetworksList = (evmBlockchains + additionalBlockchains) + .filter { supportedByCard.contains(it) } + .toMutableList() val default = Blockchain.Unknown - networks.add(0, default) + typedNetworksList.add(0, default) - return networks + return typedNetworksList } - private fun getUnsupportedTokensBlockchain(): List { - return Blockchain.values() - .filter { !it.isTestnet() } - .filter { !it.canHandleTokens() } - .toMutableList() - } - - private fun createFormValidators(): Map> { return mapOf( ContractAddress to TokenContractAddressValidator(), @@ -224,9 +203,9 @@ data class AddCustomTokenState( ) } - private fun getSupportedDerivations(): List { + private fun getSupportedDerivations(card: Card): List { val evmBlockchains = Blockchain.values().filter { - !it.isTestnet() && it.getChainId() != null + card.isTestCard == it.isTestnet() && it.getChainId() != null } return listOf(Blockchain.Unknown) + evmBlockchains } From ea834a06363f579ab8fffbaf3bb6800f4104b119 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 18 May 2022 21:31:21 +0300 Subject: [PATCH 02/10] Updated on 2026-08-14 --- .../recyclerView/SpaceItemDecoration.kt | 72 ++++++++++ .../tap/features/send/ui/SendFragment.kt | 5 +- .../tap/features/wallet/redux/WalletAction.kt | 4 +- .../tap/features/wallet/redux/WalletState.kt | 2 +- .../redux/middlewares/WalletMiddleware.kt | 7 +- .../wallet/redux/reducers/WalletReducer.kt | 31 ++-- .../wallet/ui/WalletDetailsFragment.kt | 68 +++++---- .../tap/features/wallet/ui/WalletFragment.kt | 5 +- .../WalletDetailWarningMessagesAdapter.kt | 64 +++++++++ .../ui/adapters/WarningMessagesAdapter.kt | 29 +--- .../res/layout/fragment_wallet_details.xml | 38 +---- .../layout/layout_wallet_details_warning.xml | 47 ------ app/src/main/res/layout/layout_warning.xml | 135 +++++------------- .../main/res/layout/layout_warning_card.xml | 14 ++ .../res/layout/layout_warning_card_action.xml | 88 ++++++++++++ 15 files changed, 343 insertions(+), 266 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/recyclerView/SpaceItemDecoration.kt create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletDetailWarningMessagesAdapter.kt delete mode 100644 app/src/main/res/layout/layout_wallet_details_warning.xml create mode 100644 app/src/main/res/layout/layout_warning_card.xml create mode 100644 app/src/main/res/layout/layout_warning_card_action.xml diff --git a/app/src/main/java/com/tangem/tap/common/recyclerView/SpaceItemDecoration.kt b/app/src/main/java/com/tangem/tap/common/recyclerView/SpaceItemDecoration.kt new file mode 100644 index 0000000000..b1d16fc3f6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/recyclerView/SpaceItemDecoration.kt @@ -0,0 +1,72 @@ +package com.tangem.tap.common.recyclerView + +import android.graphics.Rect +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import com.tangem.tangem_sdk_new.extensions.dpToPx + +class SpaceItemDecoration( + private val horizontalSpaceDp: Float, + private val verticalSpaceDp: Float, +) : RecyclerView.ItemDecoration() { + + private lateinit var space: Space + + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + if (state.itemCount == 0) return + if (!::space.isInitialized) { + space = Space( + view.dpToPx(horizontalSpaceDp).toInt(), + view.dpToPx(verticalSpaceDp).toInt() + ) + } + + outRect.left = space.horizontal + outRect.right = space.horizontal + + when (state.itemCount) { + 1 -> { + outRect.top = space.vertical + outRect.bottom = space.vertical + } + else -> { + val adapterPosition = parent.getChildAdapterPosition(view) + if (adapterPosition == -1) return + + when (adapterPosition) { + 0 -> { + // first + outRect.top = space.vertical + outRect.bottom = space.vertical / 2 + } + state.itemCount - 1 -> { + // last + outRect.top = space.vertical / 2 + outRect.bottom = space.vertical + } + else -> { + // middle + outRect.top = space.vertical / 2 + outRect.bottom = space.vertical / 2 + } + } + } + } + } + + private data class Space( + val horizontal: Int, + val vertical: Int, + ) + + companion object { + fun all(dp: Float): SpaceItemDecoration = SpaceItemDecoration(dp, dp) + fun vertical(dp: Float): SpaceItemDecoration = SpaceItemDecoration(0f, dp) + fun horizontal(dp: Float): SpaceItemDecoration = SpaceItemDecoration(dp, 0f) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index d999d79bcb..24c70b0b0a 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -14,13 +14,13 @@ import androidx.recyclerview.widget.RecyclerView import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.textfield.TextInputEditText import com.tangem.Message -import com.tangem.tangem_sdk_new.extensions.dpToPx import com.tangem.tangem_sdk_new.extensions.hideSoftKeyboard import com.tangem.tap.common.KeyboardObserver import com.tangem.tap.common.entities.TapCurrency import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.extensions.setOnImeActionListener import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity +import com.tangem.tap.common.recyclerView.SpaceItemDecoration import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.common.snackBar.MaxAmountSnackbar import com.tangem.tap.common.text.truncateMiddleWith @@ -36,7 +36,6 @@ import com.tangem.tap.features.send.redux.states.FeeType import com.tangem.tap.features.send.redux.states.MainCurrencyType import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.ui.adapters.SpacesItemDecoration import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter import com.tangem.tap.mainScope import com.tangem.tap.store @@ -244,7 +243,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { warningsAdapter = WarningMessagesAdapter() val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) rvWarningMessages.layoutManager = layoutManager - rvWarningMessages.addItemDecoration(SpacesItemDecoration(rvWarningMessages.dpToPx(16f).toInt())) + rvWarningMessages.addItemDecoration(SpaceItemDecoration.all(16f)) rvWarningMessages.adapter = warningsAdapter store.dispatch(SendAction.Warnings.Update) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index aa7c653e8f..c4e722e6c3 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -157,10 +157,10 @@ sealed class WalletAction : Action { data class ChangeSelectedAddress(val type: AddressType) : WalletAction() data class SetWalletRent( - val blockchain: BlockchainNetwork, + val wallet: Wallet, val minRent: String, val rentExempt: String ) : WalletAction() - data class RemoveWalletRent(val blockchain: BlockchainNetwork) : WalletAction() + data class RemoveWalletRent(val wallet: Wallet) : WalletAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index 278ccee818..cd5c4eefd2 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -345,7 +345,7 @@ data class WalletData( val fiatRate: BigDecimal? = null, val mainButton: WalletMainButton = WalletMainButton.SendButton(false), val currency: Currency, - val warningRent: WalletRent? = null, + val walletRent: WalletRent? = null, ) { fun shouldShowMultipleAddress(): Boolean { val listOfAddresses = walletAddresses?.list ?: return false diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 034122f2c1..d87020fb27 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -24,7 +24,6 @@ import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.failedRates import com.tangem.tap.domain.loadedRates -import com.tangem.tap.domain.tokens.BlockchainNetwork import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.PrepareSendScreen @@ -330,14 +329,12 @@ class WalletMiddleware { val currency = walletManager.wallet.blockchain.currency if (show) { dispatchOnMain(WalletAction.SetWalletRent( - blockchain = BlockchainNetwork.fromWalletManager(walletManager), + wallet = walletManager.wallet, minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency"), rentExempt = ("${rentExempt.stripZeroPlainString()} $currency") )) } else { - dispatchOnMain(WalletAction.RemoveWalletRent( - blockchain = BlockchainNetwork.fromWalletManager(walletManager), - )) + dispatchOnMain(WalletAction.RemoveWalletRent(walletManager.wallet)) } } is com.tangem.blockchain.extensions.Result.Failure -> {} diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index 97e6e778ec..fdfee46cfa 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -146,12 +146,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState { } else { val walletManager = newState.getWalletManager(action.blockchain) ?: return newState val currencies = listOf(Currency.fromBlockchainNetwork(action.blockchain)) + - walletManager.cardTokens.map { - Currency.fromBlockchainNetwork( - action.blockchain, - it - ) - } + walletManager.cardTokens.map { + Currency.fromBlockchainNetwork(action.blockchain, it) + } val newWallets = newState.walletsData.filter { currencies.contains(it.currency) } .map { wallet -> wallet.copy( @@ -305,23 +302,15 @@ private fun internalReduce(action: Action, state: AppState): WalletState { ) } is WalletAction.SetWalletRent -> { - var walletData = newState.getWalletData(action.blockchain) - if (walletData == null) { - newState - } else { - walletData = - walletData.copy(warningRent = WalletRent(action.minRent, action.rentExempt)) - newState = newState.updateWalletsData(listOf(walletData)) - } + val walletStore = newState.getWalletStore(action.wallet) ?: return newState + val walletRent = WalletRent(action.minRent, action.rentExempt) + val walletsData = walletStore.walletsData.map { it.copy(walletRent = walletRent) } + newState = newState.updateWalletsData(walletsData) } is WalletAction.RemoveWalletRent -> { - var walletData = newState.getWalletData(action.blockchain) - if (walletData == null) { - newState - } else { - walletData = walletData.copy(warningRent = null) - newState = newState.updateWalletsData(listOf(walletData)) - } + val walletStore = newState.getWalletStore(action.wallet) ?: return newState + val walletsData = walletStore.walletsData.map { it.copy(walletRent = null) } + newState = newState.updateWalletsData(walletsData) } } return newState 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 2c1fa686cd..331f406807 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 @@ -13,10 +13,10 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.transition.TransitionInflater import by.kirich1409.viewbindingdelegate.viewBinding import com.squareup.picasso.Picasso -import com.tangem.common.extensions.guard import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.TestActions import com.tangem.tap.common.extensions.* +import com.tangem.tap.common.recyclerView.SpaceItemDecoration import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.tokens.BlockchainNetwork @@ -24,6 +24,8 @@ import com.tangem.tap.features.onboarding.getQRReceiveMessage import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.redux.* 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.adapters.WalletDetailsWarning import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog import com.tangem.tap.features.wallet.ui.test.TestWalletDetails import com.tangem.tap.store @@ -35,6 +37,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreSubscriber { private lateinit var pendingTransactionAdapter: PendingTransactionsAdapter + private lateinit var warningMessagesAdapter: WalletDetailWarningMessagesAdapter private var dialog: Dialog? = null private val binding: FragmentWalletDetailsBinding by viewBinding(FragmentWalletDetailsBinding::bind) @@ -72,6 +75,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), setupTransactionsRecyclerView() setupButtons() + setupWarningsRecyclerView() setupTestActionButton() } @@ -81,6 +85,13 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), rvPendingTransaction.adapter = pendingTransactionAdapter } + private fun setupWarningsRecyclerView() = with(binding) { + warningMessagesAdapter = WalletDetailWarningMessagesAdapter() + rvWarningMessages.layoutManager = LinearLayoutManager(requireContext()) + rvWarningMessages.adapter = warningMessagesAdapter + rvWarningMessages.addItemDecoration(SpaceItemDecoration.vertical(8f)) + } + private fun setupButtons() = with(binding) { btnConfirm.text = getString(R.string.wallet_button_send) btnConfirm.setOnClickListener { store.dispatch(WalletAction.Send()) } @@ -116,8 +127,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), handleDialogs(state.walletDialog) handleCurrencyIcon(selectedWallet) - handleWalletRent(selectedWallet.warningRent) - handleNotEnoughFundsOnMainCurrency(selectedWallet) + handleWarnings(selectedWallet) binding.srlWalletDetails.setOnRefreshListener { if (selectedWallet.currencyData.status != BalanceStatus.Loading) { @@ -162,31 +172,36 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), btnSell.show(selectedWallet.tradeCryptoState.sellingAllowed) } - private fun handleWalletRent(rent: WalletRent?) = with(binding) { - val rent = rent.guard { - lWarning.root.hide() - return - } - val warningMessage = requireContext().getString( - R.string.solana_rent_warning, rent.minRentValue, rent.rentExemptValue - ) - lWarning.tvWarningMessage.text = warningMessage - lWarning.root.show() - } - - private fun handleNotEnoughFundsOnMainCurrency(selectedWalletData: WalletData) = with(binding) { - if (selectedWalletData.currency.isBlockchain()) return@with - - if (selectedWalletData.shouldShowCoinAmountWarning()) { - val blockchainName = selectedWalletData.currency.blockchain.fullName - val warningMessage = requireContext().getString( - R.string.token_details_send_blocked_fee_format, blockchainName, blockchainName + private fun handleWarnings(selectedWallet: WalletData) = with(binding) { + val walletWarnings = mutableListOf() + if (selectedWallet.currencyData.status == BalanceStatus.SameCurrencyTransactionInProgress) { + val txInProgress = WalletDetailsWarning( + title = R.string.common_warning, + message = R.string.wallet_pending_transaction_warning, ) - lWarning.tvWarningMessage.text = warningMessage - lWarning.root.show() - } else { - lWarning.root.hide() + walletWarnings.add(0, txInProgress) } + if (selectedWallet.walletRent != null) { + val rent = selectedWallet.walletRent + val rentWarning = WalletDetailsWarning( + title = R.string.common_warning, + message = R.string.solana_rent_warning, + messageArgs = listOf(rent.minRentValue, rent.rentExemptValue) + ) + walletWarnings.add(rentWarning) + } + if (!selectedWallet.currency.isBlockchain() && selectedWallet.shouldShowCoinAmountWarning()) { + val blockchainName = selectedWallet.currency.blockchain.fullName + val notEnoughBalanceForFee = WalletDetailsWarning( + title = R.string.common_warning, + message = R.string.token_details_send_blocked_fee_format, + messageArgs = listOf(blockchainName, blockchainName) + ) + walletWarnings.add(notEnoughBalanceForFee) + } + + warningMessagesAdapter.submitList(walletWarnings) + rvWarningMessages.show(walletWarnings.isNotEmpty()) } private fun handleCurrencyIcon(wallet: WalletData) = with(binding.lWalletDetails.lBalance) { @@ -299,7 +314,6 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), ) } } - binding.cardPendingTransactionWarning.show(data.status == BalanceStatus.SameCurrencyTransactionInProgress) } private fun handleDialogs(walletDialog: StateDialog?) { 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 f633010d0a..acb6309e60 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 @@ -13,9 +13,9 @@ import androidx.recyclerview.widget.RecyclerView import androidx.transition.TransitionInflater import by.kirich1409.viewbindingdelegate.viewBinding import com.squareup.picasso.Picasso -import com.tangem.tangem_sdk_new.extensions.dpToPx import com.tangem.tap.MainActivity import com.tangem.tap.common.extensions.show +import com.tangem.tap.common.recyclerView.SpaceItemDecoration import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction @@ -25,7 +25,6 @@ import com.tangem.tap.domain.statePrinter.printWalletState import com.tangem.tap.domain.termsOfUse.CardTou import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.wallet.redux.* -import com.tangem.tap.features.wallet.ui.adapters.SpacesItemDecoration 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.SingleWalletView @@ -100,7 +99,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber(DiffUtilCallback()) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WalletDetailsWarningMessageVH { + val inflater = LayoutInflater.from(parent.context) + val binding = LayoutWarningCardBinding.inflate(inflater, parent, false) + + return WalletDetailsWarningMessageVH(binding) + } + + override fun onBindViewHolder(holder: WalletDetailsWarningMessageVH, position: Int) { + holder.bind(currentList[position]) + } + + private class DiffUtilCallback : DiffUtil.ItemCallback() { + override fun areContentsTheSame(oldItem: WalletDetailsWarning, newItem: WalletDetailsWarning) = + oldItem == newItem + + override fun areItemsTheSame(oldItem: WalletDetailsWarning, newItem: WalletDetailsWarning) = + oldItem == newItem + } +} + +class WalletDetailsWarningMessageVH( + val binding: LayoutWarningCardBinding +) : RecyclerView.ViewHolder(binding.root) { + + fun bind(warning: WalletDetailsWarning) { + binding.warningCard.setCardBackgroundColor(binding.root.getColor(R.color.darkGray2)) + setText(warning) + } + + private fun setText(warning: WalletDetailsWarning) = with(binding.warningContentContainer) { + fun getString( + stringResId: Int, + formatArgs: List = emptyList() + ) = root.getString(stringResId, *formatArgs.toTypedArray()) + + tvTitle.text = getString(warning.title) + tvMessage.text = getString(warning.message, warning.messageArgs) + } +} + +data class WalletDetailsWarning( + val title: Int, + val message: Int, + val titleArgs: List = emptyList(), + val messageArgs: List = emptyList(), +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt index a566f7b5e6..946e347a65 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.wallet.ui.adapters import android.content.res.Resources -import android.graphics.Rect import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -9,7 +8,6 @@ import androidx.core.os.ConfigurationCompat import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.ItemDecoration import com.google.android.play.core.review.ReviewManagerFactory import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.extensions.* @@ -20,13 +18,13 @@ import com.tangem.tap.features.feedback.RateCanBeBetterEmail import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R -import com.tangem.wallet.databinding.LayoutWarningBinding +import com.tangem.wallet.databinding.LayoutWarningCardActionBinding import timber.log.Timber class WarningMessagesAdapter : ListAdapter(DiffUtilCallback) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WarningMessageVH { - val binding = LayoutWarningBinding.inflate( + val binding = LayoutWarningCardActionBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return WarningMessageVH(binding) @@ -45,7 +43,7 @@ class WarningMessagesAdapter : ListAdapter(Dif } } -class WarningMessageVH(val binding: LayoutWarningBinding) : RecyclerView.ViewHolder(binding.root) { +class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(warning: WarningMessage) { setBgColor(warning.priority) @@ -53,7 +51,7 @@ class WarningMessageVH(val binding: LayoutWarningBinding) : RecyclerView.ViewHol setupControlButtons(warning) } - private fun setText(warning: WarningMessage) = with(binding) { + private fun setText(warning: WarningMessage) = with(binding.warningContentContainer) { fun getString(resId: Int?, default: String, formatArgs: String? = null) = if (resId == null) default else root.getString(resId, formatArgs) @@ -71,7 +69,7 @@ class WarningMessageVH(val binding: LayoutWarningBinding) : RecyclerView.ViewHol WarningMessage.Priority.Warning -> R.color.warning_warning WarningMessage.Priority.Critical -> R.color.warning_critical } - binding.cardView.setCardBackgroundColor(binding.root.getColor(color)) + binding.warningCardAction.setCardBackgroundColor(binding.root.getColor(color)) } private fun setupControlButtons(warning: WarningMessage) = when (warning.type) { @@ -115,7 +113,7 @@ class WarningMessageVH(val binding: LayoutWarningBinding) : RecyclerView.ViewHol val buttonTitle = binding.root.getString( warning.buttonTextId ?: R.string.how_to_got_it_button ) - binding.btnGotIt.setOnClickListener (buttonAction) + binding.btnGotIt.setOnClickListener(buttonAction) binding.btnGotIt.text = buttonTitle } WarningMessage.Type.AppRating -> { @@ -161,19 +159,4 @@ class WarningMessageVH(val binding: LayoutWarningBinding) : RecyclerView.ViewHol } } } -} - -class SpacesItemDecoration(private val spacePx: Int) : ItemDecoration() { - override fun getItemOffsets( - outRect: Rect, - view: View, - parent: RecyclerView, - state: RecyclerView.State - ) { - outRect.left = spacePx - outRect.right = spacePx - - outRect.top = spacePx / 2 - outRect.top = spacePx / 2 - } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_wallet_details.xml b/app/src/main/res/layout/fragment_wallet_details.xml index a2d946a3ca..cd51f4c2fa 100644 --- a/app/src/main/res/layout/fragment_wallet_details.xml +++ b/app/src/main/res/layout/fragment_wallet_details.xml @@ -98,48 +98,22 @@ android:layout_marginTop="16dp" app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" /> - - - - - - + app:constraint_referenced_ids="l_wallet_details" /> diff --git a/app/src/main/res/layout/layout_wallet_details_warning.xml b/app/src/main/res/layout/layout_wallet_details_warning.xml deleted file mode 100644 index 43eeed159a..0000000000 --- a/app/src/main/res/layout/layout_wallet_details_warning.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_warning.xml b/app/src/main/res/layout/layout_warning.xml index adab88869c..7ba7e096ef 100644 --- a/app/src/main/res/layout/layout_warning.xml +++ b/app/src/main/res/layout/layout_warning.xml @@ -1,110 +1,41 @@ - + android:background="@android:color/transparent"> - + android:layout_marginStart="16dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="8dp" + android:textColor="@android:color/white" + android:textStyle="bold" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="Test title" /> - + - - - - -