Updated on 2026-08-14

This commit is contained in:
Tangem 2022-05-18 21:31:21 +03:00
parent 15b5d45d35
commit ea834a0636
15 changed files with 343 additions and 266 deletions

View file

@ -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)
}
}

View file

@ -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)

View file

@ -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()
}

View file

@ -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

View file

@ -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 -> {}

View file

@ -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

View file

@ -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<WalletState> {
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<WalletDetailsWarning>()
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?) {

View file

@ -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<Walle
val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
with(binding) {
rvWarningMessages.layoutManager = layoutManager
rvWarningMessages.addItemDecoration(SpacesItemDecoration(rvWarningMessages.dpToPx(16f).toInt()))
rvWarningMessages.addItemDecoration(SpaceItemDecoration.all(16f))
rvWarningMessages.adapter = warningsAdapter
}
}

View file

@ -0,0 +1,64 @@
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.common.extensions.getString
import com.tangem.wallet.R
import com.tangem.wallet.databinding.LayoutWarningCardBinding
/**
[REDACTED_AUTHOR]
*/
class WalletDetailWarningMessagesAdapter
: ListAdapter<WalletDetailsWarning, WalletDetailsWarningMessageVH>(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<WalletDetailsWarning>() {
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<String> = 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<String> = emptyList(),
val messageArgs: List<String> = emptyList(),
)

View file

@ -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<WarningMessage, WarningMessageVH>(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<WarningMessage, WarningMessageVH>(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
}
}