diff --git a/app/build.gradle b/app/build.gradle index ff8f810f68..48cacfc0f1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,6 +94,7 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation 'com.tangem:blockchain:develop-81' +// 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/app/src/main/java/com/tangem/tap/common/moduleMessage/domain/DomainErrorConverters.kt b/app/src/main/java/com/tangem/tap/common/moduleMessage/domain/DomainErrorConverters.kt index aeb3723a1a..7f5221041e 100644 --- a/app/src/main/java/com/tangem/tap/common/moduleMessage/domain/DomainErrorConverters.kt +++ b/app/src/main/java/com/tangem/tap/common/moduleMessage/domain/DomainErrorConverters.kt @@ -28,6 +28,7 @@ private class AddCustomTokenConverter( val rawMessage = when (customTokenError) { AddCustomTokenError.Warning.PotentialScamToken -> R.string.custom_token_validation_error_not_found AddCustomTokenError.Warning.TokenAlreadyAdded -> R.string.custom_token_validation_error_already_added + AddCustomTokenError.Warning.UnsupportedSolanaToken -> R.string.alert_manage_tokens_unsupported_message AddCustomTokenError.InvalidContractAddress -> R.string.custom_token_creation_error_invalid_contract_address AddCustomTokenError.NetworkIsNotSelected -> R.string.custom_token_creation_error_network_not_selected AddCustomTokenError.InvalidDerivationPath -> R.string.custom_token_creation_error_invalid_derivation_path 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 4fa59376fb..4837aa7449 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.FiatCurrency 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/tokens/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt index 0c3387d81a..1c91c67b5f 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt @@ -58,8 +58,7 @@ class TokensMiddleware { currenciesRepository.getSupportedTokens(isTestcard) .filter(action.supportedBlockchains?.toSet()) } - val delay = async { delay(600) } - delay.await() + delay(600) store.dispatchOnMain(TokensAction.LoadCurrencies.Success(currencies.await())) } } @@ -78,8 +77,8 @@ class TokensMiddleware { val blockchainsToRemove = currentBlockchains.filter { !action.addedBlockchains.contains(it) } val tokensToAdd = action.addedTokens.filter { !currentTokens.contains(it) } - val tokensToRemove = currentTokens.filter { - token -> !action.addedTokens.any { it.token == token.token } + val tokensToRemove = currentTokens.filter { token -> + !action.addedTokens.any { it.token == token.token } } val derivationStyle = scanResponse.card.derivationStyle diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt index 05014acd99..6c8e6a58e8 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt @@ -4,6 +4,7 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.DerivationStyle import com.tangem.blockchain.common.Token import com.tangem.domain.common.ScanResponse +import com.tangem.domain.common.extensions.canHandleToken import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.tap.domain.tokens.Currency import com.tangem.tap.features.wallet.redux.WalletData @@ -19,7 +20,12 @@ data class TokensState( val allowToAdd: Boolean = true, val derivationStyle: DerivationStyle? = null, val scanResponse: ScanResponse? = null -) : StateType +) : StateType { + + fun canHandleToken(token: TokenWithBlockchain): Boolean { + return scanResponse?.card?.canHandleToken(token.blockchain) ?: false + } +} typealias ContractAddress = String @@ -62,7 +68,7 @@ fun List.filter(supportedBlockchains: Set?): List, List) -> Unit, onNetworkItemClicked: (ContractAddress) -> Unit ) { - + val context = LocalContext.current val addedTokensState = remember { mutableStateOf(tokensState.value.addedTokens) } val addedBlockchainsState = remember { mutableStateOf(tokensState.value.addedBlockchains) } @@ -77,7 +79,7 @@ fun CurrenciesScreen( visible = tokensState.value.currencies.isEmpty(), enter = fadeIn(), exit = fadeOut() - ){ + ) { Box( contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize() @@ -93,8 +95,9 @@ fun CurrenciesScreen( exit = fadeOut(animationSpec = tween(1000)) ) { Column { - if (tokensState.value.scanResponse?.card?.useOldStyleDerivation == true) CurrenciesWarning() + val showHeader = tokensState.value.scanResponse?.card?.useOldStyleDerivation == true ListOfCurrencies( + header = { if (showHeader) CurrenciesWarning() }, currencies = tokensState.value.currencies, nonRemovableTokens = tokensState.value.nonRemovableTokens, nonRemovableBlockchains = tokensState.value.nonRemovableBlockchains, @@ -102,7 +105,19 @@ fun CurrenciesScreen( addedBlockchains = addedBlockchainsState.value, searchInput = searchInput.value, allowToAdd = tokensState.value.allowToAdd, - onAddCurrencyToggled = onAddCurrencyToggleClick, + onAddCurrencyToggled = { currency, token -> + onAddCurrencyToggleClick(currency, token) + token?.let { + if (!tokensState.value.canHandleToken(it)) { + val dialog = AppDialog.SimpleOkDialog( + header = context.getString(R.string.common_warning), + message = context.getString(R.string.alert_manage_tokens_unsupported_message) + ) { onAddCurrencyToggleClick(currency, it) } + store.dispatchDialogShow(dialog) + } + } + + }, onNetworkItemClicked = onNetworkItemClicked ) } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/ui/compose/ListOfCurrencies.kt b/app/src/main/java/com/tangem/tap/features/tokens/ui/compose/ListOfCurrencies.kt index 2443f25df4..d55cbd35a4 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/ui/compose/ListOfCurrencies.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/ui/compose/ListOfCurrencies.kt @@ -17,6 +17,7 @@ import com.tangem.tap.features.tokens.redux.TokenWithBlockchain @Composable fun ListOfCurrencies( + header: @Composable ()->Unit, currencies: List, nonRemovableTokens: List, nonRemovableBlockchains: List, @@ -54,6 +55,7 @@ fun ListOfCurrencies( .contains(searchInput) }.toList() } + item { header() } items(filteredCurrencies) { currency -> CurrencyItem( currency = currency, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt new file mode 100644 index 0000000000..f54300f3ec --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt @@ -0,0 +1,20 @@ +package com.tangem.tap.features.wallet.models + +sealed class WalletWarning( + val showingPosition: Int, +) { + object TransactionInProgress : WalletWarning(10) + object SolanaTokensUnsupported : WalletWarning(20) + data class BalanceNotEnoughForFee(val blockchainFullName: String) : WalletWarning(30) + data class Rent(val walletRent: WalletRent) : WalletWarning(40) +} + +data class WalletWarningDescription( + val title: String, + val message: String, +) + +data class WalletRent( + val minRentValue: String, + val rentExemptValue: String, +) \ No newline at end of file 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 b9e63b7190..6df77f4a30 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 @@ -5,6 +5,7 @@ import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.AddressType import com.tangem.blockchain.extensions.isAboveZero import com.tangem.common.extensions.isZero +import com.tangem.domain.common.extensions.canHandleToken import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.features.addCustomToken.CustomCurrency import com.tangem.tap.common.entities.Button @@ -19,10 +20,7 @@ import com.tangem.tap.domain.extensions.toSendableAmounts import com.tangem.tap.domain.tokens.BlockchainNetwork import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.tokens.redux.TokenWithBlockchain -import com.tangem.tap.features.wallet.models.PendingTransaction -import com.tangem.tap.features.wallet.models.TotalBalance -import com.tangem.tap.features.wallet.models.toPendingTransactions -import com.tangem.tap.features.wallet.models.toPendingTransactionsForToken +import com.tangem.tap.features.wallet.models.* import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceWidgetData import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager @@ -357,31 +355,44 @@ 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 return listOfAddresses.size > 1 } - fun shouldShowCoinAmountWarning(): Boolean = when (currency) { - is Currency.Blockchain -> false - is Currency.Token -> blockchainAmountIsEmpty() && !tokenAmountIsEmpty() - } - fun shouldEnableTokenSendButton(): Boolean = !blockchainAmountIsEmpty() || !tokenAmountIsEmpty() - private fun blockchainAmountIsEmpty(): Boolean = - currencyData.blockchainAmount?.isZero() ?: false + fun assembleWarnings(): List { + val blockchain = currency.blockchain + val walletWarnings = mutableListOf() + if (currencyData.status == BalanceStatus.SameCurrencyTransactionInProgress) { + walletWarnings.add(WalletWarning.TransactionInProgress) + } + if (currency.isBlockchain()) { + if (blockchain == Blockchain.Solana || blockchain == Blockchain.SolanaTestnet) { + val card = store.state.globalState.scanResponse?.card + if (card?.canHandleToken(blockchain) == false) { + walletWarnings.add(WalletWarning.SolanaTokensUnsupported) + } + } + } + if (walletRent != null) { + walletWarnings.add(WalletWarning.Rent(walletRent)) + } + if (!currency.isBlockchain() && (blockchainAmountIsEmpty() && !tokenAmountIsEmpty())) { + val fullName = currency.blockchain.fullName + walletWarnings.add(WalletWarning.BalanceNotEnoughForFee(fullName)) + } + return walletWarnings.sortedBy { it.showingPosition } + } + + private fun blockchainAmountIsEmpty(): Boolean = currencyData.blockchainAmount?.isZero() ?: false private fun tokenAmountIsEmpty(): Boolean = currencyData.amount?.isZero() == true } -data class WalletRent( - val minRentValue: String, - val rentExemptValue: String -) - sealed interface Currency { val coinId: String? 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 8b9033964d..3f3c285830 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 eb079d3116..ffa919f475 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 @@ -14,6 +14,7 @@ import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.getArtworkUrl import com.tangem.tap.domain.getFirstToken import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.features.wallet.models.WalletRent import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceWidgetData @@ -148,12 +149,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( @@ -307,21 +305,15 @@ private fun internalReduce(action: Action, state: AppState): WalletState { ) } is WalletAction.SetWalletRent -> { - var walletData = newState.getWalletData(action.blockchain) - if (walletData != null) { - 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) { - 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) } else -> { /* no-op */ } 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..f267c25f89 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,7 @@ 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.dialogs.AmountToSendDialog import com.tangem.tap.features.wallet.ui.test.TestWalletDetails import com.tangem.tap.store @@ -35,6 +36,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 +74,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), setupTransactionsRecyclerView() setupButtons() + setupWarningsRecyclerView() setupTestActionButton() } @@ -81,6 +84,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 +126,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 +171,12 @@ 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 handleWarnings(selectedWallet: WalletData) = with(binding) { + val converter = WalletWarningConverter(requireContext()) + val warningDetails = selectedWallet.assembleWarnings().map { converter.convert(it) } - 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 - ) - lWarning.tvWarningMessage.text = warningMessage - lWarning.root.show() - } else { - lWarning.root.hide() - } + warningMessagesAdapter.submitList(warningDetails) + rvWarningMessages.show(warningDetails.isNotEmpty()) } private fun handleCurrencyIcon(wallet: WalletData) = with(binding.lWalletDetails.lBalance) { @@ -299,7 +289,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 { + + override fun convert(message: WalletWarning): WalletWarningDescription { + val warningMessage = when (message) { + is WalletWarning.BalanceNotEnoughForFee -> { + context.getString( + R.string.token_details_send_blocked_fee_format, + message.blockchainFullName, message.blockchainFullName + ) + } + WalletWarning.SolanaTokensUnsupported -> { + context.getString(R.string.warning_token_send_unsupported_message) + } + WalletWarning.TransactionInProgress -> { + context.getString(R.string.wallet_pending_transaction_warning) + } + is WalletWarning.Rent -> { + context.getString( + R.string.solana_rent_warning, + message.walletRent.minRentValue, message.walletRent.rentExemptValue + ) + } + } + return WalletWarningDescription(context.getString(R.string.common_warning), warningMessage) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletDetailWarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletDetailWarningMessagesAdapter.kt new file mode 100644 index 0000000000..5164928569 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletDetailWarningMessagesAdapter.kt @@ -0,0 +1,52 @@ +package com.tangem.tap.features.wallet.ui.adapters + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.tangem.tap.common.extensions.getColor +import com.tangem.tap.features.wallet.models.WalletWarningDescription +import com.tangem.wallet.R +import com.tangem.wallet.databinding.LayoutWarningCardBinding + +/** +[REDACTED_AUTHOR] + */ +class WalletDetailWarningMessagesAdapter + : ListAdapter(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: WalletWarningDescription, newItem: WalletWarningDescription) = + oldItem == newItem + + override fun areItemsTheSame(oldItem: WalletWarningDescription, newItem: WalletWarningDescription) = + oldItem == newItem + } +} + +class WalletDetailsWarningMessageVH( + val binding: LayoutWarningCardBinding +) : RecyclerView.ViewHolder(binding.root) { + + fun bind(warning: WalletWarningDescription) { + binding.warningCard.setCardBackgroundColor(binding.root.getColor(R.color.darkGray2)) + setText(warning) + } + + private fun setText(warning: WalletWarningDescription) = with(binding.warningContentContainer) { + tvTitle.text = warning.title + tvMessage.text = warning.message + } +} \ 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" /> - + - - - - -