Updated on 2026-08-14

This commit is contained in:
Tangem 2021-02-16 22:45:54 +03:00
parent 02d07a4e29
commit 0b4ef307aa
34 changed files with 567 additions and 163 deletions

View file

@ -6,6 +6,7 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.tap.common.redux.ErrorAction
import com.tangem.tap.common.redux.ToastNotificationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.features.send.redux.states.FeeType
import com.tangem.tap.features.send.redux.states.MainCurrencyType
import com.tangem.tap.features.send.redux.states.SendButtonState
@ -146,4 +147,5 @@ sealed class SendAction : SendScreenAction {
object Hide : Dialog()
}
data class SetWarnings(val warningList: List<WarningMessage>) : SendAction()
}

View file

@ -44,6 +44,7 @@ private class SendReducer : SendInternalReducer {
is SendAction.ChangeSendButtonState -> sendState.copy(sendButtonState = action.state)
is SendAction.Dialog.ShowTezosWarningDialog -> sendState.copy(dialog = action)
is SendAction.Dialog.Hide -> sendState.copy(dialog = null)
is SendAction.SetWarnings -> sendState.copy(sendWarningsList = action.warningList)
else -> return sendState
}

View file

@ -8,6 +8,7 @@ import com.tangem.tap.common.CurrencyConverter
import com.tangem.tap.common.entities.TapCurrency
import com.tangem.tap.common.text.DecimalDigitsInputFilter
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.store
import org.rekotlin.StateType
@ -37,6 +38,7 @@ data class SendState(
val amountState: AmountState = AmountState(),
val feeState: FeeState = FeeState(),
val receiptState: ReceiptState = ReceiptState(),
val sendWarningsList: List<WarningMessage> = listOf(),
val sendButtonState: SendButtonState = SendButtonState.DISABLED,
val dialog: SendAction.Dialog? = null
) : SendScreenState {

View file

@ -9,9 +9,12 @@ import android.view.inputmethod.EditorInfo
import android.widget.EditText
import androidx.core.view.postDelayed
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.textfield.TextInputEditText
import com.tangem.Message
import com.tangem.merchant.common.toggleWidget.ToggleWidget
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
@ -22,6 +25,7 @@ import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity
import com.tangem.tap.common.snackBar.MaxAmountSnackbar
import com.tangem.tap.common.text.truncateMiddleWith
import com.tangem.tap.common.toggleWidget.*
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.features.send.BaseStoreFragment
import com.tangem.tap.features.send.redux.*
import com.tangem.tap.features.send.redux.AddressPayIdActionUi.*
@ -30,10 +34,14 @@ import com.tangem.tap.features.send.redux.FeeActionUi.*
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.ui.SpacesItemDecoration
import com.tangem.tap.features.wallet.ui.WarningMessagesAdapter
import com.tangem.tap.mainScope
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_send.*
import kotlinx.android.synthetic.main.fragment_send.rv_warning_messages
import kotlinx.android.synthetic.main.fragment_wallet.*
import kotlinx.android.synthetic.main.layout_send_address_payid.*
import kotlinx.android.synthetic.main.layout_send_amount.*
import kotlinx.android.synthetic.main.layout_send_fee.*
@ -50,6 +58,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
lateinit var sendBtn: ToggleWidget
private lateinit var etAmountToSend: TextInputEditText
private lateinit var warningsAdapter: WarningMessagesAdapter
private fun initSendButtonStates() {
sendBtn = ToggleWidget(flSendButtonContainer, btnSend, progress, ProgressState.None())
@ -70,6 +79,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
setupTransactionExtrasLayout()
setupAmountLayout()
setupFeeLayout()
setupWarningMessages()
btnSend.setOnClickListener {
store.dispatch(SendActionUi.SendAmountToRecipient(
@ -227,6 +237,17 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
}
}
private fun setupWarningMessages() {
warningsAdapter = WarningMessagesAdapter()
val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
rv_warning_messages.layoutManager = layoutManager
rv_warning_messages.addItemDecoration(SpacesItemDecoration(rv_warning_messages.dpToPx(16f).toInt()))
rv_warning_messages.adapter = warningsAdapter
val warnings = store.state.globalState.warningManager?.getWarnings(WarningMessage.Location.SendScreen) ?: listOf()
store.dispatch(SendAction.SetWarnings(warnings))
}
override fun subscribeToStore() {
store.subscribe(sendSubscriber) { appState ->
appState.skipRepeats { oldState, newState ->

View file

@ -22,6 +22,7 @@ import com.tangem.tap.features.send.redux.states.*
import com.tangem.tap.features.send.ui.FeeUiHelper
import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog
import com.tangem.tap.features.wallet.ui.WarningMessagesAdapter
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.btn_expand_collapse.*
@ -122,6 +123,12 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
sendFragment.sendBtn.setState(ProgressState.Progress(), true)
}
}
val rv = fg.rv_warning_messages
val adapter = rv.adapter as? WarningMessagesAdapter ?: return
adapter.submitList(state.sendWarningsList)
rv.show(state.sendWarningsList.isNotEmpty())
}
private fun handleAddressPayIdState(fg: BaseStoreFragment, state: AddressPayIdState) {

View file

@ -9,6 +9,7 @@ import com.tangem.tap.common.redux.ErrorAction
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.wallet.R
import org.rekotlin.Action
@ -34,7 +35,7 @@ sealed class WalletAction : Action {
object CheckHashesCountOnline : WalletAction()
object NeedToCheckHashesCountOnline : WalletAction()
object ConfirmHashesCount : WalletAction()
data class ShowWarning(val warningType: WarningType) : WalletAction()
data class SetWarnings(val warningList: List<WarningMessage>) : WalletAction()
object SaveCardId : WalletAction()
object UpdateWallet : WalletAction() {

View file

@ -20,6 +20,8 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.PayIdManager
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.TopUpHelper
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.domain.twins.TwinsHelper
import com.tangem.tap.domain.twins.isTwinCard
@ -172,13 +174,17 @@ class WalletMiddleware {
}
}
is WalletAction.CheckIfWarningNeeded -> {
val card = store.state.globalState.scanNoteResponse?.card
val validator = store.state.globalState.scanNoteResponse?.walletManager
as? SignatureCountValidator
if (card != null && !preferencesStorage.wasCardScannedBefore(card.cardId)) {
val result = checkIfWarningNeeded(card, validator)
if (result != null) store.dispatch(WalletAction.ShowWarning(result))
val globalState = store.state.globalState
val validator = globalState.scanNoteResponse?.walletManager as? SignatureCountValidator
globalState.scanNoteResponse?.card?.let { card ->
store.state.globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local)
if (card.getType() != CardType.Release) addWarningMessage(WarningMessagesManager.devCardWarning())
if (!preferencesStorage.wasCardScannedBefore(card.cardId)) {
checkIfWarningNeeded(card, validator)?.let { addWarningMessage(it) }
}
updateWarningMessages()
}
}
is WalletAction.CheckHashesCountOnline -> checkHashesCountOnline()
is WalletAction.SaveCardId -> {
@ -230,17 +236,13 @@ class WalletMiddleware {
}
private fun checkIfWarningNeeded(
card: Card, signatureCountValidator: SignatureCountValidator? = null
): WarningType? {
if (card.getType() != CardType.Release) {
return WarningType.DevCard
}
card: Card, signatureCountValidator: SignatureCountValidator? = null,
): WarningMessage? {
if (card.isTwinCard()) return null
return if (signatureCountValidator == null) {
if (card.walletSignedHashes ?: 0 > 0) {
WarningType.CardSignedHashesBefore
WarningMessagesManager.alreadySignedHashesWarning()
} else {
store.dispatch(WalletAction.SaveCardId)
null
@ -272,14 +274,24 @@ class WalletMiddleware {
}
is SimpleResult.Failure ->
if (result.error is BlockchainSdkError.SignatureCountNotMatched) {
store.dispatch(WalletAction.ShowWarning(WarningType.CardSignedHashesBefore))
addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true)
} else if (card.walletSignedHashes ?: 0 > 0) {
store.dispatch(WalletAction.ShowWarning(WarningType.CardSignedHashesBefore))
addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true)
}
}
}
}
}
private fun addWarningMessage(warning: WarningMessage, autoUpdate: Boolean = false) {
store.state.globalState.warningManager?.addWarning(warning)
if (autoUpdate) updateWarningMessages()
}
private fun updateWarningMessages() {
val warningManager = store.state.globalState.warningManager ?: return
store.dispatch(WalletAction.SetWarnings(warningManager.getWarnings(WarningMessage.Location.MainScreen)))
}
}
private class TopUpMiddleware {

View file

@ -215,8 +215,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
)
}
is WalletAction.Send.Cancel -> newState = newState.copy(walletDialog = null)
is WalletAction.ShowWarning ->
newState = newState.copy(walletDialog = WalletDialog.WarningDialog(action.warningType))
is WalletAction.SetWarnings -> newState = newState.copy(mainWarningsList = action.warningList)
is WalletAction.NeedToCheckHashesCountOnline ->
newState = newState.copy(hashesCountVerified = false)
is WalletAction.ConfirmHashesCount ->

View file

@ -7,6 +7,7 @@ import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.address.AddressType
import com.tangem.tap.common.entities.Button
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
@ -27,6 +28,7 @@ data class WalletState(
val mainButton: WalletMainButton = WalletMainButton.SendButton(false),
val topUpState: TopUpState = TopUpState(),
val twinCardsState: TwinCardsState? = null,
val mainWarningsList: List<WarningMessage> = mutableListOf()
) : StateType {
val showDetails: Boolean =
currencyData.status != com.tangem.tap.features.wallet.ui.BalanceStatus.EmptyCard &&
@ -49,13 +51,9 @@ sealed class WalletDialog {
data class CreatePayIdDialog(val creatingPayIdState: CreatingPayIdState?) : WalletDialog()
data class SelectAmountToSendDialog(val amounts: List<Amount>?) : WalletDialog()
data class WarningDialog(val type: WarningType) : WalletDialog()
data class TwinsOnboardingFragment(val secondCardId: String): WalletDialog()
}
enum class WarningType { CardSignedHashesBefore, DevCard }
enum class ProgressState { Loading, Done, Error }
enum class ErrorType { NoInternetConnection }

View file

@ -8,6 +8,7 @@ import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionInflater
import com.google.android.material.snackbar.Snackbar
import com.squareup.picasso.Picasso
@ -15,6 +16,7 @@ import com.tangem.blockchain.blockchains.bitcoin.BitcoinAddressType
import com.tangem.blockchain.blockchains.cardano.CardanoAddressType
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.address.AddressType
import com.tangem.tangem_sdk_new.extensions.dpToPx
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
@ -24,7 +26,6 @@ import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog
import com.tangem.tap.features.wallet.ui.dialogs.PayIdDialog
import com.tangem.tap.features.wallet.ui.dialogs.QrDialog
import com.tangem.tap.features.wallet.ui.dialogs.WarningDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.card_balance.*
@ -41,7 +42,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
private var dialog: Dialog? = null
private var snackbar: Snackbar? = null
private lateinit var viewAdapter: PendingTransactionsAdapter
private lateinit var pendingTransactionAdapter: PendingTransactionsAdapter
private lateinit var warningsAdapter: WarningMessagesAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -78,14 +80,22 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
setupWarningsRecyclerView()
setupTransactionsRecyclerView()
}
private fun setupWarningsRecyclerView() {
warningsAdapter = WarningMessagesAdapter()
val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
rv_warning_messages.layoutManager = layoutManager
rv_warning_messages.addItemDecoration(SpacesItemDecoration(rv_warning_messages.dpToPx(16f).toInt()))
rv_warning_messages.adapter = warningsAdapter
}
private fun setupTransactionsRecyclerView() {
viewAdapter = PendingTransactionsAdapter()
pendingTransactionAdapter = PendingTransactionsAdapter()
rv_pending_transaction.layoutManager = LinearLayoutManager(context)
rv_pending_transaction.adapter = viewAdapter
rv_pending_transaction.adapter = pendingTransactionAdapter
}
@ -122,12 +132,11 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
setupAddressCard(state)
setupCardImage(state.cardImage)
viewAdapter.submitList(state.pendingTransactions)
if (state.pendingTransactions.isEmpty()) {
rv_pending_transaction.hide()
} else {
rv_pending_transaction.show()
}
warningsAdapter.submitList(state.mainWarningsList)
rv_warning_messages.show(state.mainWarningsList.isNotEmpty())
pendingTransactionAdapter.submitList(state.pendingTransactions)
rv_pending_transaction.show(state.pendingTransactions.isNotEmpty())
handleDialogs(state.walletDialog)
@ -292,11 +301,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
this.show(walletDialog.amounts)
}
}
is WalletDialog.WarningDialog -> {
if (dialog == null) dialog = WarningDialog(requireContext()).apply {
this.show(walletDialog.type)
}
}
null -> {
dialog?.dismiss()
dialog = null

View file

@ -0,0 +1,78 @@
package com.tangem.tap.features.wallet.ui
import android.graphics.Rect
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.layout_warning.view.*
class WarningMessagesAdapter : ListAdapter<WarningMessage, WarningMessageVH>(DiffUtilCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WarningMessageVH {
val inflater = LayoutInflater.from(parent.context)
val layout = inflater.inflate(R.layout.layout_warning, parent, false)
return WarningMessageVH(layout)
}
override fun onBindViewHolder(holder: WarningMessageVH, position: Int) {
holder.bind(currentList[position])
}
object DiffUtilCallback : DiffUtil.ItemCallback<WarningMessage>() {
override fun areContentsTheSame(oldItem: WarningMessage, newItem: WarningMessage) = oldItem == newItem
override fun areItemsTheSame(oldItem: WarningMessage, newItem: WarningMessage) = oldItem == newItem
}
}
class WarningMessageVH(val view: View) : RecyclerView.ViewHolder(view) {
fun bind(warning: WarningMessage) {
setBgColor(warning.priority)
setText(warning)
setupOkButton(warning)
}
private fun setText(warning: WarningMessage) {
fun getString(resId: Int?, default: String) = if (resId == null) default else view.getString(resId)
view.tv_title.text = getString(warning.titleResId, warning.title)
view.tv_message.text = getString(warning.messageResId, warning.message)
}
private fun setBgColor(priority: WarningMessage.Priority) {
val color = when (priority) {
WarningMessage.Priority.Info -> R.color.warning_info
WarningMessage.Priority.Warning -> R.color.warning_warning
WarningMessage.Priority.Critical -> R.color.warning_critical
}
view.card_view.setCardBackgroundColor(view.context.resources.getColor(color))
}
private fun setupOkButton(warning: WarningMessage) {
view.btn_got_it.show(warning.type == WarningMessage.Type.Temporary)
view.btn_got_it.setOnClickListener {
store.dispatch(GlobalAction.HideWarningMessage(warning))
}
}
}
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
}
}

View file

@ -1,30 +0,0 @@
package com.tangem.tap.features.wallet.ui.dialogs
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WarningType
import com.tangem.tap.store
import com.tangem.wallet.R
class WarningDialog(context: Context) : AlertDialog(context) {
private val dialog: AlertDialog = Builder(context)
.setTitle(context.getString(R.string.common_warning))
.setPositiveButton(context.getString(R.string.common_ok)) { _, _ ->
dismiss()
}.setOnDismissListener {
store.dispatch(WalletAction.SaveCardId)
store.dispatch(WalletAction.HideDialog)
}
.create()
fun show(warningType: WarningType) {
val messageRes = when (warningType) {
WarningType.CardSignedHashesBefore -> R.string.alert_card_signed_transactions
WarningType.DevCard -> R.string.alert_developer_card
}
dialog.setMessage(dialog.context.getString(messageRes))
dialog.show()
}
}