From 316a305f5335573fc2ff7eb133fa1c5a35454abb Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Fri, 6 Apr 2018 17:01:08 -0500 Subject: [PATCH] Improve UI, logging and error handling - Use ExceptionHandler at publishBlindVote - Add hasEnoughBsqFunds method to BlindVoteService - Show fee and confirmation popup at publish blind vote - Add GUIUtils.showBsqFeeInfoPopup to avoid code duplication - Add busyAnimation to publish blind vote button - Improve logs --- .../main/dao/proposal/BaseProposalView.java | 10 +- .../main/dao/proposal/ProposalListItem.java | 1 - .../proposal/active/ActiveProposalsView.java | 94 ++++++++++--------- .../proposal/closed/ClosedProposalsView.java | 9 +- .../dao/proposal/make/MakeProposalView.java | 35 +++---- .../dao/proposal/myvotes/MyVotesView.java | 10 +- src/main/java/bisq/desktop/util/GUIUtil.java | 18 ++++ 7 files changed, 104 insertions(+), 73 deletions(-) diff --git a/src/main/java/bisq/desktop/main/dao/proposal/BaseProposalView.java b/src/main/java/bisq/desktop/main/dao/proposal/BaseProposalView.java index 371fe77ba3..3658f3e8e7 100644 --- a/src/main/java/bisq/desktop/main/dao/proposal/BaseProposalView.java +++ b/src/main/java/bisq/desktop/main/dao/proposal/BaseProposalView.java @@ -23,12 +23,14 @@ import bisq.desktop.components.AutoTooltipLabel; import bisq.desktop.components.AutoTooltipTableColumn; import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.components.TableGroupHeadline; +import bisq.desktop.util.BSFormatter; import bisq.desktop.util.BsqFormatter; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.dao.blockchain.BsqBlockChain; import bisq.core.dao.blockchain.ReadableBsqBlockChain; import bisq.core.dao.blockchain.vo.BsqBlock; +import bisq.core.dao.param.DaoParamService; import bisq.core.dao.vote.PeriodService; import bisq.core.dao.vote.proposal.Proposal; import bisq.core.dao.vote.proposal.ProposalPayload; @@ -73,8 +75,10 @@ public abstract class BaseProposalView extends ActivatableView i protected final ProposalService proposalService; protected final ReadableBsqBlockChain readableBsqBlockChain; + protected final DaoParamService daoParamService; protected final BsqWalletService bsqWalletService; protected final BsqFormatter bsqFormatter; + protected final BSFormatter btcFormatter; protected final ObservableList proposalListItems = FXCollections.observableArrayList(); protected final SortedList sortedList = new SortedList<>(proposalListItems); @@ -101,13 +105,17 @@ public abstract class BaseProposalView extends ActivatableView i protected BaseProposalView(ProposalService proposalService, BsqWalletService bsqWalletService, ReadableBsqBlockChain readableBsqBlockChain, + DaoParamService daoParamService, PeriodService periodService, - BsqFormatter bsqFormatter) { + BsqFormatter bsqFormatter, + BSFormatter btcFormatter) { this.proposalService = proposalService; this.bsqWalletService = bsqWalletService; this.readableBsqBlockChain = readableBsqBlockChain; + this.daoParamService = daoParamService; this.periodService = periodService; this.bsqFormatter = bsqFormatter; + this.btcFormatter = btcFormatter; } @Override diff --git a/src/main/java/bisq/desktop/main/dao/proposal/ProposalListItem.java b/src/main/java/bisq/desktop/main/dao/proposal/ProposalListItem.java index 9990370112..8d8bedcf22 100644 --- a/src/main/java/bisq/desktop/main/dao/proposal/ProposalListItem.java +++ b/src/main/java/bisq/desktop/main/dao/proposal/ProposalListItem.java @@ -162,7 +162,6 @@ public class ProposalListItem implements BsqBlockChain.Listener { //TODO } } else { - log.error("actionButtonIconView.setVisible(false);"); actionButtonIconView.setVisible(false); } } diff --git a/src/main/java/bisq/desktop/main/dao/proposal/active/ActiveProposalsView.java b/src/main/java/bisq/desktop/main/dao/proposal/active/ActiveProposalsView.java index cf30c1c16e..49b0a0c1cb 100644 --- a/src/main/java/bisq/desktop/main/dao/proposal/active/ActiveProposalsView.java +++ b/src/main/java/bisq/desktop/main/dao/proposal/active/ActiveProposalsView.java @@ -18,36 +18,37 @@ package bisq.desktop.main.dao.proposal.active; import bisq.desktop.common.view.FxmlView; +import bisq.desktop.components.BusyAnimation; import bisq.desktop.components.InputTextField; import bisq.desktop.components.TitledGroupBg; import bisq.desktop.main.dao.proposal.BaseProposalView; import bisq.desktop.main.dao.proposal.ProposalListItem; import bisq.desktop.main.overlays.popups.Popup; +import bisq.desktop.util.BSFormatter; import bisq.desktop.util.BsqFormatter; +import bisq.desktop.util.GUIUtil; import bisq.desktop.util.Layout; import bisq.core.btc.exceptions.TransactionVerificationException; import bisq.core.btc.exceptions.WalletException; import bisq.core.btc.wallet.BsqBalanceListener; import bisq.core.btc.wallet.BsqWalletService; -import bisq.core.btc.wallet.InsufficientBsqException; import bisq.core.dao.blockchain.ReadableBsqBlockChain; +import bisq.core.dao.param.DaoParamService; import bisq.core.dao.vote.PeriodService; +import bisq.core.dao.vote.blindvote.BlindVoteConsensus; import bisq.core.dao.vote.blindvote.BlindVoteService; import bisq.core.dao.vote.proposal.Proposal; import bisq.core.dao.vote.proposal.ProposalService; import bisq.core.dao.vote.result.BooleanVoteResult; import bisq.core.locale.Res; -import bisq.common.UserThread; -import bisq.common.crypto.CryptoException; import bisq.common.util.Tuple2; import bisq.common.util.Tuple3; -import com.google.protobuf.InvalidProtocolBufferException; - import org.bitcoinj.core.Coin; import org.bitcoinj.core.InsufficientMoneyException; +import org.bitcoinj.core.Transaction; import javax.inject.Inject; @@ -62,17 +63,11 @@ import javafx.beans.property.ReadOnlyObjectWrapper; import javafx.util.Callback; -import java.io.IOException; - import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.concurrent.TimeUnit; -import static bisq.desktop.util.FormBuilder.add3ButtonsAfterGroup; -import static bisq.desktop.util.FormBuilder.addButtonAfterGroup; -import static bisq.desktop.util.FormBuilder.addLabelInputTextField; -import static bisq.desktop.util.FormBuilder.addTitledGroupBg; +import static bisq.desktop.util.FormBuilder.*; @FxmlView public class ActiveProposalsView extends BaseProposalView implements BsqBalanceListener { @@ -82,6 +77,8 @@ public class ActiveProposalsView extends BaseProposalView implements BsqBalanceL private Button removeButton, acceptButton, rejectButton, cancelVoteButton, voteButton; private InputTextField stakeInputTextField; private List voteViewItems = new ArrayList<>(); + private BusyAnimation voteButtonBusyAnimation; + private Label voteButtonInfoLabel; /////////////////////////////////////////////////////////////////////////////////////////// @@ -94,9 +91,11 @@ public class ActiveProposalsView extends BaseProposalView implements BsqBalanceL BlindVoteService blindVoteService, BsqWalletService bsqWalletService, ReadableBsqBlockChain readableBsqBlockChain, - BsqFormatter bsqFormatter) { - super(voteRequestManger, bsqWalletService, readableBsqBlockChain, periodService, - bsqFormatter); + DaoParamService daoParamService, + BsqFormatter bsqFormatter, + BSFormatter btcFormatter) { + super(voteRequestManger, bsqWalletService, readableBsqBlockChain, daoParamService, periodService, bsqFormatter, + btcFormatter); this.blindVoteService = blindVoteService; } @@ -123,43 +122,43 @@ public class ActiveProposalsView extends BaseProposalView implements BsqBalanceL if (voteButton != null) { voteButton.setOnAction(e -> { - Coin stake = bsqFormatter.parseToCoin(stakeInputTextField.getText()); // TODO verify stake - Popup startPublishingPopup = new Popup<>(); - startPublishingPopup.information(Res.get("dao.proposal.blindVote.startPublishing")) - .hideCloseButton() - .show(); + Coin stake = bsqFormatter.parseToCoin(stakeInputTextField.getText()); + final Coin fee = BlindVoteConsensus.getFee(daoParamService, readableBsqBlockChain.getChainHeadHeight()); + Transaction dummyTx = null; try { - blindVoteService.publishBlindVote(stake, - () -> { - startPublishingPopup.hide(); - UserThread.runAfter(() -> { - new Popup<>().feedback(Res.get("dao.proposal.blindVote.success")) - .show(); - }, 100, TimeUnit.MILLISECONDS); - }, errorMessage -> { - new Popup<>().error(errorMessage).show(); - }); - } catch (CryptoException e1) { - //TODO show error popup - e1.printStackTrace(); - } catch (InsufficientBsqException e1) { - e1.printStackTrace(); - } catch (WalletException e1) { - e1.printStackTrace(); - } catch (TransactionVerificationException e1) { - e1.printStackTrace(); - } catch (InsufficientMoneyException e1) { - e1.printStackTrace(); - } catch (InvalidProtocolBufferException e1) { - e1.printStackTrace(); - } catch (IOException e1) { - e1.printStackTrace(); + // We create a tx with dummy opreturn data to get the mining fee for confirmation popup + dummyTx = blindVoteService.getBlindVoteTx(stake, fee, new byte[22]); + } catch (InsufficientMoneyException | WalletException | TransactionVerificationException exception) { + new Popup<>().warning(exception.toString()).show(); + } + + if (dummyTx != null) { + Coin miningFee = dummyTx.getFee(); + int txSize = dummyTx.bitcoinSerialize().length; + GUIUtil.showBsqFeeInfoPopup(fee, miningFee, txSize, bsqFormatter, btcFormatter, + Res.get("dao.blindVote"), () -> publishBlindVote(stake)); } }); } } + private void publishBlindVote(Coin stake) { + voteButtonBusyAnimation.play(); + voteButtonInfoLabel.setText(Res.get("dao.blindVote.startPublishing")); + blindVoteService.publishBlindVote(stake, + () -> { + voteButtonBusyAnimation.stop(); + voteButtonInfoLabel.setText(""); + new Popup().feedback(Res.get("dao.blindVote.success")) + .show(); + }, exception -> { + voteButtonBusyAnimation.stop(); + voteButtonInfoLabel.setText(""); + new Popup<>().warning(exception.toString()).show(); + }); + } + @Override protected void deactivate() { super.deactivate(); @@ -180,7 +179,10 @@ public class ActiveProposalsView extends BaseProposalView implements BsqBalanceL Res.getWithCol("dao.proposal.myVote.stake"), Layout .FIRST_ROW_AND_GROUP_DISTANCE - 20); stakeInputTextField = tuple2.second; - voteButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.proposal.myVote.button")); + Tuple3 tuple = addButtonBusyAnimationLabelAfterGroup(root, ++gridRow, Res.get("dao.proposal.myVote.button")); + voteButton = tuple.first; + voteButtonBusyAnimation = tuple.second; + voteButtonInfoLabel = tuple.third; voteViewItems.add(titledGroupBg); voteViewItems.add(tuple2.first); diff --git a/src/main/java/bisq/desktop/main/dao/proposal/closed/ClosedProposalsView.java b/src/main/java/bisq/desktop/main/dao/proposal/closed/ClosedProposalsView.java index 66ed8481b4..8acd1359f6 100644 --- a/src/main/java/bisq/desktop/main/dao/proposal/closed/ClosedProposalsView.java +++ b/src/main/java/bisq/desktop/main/dao/proposal/closed/ClosedProposalsView.java @@ -19,10 +19,12 @@ package bisq.desktop.main.dao.proposal.closed; import bisq.desktop.common.view.FxmlView; import bisq.desktop.main.dao.proposal.BaseProposalView; +import bisq.desktop.util.BSFormatter; import bisq.desktop.util.BsqFormatter; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.dao.blockchain.ReadableBsqBlockChain; +import bisq.core.dao.param.DaoParamService; import bisq.core.dao.vote.PeriodService; import bisq.core.dao.vote.proposal.ProposalService; @@ -40,8 +42,11 @@ public class ClosedProposalsView extends BaseProposalView { PeriodService periodService, BsqWalletService bsqWalletService, ReadableBsqBlockChain readableBsqBlockChain, - BsqFormatter bsqFormatter) { - super(proposalService, bsqWalletService, readableBsqBlockChain, periodService, bsqFormatter); + DaoParamService daoParamService, + BsqFormatter bsqFormatter, + BSFormatter bsFormatter) { + super(proposalService, bsqWalletService, readableBsqBlockChain, daoParamService, periodService, + bsqFormatter, bsFormatter); } @Override diff --git a/src/main/java/bisq/desktop/main/dao/proposal/make/MakeProposalView.java b/src/main/java/bisq/desktop/main/dao/proposal/make/MakeProposalView.java index 6185f7dcdb..57ae79d3b6 100644 --- a/src/main/java/bisq/desktop/main/dao/proposal/make/MakeProposalView.java +++ b/src/main/java/bisq/desktop/main/dao/proposal/make/MakeProposalView.java @@ -44,7 +44,6 @@ import bisq.core.dao.vote.proposal.generic.GenericProposalPayload; import bisq.core.dao.vote.proposal.generic.GenericProposalService; import bisq.core.locale.Res; import bisq.core.provider.fee.FeeService; -import bisq.core.util.CoinUtil; import bisq.network.p2p.P2PService; @@ -175,25 +174,11 @@ public class MakeProposalView extends ActivatableView { Coin miningFee = Objects.requireNonNull(tx).getFee(); int txSize = tx.bitcoinSerialize().length; - final Coin fee = ProposalConsensus.getFee(daoParamService, readableBsqBlockChain); - new Popup<>().headLine(Res.get("dao.proposal.create.confirm")) - .confirmation(Res.get("dao.proposal.create.confirm.info", - bsqFormatter.formatCoinWithCode(fee), - btcFormatter.formatCoinWithCode(miningFee), - CoinUtil.getFeePerByte(miningFee, txSize), - txSize / 1000d)) - .actionButtonText(Res.get("shared.yes")) - .onAction(() -> { - proposalService.publishProposal(proposal, - () -> { - proposalDisplay.clearForm(); - proposalTypeComboBox.getSelectionModel().clearSelection(); - new Popup<>().confirmation(Res.get("dao.tx.published.success")).show(); - }, - errorMessage -> new Popup<>().warning(errorMessage).show()); - }) - .closeButtonText(Res.get("shared.cancel")) - .show(); + final Coin fee = ProposalConsensus.getFee(daoParamService, readableBsqBlockChain.getChainHeadHeight()); + + GUIUtil.showBsqFeeInfoPopup(fee, miningFee, txSize, bsqFormatter, btcFormatter, + Res.get("dao.proposal"), () -> publishProposal(proposal)); + } catch (InsufficientMoneyException e) { BSFormatter formatter = e instanceof InsufficientBsqException ? bsqFormatter : btcFormatter; new Popup<>().warning(Res.get("dao.proposal.create.missingFunds", @@ -214,6 +199,16 @@ public class MakeProposalView extends ActivatableView { } } + private void publishProposal(Proposal proposal) { + proposalService.publishProposal(proposal, + () -> { + proposalDisplay.clearForm(); + proposalTypeComboBox.getSelectionModel().clearSelection(); + new Popup<>().confirmation(Res.get("dao.tx.published.success")).show(); + }, + errorMessage -> new Popup<>().warning(errorMessage).show()); + } + private Proposal createProposal(ProposalType type) throws InsufficientMoneyException, TransactionVerificationException, ValidationException, WalletException, IOException { diff --git a/src/main/java/bisq/desktop/main/dao/proposal/myvotes/MyVotesView.java b/src/main/java/bisq/desktop/main/dao/proposal/myvotes/MyVotesView.java index d319b68dd5..d744bbdb81 100644 --- a/src/main/java/bisq/desktop/main/dao/proposal/myvotes/MyVotesView.java +++ b/src/main/java/bisq/desktop/main/dao/proposal/myvotes/MyVotesView.java @@ -24,12 +24,14 @@ import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.components.TableGroupHeadline; import bisq.desktop.main.dao.proposal.BaseProposalView; import bisq.desktop.main.dao.proposal.ProposalListItem; +import bisq.desktop.util.BSFormatter; import bisq.desktop.util.BsqFormatter; import bisq.desktop.util.GUIUtil; import bisq.desktop.util.Layout; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.dao.blockchain.ReadableBsqBlockChain; +import bisq.core.dao.param.DaoParamService; import bisq.core.dao.vote.PeriodService; import bisq.core.dao.vote.myvote.MyVoteService; import bisq.core.dao.vote.proposal.ProposalList; @@ -92,10 +94,12 @@ public class MyVotesView extends BaseProposalView { MyVoteService myVoteService, BsqWalletService bsqWalletService, ReadableBsqBlockChain readableBsqBlockChain, + DaoParamService daoParamService, Preferences preferences, - BsqFormatter bsqFormatter) { - super(voteRequestManger, bsqWalletService, readableBsqBlockChain, periodService, - bsqFormatter); + BsqFormatter bsqFormatter, + BSFormatter btcFormatter) { + super(voteRequestManger, bsqWalletService, readableBsqBlockChain, daoParamService, periodService, bsqFormatter, + btcFormatter); this.myVoteService = myVoteService; this.readableBsqBlockChain = readableBsqBlockChain; this.preferences = preferences; diff --git a/src/main/java/bisq/desktop/util/GUIUtil.java b/src/main/java/bisq/desktop/util/GUIUtil.java index 0c8271c01f..1824148c1a 100644 --- a/src/main/java/bisq/desktop/util/GUIUtil.java +++ b/src/main/java/bisq/desktop/util/GUIUtil.java @@ -33,6 +33,7 @@ import bisq.core.provider.fee.FeeService; import bisq.core.user.DontShowAgainLookup; import bisq.core.user.Preferences; import bisq.core.user.User; +import bisq.core.util.CoinUtil; import bisq.network.p2p.P2PService; @@ -543,4 +544,21 @@ public class GUIUtil { childByRowMap.get(i).forEach(child -> gridPane.getChildren().remove(child)); } } + + public static void showBsqFeeInfoPopup(Coin fee, Coin miningFee, int txSize, BsqFormatter bsqFormatter, + BSFormatter btcFormatter, String type, + Runnable actionHandler) { + new Popup<>().headLine(Res.get("dao.feeTx.confirm", type)) + .confirmation(Res.get("dao.feeTx.confirm.details", + type, + bsqFormatter.formatCoinWithCode(fee), + btcFormatter.formatCoinWithCode(miningFee), + CoinUtil.getFeePerByte(miningFee, txSize), + txSize / 1000d, + type)) + .actionButtonText(Res.get("shared.yes")) + .onAction(actionHandler::run) + .closeButtonText(Res.get("shared.cancel")) + .show(); + } }