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
This commit is contained in:
Manfred Karrer 2018-04-06 17:01:08 -05:00
parent 4d217e682a
commit 316a305f53
No known key found for this signature in database
GPG key ID: 401250966A6B2C46
7 changed files with 104 additions and 73 deletions

View file

@ -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<GridPane, Void> 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<ProposalListItem> proposalListItems = FXCollections.observableArrayList();
protected final SortedList<ProposalListItem> sortedList = new SortedList<>(proposalListItems);
@ -101,13 +105,17 @@ public abstract class BaseProposalView extends ActivatableView<GridPane, Void> 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

View file

@ -162,7 +162,6 @@ public class ProposalListItem implements BsqBlockChain.Listener {
//TODO
}
} else {
log.error("actionButtonIconView.setVisible(false);");
actionButtonIconView.setVisible(false);
}
}

View file

@ -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<Node> 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<Button, BusyAnimation, Label> 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);

View file

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

View file

@ -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<GridPane, Void> {
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<GridPane, Void> {
}
}
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 {

View file

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

View file

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