Skip to content

Commit

Permalink
Merge pull request #1470 from ManfredKarrer/voting
Browse files Browse the repository at this point in the history
Add validation for description text. Fix wrong proposal type.
  • Loading branch information
ManfredKarrer authored Mar 19, 2018
2 parents c27e1d4 + 118cbb5 commit 1bfbd25
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ protected void onSelectProposal(ProposalListItem item) {
if (item != null) {
final Proposal proposal = item.getProposal();
proposalDisplay.removeAllFields();
proposalDisplay.createAllFields(Res.get("dao.proposal.selectedProposal"), 0, 0, item.getProposal().getType(), false);
proposalDisplay.createAllFields(Res.get("dao.proposal.selectedProposal"), 0, 0, proposal.getType(), false);
proposalDisplay.setAllFieldsEditable(false);
proposalDisplay.fillWithData(proposal.getProposalPayload());
}
Expand Down
23 changes: 21 additions & 2 deletions src/main/java/bisq/desktop/main/dao/proposal/ProposalDisplay.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.InputTextField;
import bisq.desktop.components.TxIdTextField;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.BsqFormatter;
import bisq.desktop.util.GUIUtil;
import bisq.desktop.util.Layout;
Expand All @@ -28,10 +29,10 @@

import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.dao.proposal.ProposalPayload;
import bisq.core.dao.proposal.ProposalRestrictions;
import bisq.core.dao.proposal.ProposalType;
import bisq.core.dao.proposal.compensation.CompensationRequestPayload;
import bisq.core.dao.proposal.compensation.consensus.Restrictions;
import bisq.core.dao.proposal.consensus.ProposalRestrictions;
import bisq.core.locale.Res;
import bisq.core.provider.fee.FeeService;

Expand All @@ -44,6 +45,8 @@

import javafx.geometry.HPos;

import javafx.beans.value.ChangeListener;

import java.util.Objects;
import java.util.UUID;

Expand All @@ -56,6 +59,7 @@
// use ProposalRestrictions.getMaxLengthDescriptionText()
public class ProposalDisplay {
private final GridPane gridPane;
private final int maxLengthDescriptionText;
private BsqFormatter bsqFormatter;
private BsqWalletService bsqWalletService;
public InputTextField uidTextField, nameTextField, titleTextField, linkInputTextField;
Expand All @@ -67,12 +71,22 @@ public class ProposalDisplay {
@Nullable
private TxIdTextField txIdTextField;
private FeeService feeService;
private ChangeListener<String> descriptionTextAreaListener;

public ProposalDisplay(GridPane gridPane, BsqFormatter bsqFormatter, BsqWalletService bsqWalletService, @Nullable FeeService feeService) {
this.gridPane = gridPane;
this.bsqFormatter = bsqFormatter;
this.bsqWalletService = bsqWalletService;
this.feeService = feeService;

maxLengthDescriptionText = ProposalRestrictions.getMaxLengthDescriptionText();

descriptionTextAreaListener = (observable, oldValue, newValue) -> {
if (!ProposalRestrictions.isDescriptionSizeValid(newValue)) {
new Popup<>().warning(Res.get("dao.proposal.display.description.tooLong", maxLengthDescriptionText)).show();
descriptionTextArea.setText(newValue.substring(0, maxLengthDescriptionText));
}
};
}

public void createAllFields(String title, int index, double top, ProposalType proposalType, boolean isMakeProposalScreen) {
Expand All @@ -88,7 +102,10 @@ public void createAllFields(String title, int index, double top, ProposalType pr
uidTextField.setEditable(false);
nameTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.name")).second;
titleTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.title")).second;
descriptionTextArea = addLabelTextArea(gridPane, ++gridRow, Res.get("dao.proposal.display.description"), Res.get("dao.proposal.display.description.prompt", ProposalRestrictions.getMaxLengthDescriptionText())).second;

descriptionTextArea = addLabelTextArea(gridPane, ++gridRow, Res.get("dao.proposal.display.description"), Res.get("dao.proposal.display.description.prompt", maxLengthDescriptionText)).second;
descriptionTextArea.setPrefColumnCount(2);
descriptionTextArea.textProperty().addListener(descriptionTextAreaListener);
linkInputTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.link")).second;
linkHyperlinkWithIcon = addLabelHyperlinkWithIcon(gridPane, gridRow, Res.get("dao.proposal.display.link"), "", "").second;
linkHyperlinkWithIcon.setVisible(false);
Expand Down Expand Up @@ -150,6 +167,8 @@ public void clearForm() {
bsqAddressTextField.clear();
if (txIdTextField != null)
txIdTextField.cleanup();

descriptionTextArea.textProperty().removeListener(descriptionTextAreaListener);
}

public void fillWithMock() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@
import bisq.core.btc.wallet.BsqWalletService;
import bisq.core.btc.wallet.InsufficientBsqException;
import bisq.core.btc.wallet.WalletsSetup;
import bisq.core.dao.proposal.Proposal;
import bisq.core.dao.proposal.ProposalCollectionsManager;
import bisq.core.dao.proposal.ProposalType;
import bisq.core.dao.proposal.compensation.CompensationAmountException;
import bisq.core.dao.proposal.compensation.CompensationRequest;
import bisq.core.dao.proposal.compensation.CompensationRequestManager;
import bisq.core.dao.proposal.compensation.CompensationRequestPayload;
import bisq.core.dao.proposal.generic.GenericProposal;
import bisq.core.dao.proposal.consensus.ProposalRestrictions;
import bisq.core.dao.proposal.generic.GenericProposalManager;
import bisq.core.dao.proposal.generic.GenericProposalPayload;
import bisq.core.locale.Res;
Expand Down Expand Up @@ -76,6 +76,7 @@
import static bisq.desktop.util.FormBuilder.addButtonAfterGroup;
import static bisq.desktop.util.FormBuilder.addLabelComboBox;
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
import static com.google.common.base.Preconditions.checkArgument;

@FxmlView
public class MakeProposalView extends ActivatableView<GridPane, Void> {
Expand Down Expand Up @@ -150,6 +151,9 @@ public ProposalType fromString(String string) {
@Override
protected void activate() {
proposalTypeComboBox.getSelectionModel().selectedItemProperty().addListener(proposalTypeChangeListener);

if (createButton != null)
setCreateButtonHandler();
}

@Override
Expand All @@ -159,31 +163,29 @@ protected void deactivate() {
createButton.setOnAction(null);
}

private void createCompensationRequest() {
CompensationRequestPayload compensationRequestPayload = compensationRequestManager.getNewCompensationRequestPayload(
proposalDisplay.nameTextField.getText(),
proposalDisplay.titleTextField.getText(),
proposalDisplay.descriptionTextArea.getText(),
proposalDisplay.linkInputTextField.getText(),
bsqFormatter.parseToCoin(Objects.requireNonNull(proposalDisplay.requestedBsqTextField).getText()),
Objects.requireNonNull(proposalDisplay.bsqAddressTextField).getText());
private void createCompensationRequest(ProposalType type) {
try {
CompensationRequest compensationRequest = compensationRequestManager.prepareCompensationRequest(compensationRequestPayload);
Coin miningFee = compensationRequest.getTx().getFee();
int txSize = compensationRequest.getTx().bitcoinSerialize().length;
Proposal proposal = createProposal(type);
Coin miningFee = Objects.requireNonNull(proposal).getTx().getFee();
int txSize = proposal.getTx().bitcoinSerialize().length;

validateInputs();

new Popup<>().headLine(Res.get("dao.proposal.create.confirm"))
.confirmation(Res.get("dao.proposal.create.confirm.info",
bsqFormatter.formatCoinWithCode(compensationRequest.getRequestedBsq()),
bsqFormatter.formatCoinWithCode(compensationRequest.getFeeAsCoin()),
bsqFormatter.formatCoinWithCode(proposal.getFeeAsCoin()),
btcFormatter.formatCoinWithCode(miningFee),
CoinUtil.getFeePerByte(miningFee, txSize),
(txSize / 1000d)))
.actionButtonText(Res.get("shared.yes"))
.onAction(() -> {
proposalCollectionsManager.publishProposal(compensationRequest, new FutureCallback<Transaction>() {
proposalCollectionsManager.publishProposal(proposal, new FutureCallback<Transaction>() {
@Override
public void onSuccess(@Nullable Transaction transaction) {
proposalDisplay.clearForm();

proposalTypeComboBox.getSelectionModel().clearSelection();

new Popup<>().confirmation(Res.get("dao.tx.published.success")).show();
}

Expand All @@ -208,91 +210,73 @@ public void onFailure(@NotNull Throwable t) {
}
}

private void createGenericProposal() {
GenericProposalPayload genericProposalPayload = genericProposalManager.getNewGenericProposalPayload(
proposalDisplay.nameTextField.getText(),
proposalDisplay.titleTextField.getText(),
proposalDisplay.descriptionTextArea.getText(),
proposalDisplay.linkInputTextField.getText());
try {
GenericProposal genericProposal = genericProposalManager.prepareGenericProposal(genericProposalPayload);
Coin miningFee = genericProposal.getTx().getFee();
int txSize = genericProposal.getTx().bitcoinSerialize().length;
new Popup<>().headLine(Res.get("dao.proposal.create.confirm"))
.confirmation(Res.get("dao.proposal.create.confirm.info",
bsqFormatter.formatCoinWithCode(Coin.valueOf(10000)), // TODO dummy
bsqFormatter.formatCoinWithCode(genericProposal.getFeeAsCoin()),
btcFormatter.formatCoinWithCode(miningFee),
CoinUtil.getFeePerByte(miningFee, txSize),
(txSize / 1000d)))
.actionButtonText(Res.get("shared.yes"))
.onAction(() -> {
proposalCollectionsManager.publishProposal(genericProposal, new FutureCallback<Transaction>() {
@Override
public void onSuccess(@Nullable Transaction transaction) {
proposalDisplay.clearForm();
new Popup<>().confirmation(Res.get("dao.tx.published.success")).show();
}

@Override
public void onFailure(@NotNull Throwable t) {
log.error(t.toString());
new Popup<>().warning(t.toString()).show();
}
});
})
.closeButtonText(Res.get("shared.cancel"))
.show();
} catch (InsufficientMoneyException e) {
BSFormatter formatter = e instanceof InsufficientBsqException ? bsqFormatter : btcFormatter;
new Popup<>().warning(Res.get("dao.proposal.create.missingFunds", formatter.formatCoinWithCode(e.missing))).show();
} catch (CompensationAmountException e) {
new Popup<>().warning(Res.get("validation.bsq.amountBelowMinAmount", bsqFormatter.formatCoinWithCode(e.required))).show();
} catch (TransactionVerificationException | WalletException e) {
log.error(e.toString());
e.printStackTrace();
new Popup<>().warning(e.toString()).show();
private Proposal createProposal(ProposalType type) throws InsufficientMoneyException, TransactionVerificationException, CompensationAmountException, WalletException {
switch (type) {
case COMPENSATION_REQUEST:
CompensationRequestPayload compensationRequestPayload = compensationRequestManager.getNewCompensationRequestPayload(
proposalDisplay.nameTextField.getText(),
proposalDisplay.titleTextField.getText(),
proposalDisplay.descriptionTextArea.getText(),
proposalDisplay.linkInputTextField.getText(),
bsqFormatter.parseToCoin(Objects.requireNonNull(proposalDisplay.requestedBsqTextField).getText()),
Objects.requireNonNull(proposalDisplay.bsqAddressTextField).getText());
return compensationRequestManager.prepareCompensationRequest(compensationRequestPayload);
case GENERIC:
GenericProposalPayload genericProposalPayload = genericProposalManager.getNewGenericProposalPayload(
proposalDisplay.nameTextField.getText(),
proposalDisplay.titleTextField.getText(),
proposalDisplay.descriptionTextArea.getText(),
proposalDisplay.linkInputTextField.getText());
return genericProposalManager.prepareGenericProposal(genericProposalPayload);
case CHANGE_PARAM:
//TODO
return null;
case REMOVE_ALTCOIN:
//TODO
return null;
default:
final String msg = "Undefined ProposalType " + selectedProposalType;
log.error(msg);
if (DevEnv.isDevMode())
throw new RuntimeException(msg);
return null;
}
}

private void addProposalDisplay() {
// TODO need to update removed fields when switching.
if (proposalDisplay != null) {
root.getChildren().remove(3, root.getChildren().size());
proposalDisplay = null;
}
if (selectedProposalType != null) {
proposalDisplay = new ProposalDisplay(root, bsqFormatter, bsqWalletService, feeService);
proposalDisplay.createAllFields(Res.get("dao.proposal.create.createNew"), 1, Layout.GROUP_DISTANCE, selectedProposalType, true);
proposalDisplay.fillWithMock();

createButton = addButtonAfterGroup(root, proposalDisplay.incrementAndGetGridRow(), Res.get("dao.proposal.create.create.button"));
setCreateButtonHandler();
}
proposalDisplay = new ProposalDisplay(root, bsqFormatter, bsqWalletService, feeService);
proposalDisplay.createAllFields(Res.get("dao.proposal.create.createNew"), 1, Layout.GROUP_DISTANCE, selectedProposalType, true);
proposalDisplay.fillWithMock();
}

createButton = addButtonAfterGroup(root, proposalDisplay.incrementAndGetGridRow(), Res.get("dao.proposal.create.create.button"));
private void setCreateButtonHandler() {
createButton.setOnAction(event -> {
// TODO break up in methods
if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) {
switch (selectedProposalType) {
case COMPENSATION_REQUEST:
createCompensationRequest();
break;
case GENERIC:
createGenericProposal();
break;
case CHANGE_PARAM:
//TODO
break;
case REMOVE_ALTCOIN:
//TODO
break;
default:
final String msg = "Undefined ProposalType " + selectedProposalType;
log.error(msg);
if (DevEnv.isDevMode())
throw new RuntimeException(msg);
break;

}
createCompensationRequest(selectedProposalType);
} else {
GUIUtil.showNotReadyForTxBroadcastPopups(p2PService, walletsSetup);
}
});
}

private void validateInputs() {
// We check in proposalDisplay that no invalid input as allowed
checkArgument(ProposalRestrictions.isDescriptionSizeValid(proposalDisplay.descriptionTextArea.getText()), "descriptionText must not be longer than " +
ProposalRestrictions.getMaxLengthDescriptionText() + " chars");

// TODO add more checks for all input fields
}

}

0 comments on commit 1bfbd25

Please sign in to comment.