Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Edit transaction fee screen for EIP-1559 and legacy transactions #1943

Merged
merged 8 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ import io.gnosis.safe.ui.terms.TermsBottomSheetDialog
import io.gnosis.safe.ui.transactions.TransactionListFragment
import io.gnosis.safe.ui.transactions.TransactionsFragment
import io.gnosis.safe.ui.transactions.details.*
import io.gnosis.safe.ui.transactions.execution.TxAdvancedParamsFragment
import io.gnosis.safe.ui.transactions.execution.TxEditFee1559Fragment
import io.gnosis.safe.ui.transactions.execution.TxEditFeeLegacyFragment
import io.gnosis.safe.ui.updates.UpdatesFragment
import io.gnosis.safe.ui.whatsnew.WhatsNewDialog

Expand Down Expand Up @@ -84,6 +87,12 @@ interface ViewComponent {

fun inject(fragment: AdvancedTransactionDetailsFragment)

fun inject(fragment: TxEditFee1559Fragment)

fun inject(fragment: TxEditFeeLegacyFragment)

fun inject(fragment: TxAdvancedParamsFragment)

fun inject(fragment: ConfirmRejectionFragment)

fun inject(fragment: SettingsFragment)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import io.gnosis.safe.ui.transactions.TransactionsViewModel
import io.gnosis.safe.ui.transactions.details.ConfirmRejectionViewModel
import io.gnosis.safe.ui.transactions.details.TransactionDetailsActionViewModel
import io.gnosis.safe.ui.transactions.details.TransactionDetailsViewModel
import io.gnosis.safe.ui.transactions.execution.TxEditFeeViewModel
import io.gnosis.safe.ui.updates.UpdatesViewModel
import javax.inject.Singleton

Expand Down Expand Up @@ -194,6 +195,11 @@ abstract class ViewModelFactoryModule {
@ViewModelKey(ConfirmRejectionViewModel::class)
abstract fun providesConfirmRejectionViewModel(viewModel: ConfirmRejectionViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(TxEditFeeViewModel::class)
abstract fun providesTxEditFeeViewModel(viewModel: TxEditFeeViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(AdvancedSafeSettingsViewModel::class)
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/io/gnosis/safe/di/modules/ViewModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import io.gnosis.safe.ui.transactions.TransactionsViewModel
import io.gnosis.safe.ui.transactions.details.ConfirmRejectionViewModel
import io.gnosis.safe.ui.transactions.details.TransactionDetailsActionViewModel
import io.gnosis.safe.ui.transactions.details.TransactionDetailsViewModel
import io.gnosis.safe.ui.transactions.execution.TxEditFeeViewModel
import io.gnosis.safe.ui.updates.UpdatesViewModel
import java.lang.ref.WeakReference
import javax.inject.Named
Expand Down Expand Up @@ -213,6 +214,10 @@ class ViewModule(
@ForView
fun providesConfirmRejectionViewModel(provider: ViewModelProvider) = provider[ConfirmRejectionViewModel::class.java]

@Provides
@ForView
fun providesTxEditFeeViewModel(provider: ViewModelProvider) = provider[TxEditFeeViewModel::class.java]

@Provides
@ForView
fun providesAdvancedSafeSettingsViewModel(provider: ViewModelProvider) = provider[AdvancedSafeSettingsViewModel::class.java]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import pm.gnosis.svalinn.common.utils.snackbar
import pm.gnosis.svalinn.common.utils.visible
import javax.inject.Inject

class AdvancedTransactionDetailsFragment : BaseViewBindingFragment<FragmentTransactionDetailsAdvancedBinding>() {
open class AdvancedTransactionDetailsFragment : BaseViewBindingFragment<FragmentTransactionDetailsAdvancedBinding>() {

override fun screenId() = ScreenId.TRANSACTIONS_DETAILS_ADVANCED

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.gnosis.safe.ui.transactions.execution

import io.gnosis.safe.ScreenId
import io.gnosis.safe.ui.transactions.details.AdvancedTransactionDetailsFragment

class TxAdvancedParamsFragment : AdvancedTransactionDetailsFragment() {
override fun screenId() = ScreenId.TRANSACTIONS_EXEC_REVIEW_ADVANCED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
package io.gnosis.safe.ui.transactions.execution

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.setFragmentResult
import androidx.navigation.Navigation
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import io.gnosis.safe.R
import io.gnosis.safe.ScreenId
import io.gnosis.safe.databinding.FragmentTxEditFeeBinding
import io.gnosis.safe.di.components.ViewComponent
import io.gnosis.safe.ui.base.fragment.BaseViewBindingFragment
import io.gnosis.safe.utils.appendLink
import io.gnosis.safe.utils.toColor
import java.math.BigInteger
import javax.inject.Inject

class TxEditFee1559Fragment : BaseViewBindingFragment<FragmentTxEditFeeBinding>() {

override fun screenId() = ScreenId.TRANSACTIONS_EXEC_EDIT_FEE

private val navArgs by navArgs<TxEditFee1559FragmentArgs>()
private val chain by lazy { navArgs.chain }
private val nonce by lazy { navArgs.nonce }
private val minNonce by lazy { BigInteger(navArgs.minNonce) }
private val gasLimit by lazy { navArgs.gasLimit }
private val maxPriorityFee by lazy { navArgs.maxPriorityFee }
private val maxFee by lazy { navArgs.maxFee }

@Inject
lateinit var viewModel: TxEditFeeViewModel


override fun inject(component: ViewComponent) {
component.inject(this)
}

override fun inflateBinding(
inflater: LayoutInflater,
container: ViewGroup?
): FragmentTxEditFeeBinding =
FragmentTxEditFeeBinding.inflate(inflater, container, false)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
with(binding) {
backButton.setOnClickListener {
findNavController().navigateUp()
}
chainRibbon.text = chain.name
chainRibbon.setTextColor(
chain.textColor.toColor(
requireContext(),
R.color.white
)
)
chainRibbon.setBackgroundColor(
chain.backgroundColor.toColor(
requireContext(),
R.color.primary
)
)
nonceValue.setText(nonce)
nonceValue.doOnTextChanged { text, _, _, _ ->
viewModel.validate1559Inputs(
requireContext(),
minNonce,
nonceValue.text.toString(),
gasLimitValue.text.toString(),
maxPriorityFeeValue.text.toString(),
maxFeeValue.text.toString()
)
}
gasLimitValue.setText(gasLimit)
gasLimitValue.doOnTextChanged { text, _, _, _ ->
viewModel.validate1559Inputs(
requireContext(),
minNonce,
nonceValue.text.toString(),
gasLimitValue.text.toString(),
maxPriorityFeeValue.text.toString(),
maxFeeValue.text.toString()
)
}
maxPriorityFeeValue.setText(maxPriorityFee)
maxPriorityFeeValue.doOnTextChanged { text, _, _, _ ->
viewModel.validate1559Inputs(
requireContext(),
minNonce,
nonceValue.text.toString(),
gasLimitValue.text.toString(),
maxPriorityFeeValue.text.toString(),
maxFeeValue.text.toString()
)
}
maxFeeValue.setText(maxFee)
maxFeeValue.doOnTextChanged { text, _, _, _ ->
viewModel.validate1559Inputs(
requireContext(),
minNonce,
nonceValue.text.toString(),
gasLimitValue.text.toString(),
maxPriorityFeeValue.text.toString(),
maxFeeValue.text.toString()
)
}
totalFeeLabel.text = getString(
R.string.tx_exec_estimated_fee_price,
viewModel.totalFee1559(
requireContext(),
chain,
gasLimitValue.text.toString(),
maxFeeValue.text.toString()
)
)
configHowtoLink.appendLink(
urlText = resources.getString(R.string.tx_advanced_params_config_howto),
url = resources.getString(R.string.tx_advanced_params_config_howto_link),
linkIcon = R.drawable.ic_external_link_green_16dp
)
saveButton.setOnClickListener {
setFragmentResult(
REQUEST_EDIT_FEE,
bundleOf(
RESULT_NONCE to nonceValue.text.toString(),
RESULT_GAS_LIMIT to gasLimitValue.text.toString(),
RESULT_MAX_PRIORITY_FEE to maxPriorityFeeValue.text.toString(),
RESULT_MAX_FEE to maxFeeValue.text.toString()
)
)
Navigation.findNavController(it).navigateUp()
}
saveButton.isEnabled = false
}
viewModel.state.observe(viewLifecycleOwner) { state ->
when (state) {
is TxEditFeeState -> {
binding.saveButton.isEnabled = state.saveEnabled
state.viewAction?.let { action ->
when (action) {
is Validate1559FeeData -> {
validateInputs(
action.nonceError,
action.gasLimitError,
action.maxPriorityFeeError,
action.maxFeeError
)
updateTotalFee()
}
}
}
}
}
}
}

private fun validateInputs(
nonceError: String?,
gasLimitError: String?,
maxPriorityFeeError: String?,
maxFeeError: String?
) {
with(binding) {
nonceLayout.error = nonceError
gasLimitLayout.error = gasLimitError
maxPriorityFeeLayout.error = maxPriorityFeeError
maxFeeLayout.error = maxFeeError
}
}

private fun updateTotalFee() {
with(binding) {
totalFeeLabel.text = getString(
R.string.tx_exec_estimated_fee_price,
viewModel.totalFee1559(
requireContext(),
chain,
gasLimitValue.text.toString(),
maxFeeValue.text.toString()
)
)
}
}

companion object {
const val REQUEST_EDIT_FEE = "request_edit_fee"
const val RESULT_NONCE = "result_nonce"
const val RESULT_GAS_LIMIT = "result_gas_limit"
const val RESULT_MAX_PRIORITY_FEE = "result_max_priority_fee"
const val RESULT_MAX_FEE = "result_max_fee"
}
}
Loading