From ccd3e9c5bafbc34916eafe3c8fab40b61601db64 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 23 Jul 2018 02:22:18 +0200 Subject: [PATCH] Add BondedRoles proposal and views --- .../ArbitratorRegistrationViewModel.java | 8 +- .../desktop/main/dao/BaseProposalView.java | 2 +- .../desktop/main/dao/bonding/BondingView.java | 13 +- .../main/dao/bonding/lockup/LockupView.java | 70 ++- .../bonding/roles/BondedRoleTypeWindow.java | 93 ++++ .../bonding/roles/BondedRolesListItem.java | 72 +++ .../dao/bonding/roles/BondedRolesView.fxml | 34 ++ .../dao/bonding/roles/BondedRolesView.java | 490 ++++++++++++++++++ .../main/dao/bonding/unlock/UnlockView.java | 4 +- .../main/dao/proposal/ProposalDisplay.java | 238 ++++++--- .../main/dao/proposal/ProposalWindow.java | 2 +- .../proposal/active/ActiveProposalsView.java | 2 +- .../dao/proposal/make/MakeProposalView.java | 73 ++- .../desktop/main/dao/results/ResultsView.java | 11 +- .../proposals/ProposalResultsListItem.java | 20 +- .../VoteResultsForProposalWindow.java | 4 +- 16 files changed, 973 insertions(+), 163 deletions(-) create mode 100644 src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRoleTypeWindow.java create mode 100644 src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesListItem.java create mode 100644 src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesView.fxml create mode 100644 src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesView.java diff --git a/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java b/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java index 1ffa41fddf..035cfde20e 100644 --- a/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java +++ b/src/main/java/bisq/desktop/main/account/arbitratorregistration/ArbitratorRegistrationViewModel.java @@ -136,12 +136,12 @@ class ArbitratorRegistrationViewModel extends ActivatableViewModel { } boolean setPrivKeyAndCheckPubKey(String privKeyString) { - ECKey _registrationKey = arbitratorManager.getRegistrationKey(privKeyString); - if (_registrationKey != null) { - String _registrationPubKeyAsHex = Utils.HEX.encode(_registrationKey.getPubKey()); + ECKey registrationKey = arbitratorManager.getRegistrationKey(privKeyString); + if (registrationKey != null) { + String _registrationPubKeyAsHex = Utils.HEX.encode(registrationKey.getPubKey()); boolean isKeyValid = arbitratorManager.isPublicKeyInList(_registrationPubKeyAsHex); if (isKeyValid) { - registrationKey = _registrationKey; + this.registrationKey = registrationKey; registrationPubKeyAsHex.set(_registrationPubKeyAsHex); } updateDisableStates(); diff --git a/src/main/java/bisq/desktop/main/dao/BaseProposalView.java b/src/main/java/bisq/desktop/main/dao/BaseProposalView.java index 7dc7b0bdf3..ec228a74ac 100644 --- a/src/main/java/bisq/desktop/main/dao/BaseProposalView.java +++ b/src/main/java/bisq/desktop/main/dao/BaseProposalView.java @@ -181,7 +181,7 @@ public abstract class BaseProposalView extends ActivatableView { proposalDisplayView.setManaged(true); proposalDisplay.createAllFields(Res.get("dao.proposal.selectedProposal"), 0, 0, proposal.getType(), - false, false); + false); proposalDisplay.setEditable(false); proposalDisplay.applyProposalPayload(proposal); proposalDisplayInitialized = true; diff --git a/src/main/java/bisq/desktop/main/dao/bonding/BondingView.java b/src/main/java/bisq/desktop/main/dao/bonding/BondingView.java index 0e4670a91d..973464a692 100644 --- a/src/main/java/bisq/desktop/main/dao/bonding/BondingView.java +++ b/src/main/java/bisq/desktop/main/dao/bonding/BondingView.java @@ -29,6 +29,7 @@ import bisq.desktop.main.MainView; import bisq.desktop.main.dao.DaoView; import bisq.desktop.main.dao.bonding.dashboard.BondingDashboardView; import bisq.desktop.main.dao.bonding.lockup.LockupView; +import bisq.desktop.main.dao.bonding.roles.BondedRolesView; import bisq.desktop.main.dao.bonding.unlock.UnlockView; import bisq.core.locale.Res; @@ -52,7 +53,7 @@ public class BondingView extends ActivatableViewAndModel { private final ViewLoader viewLoader; private final Navigation navigation; - private MenuItem dashboard, lockupBSQ, unlockBSQ; + private MenuItem dashboard, bondedRoles, lockupBSQ, unlockBSQ; private Navigation.Listener listener; @FXML @@ -82,16 +83,20 @@ public class BondingView extends ActivatableViewAndModel { final List> baseNavPath = Arrays.asList(MainView.class, DaoView.class, bisq.desktop.main.dao.bonding.BondingView.class); dashboard = new MenuItem(navigation, toggleGroup, Res.get("shared.dashboard"), BondingDashboardView.class, AwesomeIcon.DASHBOARD, baseNavPath); + bondedRoles = new MenuItem(navigation, toggleGroup, Res.get("dao.bonding.menuItem.bondedRoles"), + BondedRolesView.class, AwesomeIcon.SHIELD, baseNavPath); lockupBSQ = new MenuItem(navigation, toggleGroup, Res.get("dao.bonding.menuItem.lockupBSQ"), LockupView.class, AwesomeIcon.LOCK, baseNavPath); unlockBSQ = new MenuItem(navigation, toggleGroup, Res.get("dao.bonding.menuItem.unlockBSQ"), UnlockView.class, AwesomeIcon.UNLOCK, baseNavPath); - leftVBox.getChildren().addAll(dashboard, lockupBSQ, unlockBSQ); + + leftVBox.getChildren().addAll(dashboard, bondedRoles, lockupBSQ, unlockBSQ); } @Override protected void activate() { dashboard.activate(); + bondedRoles.activate(); lockupBSQ.activate(); unlockBSQ.activate(); @@ -100,7 +105,7 @@ public class BondingView extends ActivatableViewAndModel { if (viewPath.size() == 3 && viewPath.indexOf(BondingView.class) == 2 || viewPath.size() == 2 && viewPath.indexOf(DaoView.class) == 1) { if (selectedViewClass == null) - selectedViewClass = LockupView.class; + selectedViewClass = BondedRolesView.class; loadView(selectedViewClass); @@ -115,6 +120,7 @@ public class BondingView extends ActivatableViewAndModel { navigation.removeListener(listener); dashboard.deactivate(); + bondedRoles.deactivate(); lockupBSQ.deactivate(); unlockBSQ.deactivate(); } @@ -124,6 +130,7 @@ public class BondingView extends ActivatableViewAndModel { content.getChildren().setAll(view.getRoot()); if (view instanceof BondingDashboardView) dashboard.setSelected(true); + else if (view instanceof BondedRolesView) bondedRoles.setSelected(true); else if (view instanceof LockupView) lockupBSQ.setSelected(true); else if (view instanceof UnlockView) unlockBSQ.setSelected(true); } diff --git a/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java b/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java index b6b9fcdff4..36aeb8d427 100644 --- a/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java +++ b/src/main/java/bisq/desktop/main/dao/bonding/lockup/LockupView.java @@ -35,10 +35,9 @@ import bisq.core.btc.wallet.BsqBalanceListener; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.WalletsSetup; import bisq.core.dao.DaoFacade; -import bisq.core.dao.bonding.Bond; import bisq.core.dao.bonding.BondingConsensus; -import bisq.core.dao.bonding.Bonds; import bisq.core.dao.bonding.lockup.LockupType; +import bisq.core.dao.role.BondedRole; import bisq.core.locale.Res; import bisq.core.util.BsqFormatter; import bisq.core.util.validation.IntegerValidator; @@ -61,7 +60,6 @@ import javafx.collections.FXCollections; import javafx.util.StringConverter; import java.util.Arrays; -import java.util.Optional; import static bisq.desktop.util.FormBuilder.addButtonAfterGroup; import static bisq.desktop.util.FormBuilder.addLabelComboBox; @@ -84,10 +82,12 @@ public class LockupView extends ActivatableView implements BsqBa private InputTextField amountInputTextField; private InputTextField timeInputTextField; private ComboBox lockupTypeComboBox; - private ComboBox bondsComboBox; + private ComboBox bondedRolesComboBox; private Button lockupButton; private ChangeListener focusOutListener; private ChangeListener inputTextFieldListener; + private ChangeListener bondedRolesListener; + private ChangeListener lockupTypeListener; /////////////////////////////////////////////////////////////////////////////////////////// @@ -134,11 +134,11 @@ public class LockupView extends ActivatableView implements BsqBa timeInputTextField.setValidator(timeInputTextFieldValidator); lockupTypeComboBox = addLabelComboBox(root, ++gridRow, Res.get("dao.bonding.lock.type")).second; - lockupTypeComboBox.setPromptText(Res.get("list.currency.select")); + lockupTypeComboBox.setPromptText(Res.get("shared.select")); lockupTypeComboBox.setConverter(new StringConverter() { @Override public String toString(LockupType lockupType) { - return lockupType.toString(); + return lockupType.getDisplayString(); } @Override @@ -147,35 +147,52 @@ public class LockupView extends ActivatableView implements BsqBa } }); lockupTypeComboBox.setItems(FXCollections.observableArrayList(Arrays.asList(LockupType.values()))); + lockupTypeListener = (observable, oldValue, newValue) -> { + if (newValue != null) { + bondedRolesComboBox.getSelectionModel().clearSelection(); + } + }; + //TODO handle trade type + lockupTypeComboBox.getSelectionModel().select(0); - bondsComboBox = addLabelComboBox(root, ++gridRow, Res.get("dao.bonding.lock.bonds")).second; - bondsComboBox.setPromptText(Res.get("list.currency.select")); - bondsComboBox.setConverter(new StringConverter() { + bondedRolesComboBox = addLabelComboBox(root, ++gridRow, Res.get("dao.bonding.lock.bondedRoles")).second; + bondedRolesComboBox.setPromptText(Res.get("shared.select")); + bondedRolesComboBox.setConverter(new StringConverter() { @Override - public String toString(Bond bond) { - return bond.toDisplayString(); + public String toString(BondedRole bondedRole) { + return bondedRole.getDisplayString(); } @Override - public Bond fromString(String string) { + public BondedRole fromString(String string) { return null; } }); - bondsComboBox.setItems(FXCollections.observableArrayList(Bonds.getBonds())); + bondedRolesListener = (observable, oldValue, newValue) -> { + if (newValue != null) { + amountInputTextField.setText(bsqFormatter.formatCoin(Coin.valueOf(newValue.getBondedRoleType().getRequiredBond()))); + timeInputTextField.setText(String.valueOf(newValue.getBondedRoleType().getUnlockTime())); + amountInputTextField.resetValidation(); + timeInputTextField.resetValidation(); + amountInputTextField.setEditable(false); + timeInputTextField.setEditable(false); + } else { + amountInputTextField.clear(); + timeInputTextField.clear(); + amountInputTextField.resetValidation(); + timeInputTextField.resetValidation(); + amountInputTextField.setEditable(true); + timeInputTextField.setEditable(true); + } + }; lockupButton = addButtonAfterGroup(root, ++gridRow, Res.get("dao.bonding.lock.lockupButton")); lockupButton.setOnAction((event) -> { if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) { Coin lockupAmount = bsqFormatter.parseToCoin(amountInputTextField.getText()); int lockupTime = Integer.parseInt(timeInputTextField.getText()); - LockupType type = lockupTypeComboBox.getValue(); - //TODO use mapping to human readable input - Optional hashOfBondId; - if (type == LockupType.BONDED_ROLE) { - hashOfBondId = Optional.of(bondsComboBox.getSelectionModel().getSelectedItem().getHash()); - } else { - hashOfBondId = Optional.empty(); - } + LockupType lockupType = lockupTypeComboBox.getValue(); + BondedRole bondedRole = bondedRolesComboBox.getValue(); new Popup<>().headLine(Res.get("dao.bonding.lock.sendFunds.headline")) .confirmation(Res.get("dao.bonding.lock.sendFunds.details", bsqFormatter.formatCoinWithCode(lockupAmount), @@ -185,8 +202,8 @@ public class LockupView extends ActivatableView implements BsqBa .onAction(() -> { daoFacade.publishLockupTx(lockupAmount, lockupTime, - type, - hashOfBondId, + lockupType, + bondedRole, () -> { new Popup<>().feedback(Res.get("dao.tx.published.success")).show(); }, @@ -218,9 +235,12 @@ public class LockupView extends ActivatableView implements BsqBa amountInputTextField.textProperty().addListener(inputTextFieldListener); timeInputTextField.textProperty().addListener(inputTextFieldListener); amountInputTextField.focusedProperty().addListener(focusOutListener); + lockupTypeComboBox.getSelectionModel().selectedItemProperty().addListener(lockupTypeListener); + bondedRolesComboBox.getSelectionModel().selectedItemProperty().addListener(bondedRolesListener); bsqWalletService.addBsqBalanceListener(this); + bondedRolesComboBox.setItems(FXCollections.observableArrayList(daoFacade.getBondedRoleList())); onUpdateBalances(); } @@ -231,6 +251,8 @@ public class LockupView extends ActivatableView implements BsqBa amountInputTextField.textProperty().removeListener(inputTextFieldListener); timeInputTextField.textProperty().removeListener(inputTextFieldListener); amountInputTextField.focusedProperty().removeListener(focusOutListener); + lockupTypeComboBox.getSelectionModel().selectedItemProperty().removeListener(lockupTypeListener); + bondedRolesComboBox.getSelectionModel().selectedItemProperty().removeListener(bondedRolesListener); bsqWalletService.removeBsqBalanceListener(this); } @@ -259,7 +281,7 @@ public class LockupView extends ActivatableView implements BsqBa private void updateButtonState() { lockupButton.setDisable(!bsqValidator.validate(amountInputTextField.getText()).isValid || !timeInputTextFieldValidator.validate(timeInputTextField.getText()).isValid || - bondsComboBox.getSelectionModel().getSelectedItem() == null || + bondedRolesComboBox.getSelectionModel().getSelectedItem() == null || lockupTypeComboBox.getSelectionModel().getSelectedItem() == null); } diff --git a/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRoleTypeWindow.java b/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRoleTypeWindow.java new file mode 100644 index 0000000000..760f9b29c7 --- /dev/null +++ b/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRoleTypeWindow.java @@ -0,0 +1,93 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.main.dao.bonding.roles; + +import bisq.desktop.main.overlays.Overlay; +import bisq.desktop.util.FormBuilder; + +import bisq.core.dao.role.BondedRoleType; +import bisq.core.locale.Res; +import bisq.core.util.BsqFormatter; + +import org.bitcoinj.core.Coin; + +import javafx.geometry.Insets; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class BondedRoleTypeWindow extends Overlay { + private final BondedRoleType bondedRoleType; + private final BsqFormatter bsqFormatter; + + + public BondedRoleTypeWindow(BondedRoleType bondedRoleType, BsqFormatter bsqFormatter) { + this.bondedRoleType = bondedRoleType; + this.bsqFormatter = bsqFormatter; + + width = 900; + type = Type.Confirmation; + + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Public API + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void show() { + headLine = Res.get("dao.bond.bondedRoleType.details.header"); + + createGridPane(); + addHeadLine(); + addSeparator(); + addContent(); + addCloseButton(); + applyStyles(); + display(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Protected + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + protected void createGridPane() { + super.createGridPane(); + gridPane.setPadding(new Insets(35, 40, 30, 40)); + gridPane.getStyleClass().add("grid-pane"); + } + + private void addContent() { + FormBuilder.addLabelTextField(gridPane, ++rowIndex, Res.getWithCol("dao.bond.bondedRoleType.details.role"), + bondedRoleType.getDisplayString()); + + FormBuilder.addLabelTextField(gridPane, ++rowIndex, Res.getWithCol("dao.bond.bondedRoleType.details.requiredBond"), + bsqFormatter.formatCoinWithCode(Coin.valueOf(bondedRoleType.getRequiredBond()))); + + FormBuilder.addLabelTextField(gridPane, ++rowIndex, Res.getWithCol("dao.bond.bondedRoleType.details.unlockTime"), + Res.get("dao.bond.bondedRoleType.details.blocks", bondedRoleType.getUnlockTime())); + + FormBuilder.addLabelHyperlinkWithIcon(gridPane, ++rowIndex, Res.getWithCol("dao.bond.bondedRoleType.details.link"), + bondedRoleType.getLink(), bondedRoleType.getLink()); + + FormBuilder.addLabelTextField(gridPane, ++rowIndex, Res.getWithCol("dao.bond.bondedRoleType.details.isSingleton"), + bsqFormatter.booleanToYesNo(bondedRoleType.isAllowMultipleHolders())); + } +} diff --git a/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesListItem.java b/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesListItem.java new file mode 100644 index 0000000000..5ef042d82f --- /dev/null +++ b/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesListItem.java @@ -0,0 +1,72 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.main.dao.bonding.roles; + +import bisq.desktop.components.AutoTooltipButton; + +import bisq.core.dao.DaoFacade; +import bisq.core.dao.role.BondedRole; +import bisq.core.locale.Res; +import bisq.core.util.BsqFormatter; + +import java.util.Date; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@EqualsAndHashCode +@Data +class BondedRolesListItem { + @Getter + private final BondedRole bondedRole; + private final DaoFacade daoFacade; + private final BsqFormatter bsqFormatter; + private final AutoTooltipButton button; + + BondedRolesListItem(BondedRole bondedRole, + DaoFacade daoFacade, + BsqFormatter bsqFormatter) { + this.bondedRole = bondedRole; + this.daoFacade = daoFacade; + this.bsqFormatter = bsqFormatter; + + + button = new AutoTooltipButton(); + button.setMinWidth(70); + button.setText(Res.get("dao.bond.table.revoke")); + button.setVisible(true); + button.setManaged(true); + } + + public String getStartDate() { + return bondedRole.getStartDate() > 0 ? + bsqFormatter.formatDateTime(new Date(bondedRole.getStartDate())) : + "-"; + } + + public String getRevokeDate() { + return bondedRole.getRevokeDate() > 0 ? + bsqFormatter.formatDateTime(new Date(bondedRole.getRevokeDate())) : + "-"; + } + + public static void cleanup() { + + } +} diff --git a/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesView.fxml b/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesView.fxml new file mode 100644 index 0000000000..987214b490 --- /dev/null +++ b/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesView.fxml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + diff --git a/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesView.java b/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesView.java new file mode 100644 index 0000000000..f970711a91 --- /dev/null +++ b/src/main/java/bisq/desktop/main/dao/bonding/roles/BondedRolesView.java @@ -0,0 +1,490 @@ +/* + * This file is part of Bisq. + * + * Bisq is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * Bisq is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public + * License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Bisq. If not, see . + */ + +package bisq.desktop.main.dao.bonding.roles; + +import bisq.desktop.common.view.ActivatableView; +import bisq.desktop.common.view.FxmlView; +import bisq.desktop.components.AutoTooltipLabel; +import bisq.desktop.components.AutoTooltipTableColumn; +import bisq.desktop.components.HyperlinkWithIcon; +import bisq.desktop.components.TableGroupHeadline; +import bisq.desktop.main.dao.wallet.BsqBalanceUtil; +import bisq.desktop.util.GUIUtil; + +import bisq.core.btc.wallet.WalletsSetup; +import bisq.core.dao.DaoFacade; +import bisq.core.dao.role.BondedRoleType; +import bisq.core.dao.state.BsqStateListener; +import bisq.core.dao.state.blockchain.Block; +import bisq.core.locale.Res; +import bisq.core.user.Preferences; +import bisq.core.util.BsqFormatter; + +import bisq.network.p2p.P2PService; + +import javax.inject.Inject; + +import de.jensd.fx.fontawesome.AwesomeIcon; + +import javafx.scene.control.Button; +import javafx.scene.control.Hyperlink; +import javafx.scene.control.Label; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.GridPane; + +import javafx.geometry.Insets; + +import javafx.beans.property.ReadOnlyObjectWrapper; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.collections.transformation.SortedList; + +import javafx.util.Callback; + +import java.util.stream.Collectors; + +@FxmlView +public class BondedRolesView extends ActivatableView implements BsqStateListener { + private TableView tableView; + + private final BsqFormatter bsqFormatter; + private final DaoFacade daoFacade; + private final Preferences preferences; + + private final WalletsSetup walletsSetup; + private final P2PService p2PService; + + private int gridRow = 0; + + private final ObservableList observableList = FXCollections.observableArrayList(); + private final SortedList sortedList = new SortedList<>(observableList); + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor, lifecycle + /////////////////////////////////////////////////////////////////////////////////////////// + + @Inject + private BondedRolesView(BsqFormatter bsqFormatter, + BsqBalanceUtil bsqBalanceUtil, + DaoFacade daoFacade, + Preferences preferences, + WalletsSetup walletsSetup, + P2PService p2PService) { + this.bsqFormatter = bsqFormatter; + this.daoFacade = daoFacade; + this.preferences = preferences; + this.walletsSetup = walletsSetup; + this.p2PService = p2PService; + } + + @Override + public void initialize() { + TableGroupHeadline headline = new TableGroupHeadline(Res.get("dao.bond.table.header")); + GridPane.setRowIndex(headline, gridRow); + GridPane.setMargin(headline, new Insets(0, -10, -10, -10)); + GridPane.setColumnSpan(headline, 2); + root.getChildren().add(headline); + + tableView = new TableView<>(); + tableView.setPlaceholder(new AutoTooltipLabel(Res.get("table.placeholder.noData"))); + tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + + createColumns(); + + GridPane.setRowIndex(tableView, gridRow); + GridPane.setMargin(tableView, new Insets(20, -10, 5, -10)); + GridPane.setColumnSpan(tableView, 2); + root.getChildren().add(tableView); + + sortedList.comparatorProperty().bind(tableView.comparatorProperty()); + tableView.setItems(sortedList); + } + + @Override + protected void activate() { + daoFacade.addBsqStateListener(this); + + updateList(); + } + + @Override + protected void deactivate() { + daoFacade.removeBsqStateListener(this); + + observableList.forEach(e -> BondedRolesListItem.cleanup()); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // BsqStateListener + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void onNewBlockHeight(int blockHeight) { + } + + @Override + public void onEmptyBlockAdded(Block block) { + } + + @Override + public void onParseTxsComplete(Block block) { + updateList(); + } + + @Override + public void onParseBlockChainComplete() { + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + + + private void updateList() { + observableList.forEach(e -> BondedRolesListItem.cleanup()); + observableList.setAll(daoFacade.getBondedRoleList().stream() + .map(bondedRole -> new BondedRolesListItem(bondedRole, daoFacade, bsqFormatter)) + .collect(Collectors.toList())); + } + + private void onButtonClick() { + if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) { + // revoke TODO + /* Optional lockupTxOutput = daoFacade.getBondedRolesOutput(selectedItem.getTxId()); + if (!lockupTxOutput.isPresent()) { + log.warn("Lockup output not found, txId = ", selectedItem.getTxId()); + return; + } + + Coin unlockAmount = Coin.valueOf(lockupTxOutput.get().getValue()); + Optional opLockTime = daoFacade.getLockTime(selectedItem.getTxId()); + int lockTime = opLockTime.orElse(-1); + + try { + new Popup<>().headLine(Res.get("dao.bonding.unlock.sendTx.headline")) + .confirmation(Res.get("dao.bonding.unlock.sendTx.details", + bsqFormatter.formatCoinWithCode(unlockAmount), + lockTime + )) + .actionButtonText(Res.get("shared.yes")) + .onAction(() -> { + daoFacade.publishUnlockTx(selectedItem.getTxId(), + () -> { + new Popup<>().confirmation(Res.get("dao.tx.published.success")).show(); + }, + errorMessage -> new Popup<>().warning(errorMessage.toString()).show() + ); + }) + .closeButtonText(Res.get("shared.cancel")) + .show(); + } catch (Throwable t) { + log.error(t.toString()); + t.printStackTrace(); + new Popup<>().warning(t.getMessage()).show(); + }*/ + } else { + GUIUtil.showNotReadyForTxBroadcastPopups(p2PService, walletsSetup); + } + // log.info("unlock tx: {}", selectedItem.getTxId()); + } + + + private void openTxInBlockExplorer(String transactionId) { + if (transactionId != null) + GUIUtil.openWebPage(preferences.getBsqBlockChainExplorer().txUrl + transactionId); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Table columns + /////////////////////////////////////////////////////////////////////////////////////////// + + private void createColumns() { + TableColumn column; + + column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.header.name")); + column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); + column.setMinWidth(120); + column.setCellFactory( + new Callback, TableCell>() { + + @Override + public TableCell call(TableColumn column) { + return new TableCell() { + @Override + public void updateItem(final BondedRolesListItem item, boolean empty) { + super.updateItem(item, empty); + if (item != null && !empty) { + setText(item.getBondedRole().getName()); + } else + setText(""); + } + }; + } + }); + tableView.getColumns().add(column); + + column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.header.linkToAccount")); + column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); + column.setMinWidth(60); + column.setCellFactory( + new Callback, TableCell>() { + + @Override + public TableCell call(TableColumn column) { + return new TableCell() { + private HyperlinkWithIcon hyperlinkWithIcon; + + @Override + public void updateItem(final BondedRolesListItem item, boolean empty) { + super.updateItem(item, empty); + if (item != null && !empty) { + String link = item.getBondedRole().getLinkToAccount(); + hyperlinkWithIcon = new HyperlinkWithIcon(link, AwesomeIcon.EXTERNAL_LINK); + hyperlinkWithIcon.setOnAction(event -> GUIUtil.openWebPage(link)); + hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("shared.openURL", link))); + setGraphic(hyperlinkWithIcon); + } else { + setGraphic(null); + if (hyperlinkWithIcon != null) + hyperlinkWithIcon.setOnAction(null); + } + } + }; + } + }); + tableView.getColumns().add(column); + + column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.header.bondedRoleType")); + column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); + column.setMinWidth(120); + column.setCellFactory( + new Callback, TableCell>() { + + @Override + public TableCell call(TableColumn column) { + return new TableCell() { + private Hyperlink hyperlink; + + @Override + public void updateItem(final BondedRolesListItem item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + BondedRoleType bondedRoleType = item.getBondedRole().getBondedRoleType(); + String type = bondedRoleType.getDisplayString(); + hyperlink = new Hyperlink(type); + hyperlink.setOnAction(event -> { + new BondedRoleTypeWindow(bondedRoleType, bsqFormatter).show(); + }); + hyperlink.setTooltip(new Tooltip(Res.get("tooltip.openPopupForDetails", type))); + setGraphic(hyperlink); + } else { + setGraphic(null); + if (hyperlink != null) + hyperlink.setOnAction(null); + } + } + }; + } + }); + tableView.getColumns().add(column); + + column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.header.startDate")); + column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); + column.setMinWidth(60); + column.setCellFactory( + new Callback, TableCell>() { + + @Override + public TableCell call(TableColumn column) { + return new TableCell() { + @Override + public void updateItem(final BondedRolesListItem item, boolean empty) { + super.updateItem(item, empty); + if (item != null && !empty) { + setText(item.getStartDate()); + } else + setText(""); + } + }; + } + }); + tableView.getColumns().add(column); + + column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.header.revokeDate")); + column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); + column.setMinWidth(60); + column.setCellFactory( + new Callback, TableCell>() { + + @Override + public TableCell call(TableColumn column) { + return new TableCell() { + @Override + public void updateItem(final BondedRolesListItem item, boolean empty) { + super.updateItem(item, empty); + if (item != null && !empty) { + setText(item.getRevokeDate()); + } else + setText(""); + } + }; + } + }); + tableView.getColumns().add(column); + + column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.header.lockupTxId")); + column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); + column.setMinWidth(60); + column.setCellFactory( + new Callback, TableCell>() { + + @Override + public TableCell call(TableColumn column) { + return new TableCell() { + private HyperlinkWithIcon hyperlinkWithIcon; + private Label label; + + @Override + public void updateItem(final BondedRolesListItem item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + String transactionId = item.getBondedRole().getLockupTxId(); + if (transactionId != null) { + hyperlinkWithIcon = new HyperlinkWithIcon(transactionId, AwesomeIcon.EXTERNAL_LINK); + hyperlinkWithIcon.setOnAction(event -> openTxInBlockExplorer(transactionId)); + hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", transactionId))); + setGraphic(hyperlinkWithIcon); + } else { + label = new Label("-"); + setGraphic(label); + } + } else { + setGraphic(null); + if (hyperlinkWithIcon != null) + hyperlinkWithIcon.setOnAction(null); + if (label != null) + label = null; + } + } + }; + } + }); + tableView.getColumns().add(column); + + column = new AutoTooltipTableColumn<>(Res.get("dao.bond.table.column.header.unlockTxId")); + column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); + column.setMinWidth(60); + column.setCellFactory( + new Callback, TableCell>() { + + @Override + public TableCell call(TableColumn column) { + return new TableCell() { + private HyperlinkWithIcon hyperlinkWithIcon; + private Label label; + + @Override + public void updateItem(final BondedRolesListItem item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + String transactionId = item.getBondedRole().getUnlockTxId(); + if (transactionId != null) { + hyperlinkWithIcon = new HyperlinkWithIcon(transactionId, AwesomeIcon.EXTERNAL_LINK); + hyperlinkWithIcon.setOnAction(event -> openTxInBlockExplorer(transactionId)); + hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", transactionId))); + setGraphic(hyperlinkWithIcon); + } else { + label = new Label("-"); + setGraphic(label); + } + } else { + setGraphic(null); + if (hyperlinkWithIcon != null) + hyperlinkWithIcon.setOnAction(null); + if (label != null) + label = null; + } + } + }; + } + }); + tableView.getColumns().add(column); + + column = new TableColumn<>(); + column.setCellValueFactory(item -> new ReadOnlyObjectWrapper<>(item.getValue())); + column.setMinWidth(60); + column.setCellFactory( + new Callback, TableCell>() { + @Override + public TableCell call(TableColumn column) { + return new TableCell() { + Button button; + + @Override + public void updateItem(final BondedRolesListItem item, boolean empty) { + super.updateItem(item, empty); + + if (item != null && !empty) { + if (button == null) { + button = item.getButton(); + button.setOnAction(e -> { + onButtonClick(); + }); + setGraphic(button); + } + } else { + setGraphic(null); + if (button != null) { + button.setOnAction(null); + button = null; + } + } + } + }; + } + }); + tableView.getColumns().add(column); + } +} diff --git a/src/main/java/bisq/desktop/main/dao/bonding/unlock/UnlockView.java b/src/main/java/bisq/desktop/main/dao/bonding/unlock/UnlockView.java index 1d0d01587f..80ac11c52f 100644 --- a/src/main/java/bisq/desktop/main/dao/bonding/unlock/UnlockView.java +++ b/src/main/java/bisq/desktop/main/dao/bonding/unlock/UnlockView.java @@ -422,8 +422,8 @@ public class UnlockView extends ActivatableView implements BsqBa if (button == null) { button = item.getButton(); button.setOnAction(e -> { - UnlockView.this.selectedItem = item; - UnlockView.this.onButtonClick(); + selectedItem = item; + onButtonClick(); }); setGraphic(button); } diff --git a/src/main/java/bisq/desktop/main/dao/proposal/ProposalDisplay.java b/src/main/java/bisq/desktop/main/dao/proposal/ProposalDisplay.java index e44438eec0..4ee1c19c5a 100644 --- a/src/main/java/bisq/desktop/main/dao/proposal/ProposalDisplay.java +++ b/src/main/java/bisq/desktop/main/dao/proposal/ProposalDisplay.java @@ -28,8 +28,8 @@ import bisq.desktop.util.validation.BsqValidator; import bisq.core.btc.wallet.BsqWalletService; import bisq.core.dao.DaoFacade; -import bisq.core.dao.bonding.Bond; -import bisq.core.dao.bonding.Bonds; +import bisq.core.dao.role.BondedRole; +import bisq.core.dao.role.BondedRoleType; import bisq.core.dao.state.blockchain.Tx; import bisq.core.dao.state.ext.Param; import bisq.core.dao.voting.proposal.Proposal; @@ -39,10 +39,14 @@ import bisq.core.dao.voting.proposal.compensation.CompensationConsensus; import bisq.core.dao.voting.proposal.compensation.CompensationProposal; import bisq.core.dao.voting.proposal.confiscatebond.ConfiscateBondProposal; import bisq.core.dao.voting.proposal.param.ChangeParamProposal; +import bisq.core.dao.voting.proposal.role.BondedRoleProposal; import bisq.core.locale.Res; import bisq.core.util.BsqFormatter; +import bisq.common.util.Tuple2; + import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; @@ -56,12 +60,13 @@ import javafx.geometry.HPos; import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; -import javafx.collections.ObservableList; import javafx.util.StringConverter; -import java.util.Optional; +import java.util.Arrays; +import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -80,7 +85,9 @@ public class ProposalDisplay { private DaoFacade daoFacade; private InputTextField uidTextField; private TextField proposalFeeTextField; + private TextField proposalTypeTextField; public InputTextField nameTextField; + @Nullable public InputTextField titleTextField; public InputTextField linkInputTextField; @Nullable @@ -88,15 +95,20 @@ public class ProposalDisplay { @Nullable public ComboBox paramComboBox; @Nullable - public ComboBox confiscateBondComboBox; + public ComboBox confiscateBondComboBox; + @Nullable + public ComboBox bondedRoleTypeComboBox; + @Getter private int gridRow; + @Nullable public TextArea descriptionTextArea; private HyperlinkWithIcon linkHyperlinkWithIcon; @Nullable private TxIdTextField txIdTextField; private final ChangeListener descriptionTextAreaListener; private int gridRowStartIndex; + private Label linkLabel; // TODO get that warning at closing the window... @@ -115,90 +127,131 @@ public class ProposalDisplay { descriptionTextAreaListener = (observable, oldValue, newValue) -> { if (!ProposalConsensus.isDescriptionSizeValid(newValue)) { new Popup<>().warning(Res.get("dao.proposal.display.description.tooLong", maxLengthDescriptionText)).show(); - descriptionTextArea.setText(newValue.substring(0, maxLengthDescriptionText)); + if (descriptionTextArea != null) + descriptionTextArea.setText(newValue.substring(0, maxLengthDescriptionText)); } }; } public void createAllFields(String title, int gridRowStartIndex, double top, ProposalType proposalType, - boolean isMakeProposalScreen, boolean showDetails) { + boolean isMakeProposalScreen) { removeAllFields(); this.gridRowStartIndex = gridRowStartIndex; this.gridRow = gridRowStartIndex; - int rowSpan; - - boolean hasAddedFields = proposalType == ProposalType.COMPENSATION_REQUEST || - proposalType == ProposalType.CHANGE_PARAM || proposalType == ProposalType.CONFISCATE_BOND; - if (isMakeProposalScreen) { - rowSpan = hasAddedFields ? 8 : 6; - } else if (showDetails) { - rowSpan = hasAddedFields ? 9 : 7; - } else { - //noinspection IfCanBeSwitch - if (proposalType == ProposalType.COMPENSATION_REQUEST) - rowSpan = 6; - else if (proposalType == ProposalType.CHANGE_PARAM) - rowSpan = 7; - else if (proposalType == ProposalType.CONFISCATE_BOND) - rowSpan = 6; - else - rowSpan = 5; - } - - addTitledGroupBg(gridPane, gridRow, rowSpan, title, top); - if (showDetails) { - uidTextField = addLabelInputTextField(gridPane, gridRow, - Res.getWithCol("shared.id"), top == Layout.GROUP_DISTANCE ? Layout.FIRST_ROW_AND_GROUP_DISTANCE : Layout.FIRST_ROW_DISTANCE).second; - uidTextField.setEditable(false); - nameTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.name")).second; - } else { - nameTextField = addLabelInputTextField(gridPane, gridRow, Res.get("dao.proposal.display.name"), - top == Layout.GROUP_DISTANCE ? Layout.FIRST_ROW_AND_GROUP_DISTANCE : Layout.FIRST_ROW_DISTANCE).second; - } - - titleTextField = addLabelInputTextField(gridPane, ++gridRow, Res.getWithCol("dao.proposal.title")).second; - - descriptionTextArea = addLabelTextArea(gridPane, ++gridRow, Res.get("dao.proposal.display.description"), - Res.get("dao.proposal.display.description.prompt", maxLengthDescriptionText)).second; - descriptionTextArea.setMaxHeight(42); // for 2 lines - descriptionTextArea.setMinHeight(descriptionTextArea.getMaxHeight()); - if (isMakeProposalScreen) - 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); - linkHyperlinkWithIcon.setManaged(false); - linkInputTextField.setPromptText(Res.get("dao.proposal.display.link.prompt")); + int rowSpan = 5; + boolean showTitle = true; + boolean showDescription = true; switch (proposalType) { case COMPENSATION_REQUEST: + rowSpan = 6; + break; + case BONDED_ROLE: + rowSpan = 3; + showTitle = false; + showDescription = false; + break; + case REMOVE_ALTCOIN: + break; + case CHANGE_PARAM: + rowSpan = 6; + break; + case GENERIC: + break; + case CONFISCATE_BOND: + rowSpan = 5; + break; + } + if (isMakeProposalScreen) + rowSpan += 2; - requestedBsqTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.requestedBsq")).second; + addTitledGroupBg(gridPane, gridRow, rowSpan, title, top); + double proposalTypeTop = top == Layout.GROUP_DISTANCE ? Layout.FIRST_ROW_AND_GROUP_DISTANCE : Layout.FIRST_ROW_DISTANCE; + proposalTypeTextField = addLabelTextField(gridPane, gridRow, + Res.getWithCol("dao.proposal.display.type"), proposalType.getDisplayName(), proposalTypeTop).second; + + if (!isMakeProposalScreen) { + uidTextField = addLabelInputTextField(gridPane, ++gridRow, Res.getWithCol("shared.id")).second; + uidTextField.setEditable(false); + nameTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.name")).second; + } else { + nameTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.name")).second; + } + + if (showTitle) + titleTextField = addLabelInputTextField(gridPane, ++gridRow, Res.getWithCol("dao.proposal.title")).second; + + if (showDescription) { + descriptionTextArea = addLabelTextArea(gridPane, ++gridRow, Res.get("dao.proposal.display.description"), + Res.get("dao.proposal.display.description.prompt", maxLengthDescriptionText)).second; + descriptionTextArea.setMaxHeight(42); // for 2 lines + descriptionTextArea.setMinHeight(descriptionTextArea.getMaxHeight()); + if (isMakeProposalScreen) + descriptionTextArea.textProperty().addListener(descriptionTextAreaListener); + } + + Tuple2 tuple = addLabelInputTextField(gridPane, ++gridRow, + Res.get("dao.proposal.display.link")); + linkLabel = tuple.first; + linkInputTextField = tuple.second; + linkInputTextField.setPromptText(Res.get("dao.proposal.display.link.prompt")); + + linkHyperlinkWithIcon = addLabelHyperlinkWithIcon(gridPane, gridRow, + "", "", "").second; + linkHyperlinkWithIcon.setVisible(false); + linkHyperlinkWithIcon.setManaged(false); + + switch (proposalType) { + case COMPENSATION_REQUEST: + requestedBsqTextField = addLabelInputTextField(gridPane, ++gridRow, + Res.get("dao.proposal.display.requestedBsq")).second; BsqValidator bsqValidator = new BsqValidator(bsqFormatter); bsqValidator.setMinValue(CompensationConsensus.getMinCompensationRequestAmount()); checkNotNull(requestedBsqTextField, "requestedBsqTextField must not be null"); requestedBsqTextField.setValidator(bsqValidator); // TODO validator, addressTF - if (showDetails) { - bsqAddressTextField = addLabelInputTextField(gridPane, ++gridRow, - Res.get("dao.proposal.display.bsqAddress")).second; - checkNotNull(bsqAddressTextField, "bsqAddressTextField must not be null"); - bsqAddressTextField.setText("B" + bsqWalletService.getUnusedAddress().toBase58()); - bsqAddressTextField.setValidator(new BsqAddressValidator(bsqFormatter)); - } + bsqAddressTextField = addLabelInputTextField(gridPane, ++gridRow, + Res.get("dao.proposal.display.bsqAddress")).second; + checkNotNull(bsqAddressTextField, "bsqAddressTextField must not be null"); + bsqAddressTextField.setText("B" + bsqWalletService.getUnusedAddress().toBase58()); + bsqAddressTextField.setValidator(new BsqAddressValidator(bsqFormatter)); break; - case GENERIC: + case BONDED_ROLE: + linkLabel.setText(Res.get("dao.proposal.display.link.bondRole")); + linkInputTextField.setPromptText(Res.get("dao.proposal.display.link.bondRole.prompt")); + bondedRoleTypeComboBox = addLabelComboBox(gridPane, ++gridRow, + Res.getWithCol("dao.proposal.display.bondedRoleComboBox.label")).second; + checkNotNull(bondedRoleTypeComboBox, "bondedRoleTypeComboBox must not be null"); + bondedRoleTypeComboBox.setPromptText(Res.get("shared.select")); + bondedRoleTypeComboBox.setItems(FXCollections.observableArrayList(BondedRoleType.values())); + bondedRoleTypeComboBox.setConverter(new StringConverter() { + @Override + public String toString(BondedRoleType bondedRoleType) { + return bondedRoleType.getDisplayString(); + } + + @Override + public BondedRoleType fromString(String string) { + return null; + } + }); + break; + case REMOVE_ALTCOIN: break; case CHANGE_PARAM: checkNotNull(gridPane, "gridPane must not be null"); - paramComboBox = addLabelComboBox(gridPane, ++gridRow, Res.get("dao.proposal.display.paramComboBox.label")).second; + paramComboBox = addLabelComboBox(gridPane, ++gridRow, + Res.getWithCol("dao.proposal.display.paramComboBox.label")).second; checkNotNull(paramComboBox, "paramComboBox must not be null"); - paramComboBox.setItems(FXCollections.observableArrayList(Param.values())); + paramComboBox.setPromptText(Res.get("shared.select")); + List list = Arrays.stream(Param.values()) + .filter(e -> e != Param.UNDEFINED && e != Param.PHASE_UNDEFINED) + .collect(Collectors.toList()); + paramComboBox.setItems(FXCollections.observableArrayList(list)); paramComboBox.setConverter(new StringConverter() { @Override public String toString(Param param) { - return param.name(); + return param.toDisplayString(); } @Override @@ -206,33 +259,32 @@ public class ProposalDisplay { return null; } }); - paramValueTextField = addLabelInputTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.paramValue")).second; + paramValueTextField = addLabelInputTextField(gridPane, ++gridRow, + Res.get("dao.proposal.display.paramValue")).second; break; - case REMOVE_ALTCOIN: + case GENERIC: break; case CONFISCATE_BOND: confiscateBondComboBox = addLabelComboBox(gridPane, ++gridRow, - Res.get("dao.proposal.display.confiscateBondComboBox.label")).second; - ObservableList lockupAndUnlockingBondIds = - FXCollections.observableArrayList(daoFacade.getLockupAndUnlockingBondIds()); - confiscateBondComboBox.setItems(lockupAndUnlockingBondIds); - confiscateBondComboBox.setConverter(new StringConverter() { + Res.getWithCol("dao.proposal.display.confiscateBondComboBox.label")).second; + checkNotNull(confiscateBondComboBox, "confiscateBondComboBox must not be null"); + confiscateBondComboBox.setPromptText(Res.get("shared.select")); + confiscateBondComboBox.setItems(FXCollections.observableArrayList(daoFacade.getValidBondedRoleList())); + confiscateBondComboBox.setConverter(new StringConverter() { @Override - public String toString(byte[] hashOfBondId) { - Optional bond = Bonds.getBond(hashOfBondId); - return bond.isPresent() ? bond.get().toDisplayString() : "-"; + public String toString(BondedRole bondedRole) { + return bondedRole.getDisplayString(); } @Override - public byte[] fromString(String string) { + public BondedRole fromString(String string) { return null; } }); - break; } - if (!isMakeProposalScreen && showDetails) + if (!isMakeProposalScreen) txIdTextField = addLabelTxIdTextField(gridPane, ++gridRow, Res.get("dao.proposal.display.txId"), "").second; @@ -242,11 +294,14 @@ public class ProposalDisplay { } public void applyProposalPayload(Proposal proposal) { + proposalTypeTextField.setText(proposal.getType().getDisplayName()); if (uidTextField != null) uidTextField.setText(proposal.getUid()); nameTextField.setText(proposal.getName()); - titleTextField.setText(proposal.getTitle()); - descriptionTextArea.setText(proposal.getDescription()); + if (titleTextField != null) + titleTextField.setText(proposal.getTitle()); + if (descriptionTextArea != null) + descriptionTextArea.setText(proposal.getDescription()); linkInputTextField.setVisible(false); linkInputTextField.setManaged(false); linkHyperlinkWithIcon.setVisible(true); @@ -265,10 +320,17 @@ public class ProposalDisplay { paramComboBox.getSelectionModel().select(changeParamProposal.getParam()); checkNotNull(paramValueTextField, "paramValueTextField must not be null"); paramValueTextField.setText(String.valueOf(changeParamProposal.getParamValue())); + } else if (proposal instanceof BondedRoleProposal) { + BondedRoleProposal bondedRoleProposal = (BondedRoleProposal) proposal; + checkNotNull(bondedRoleTypeComboBox, "bondedRoleComboBox must not be null"); + BondedRole bondedRole = bondedRoleProposal.getBondedRole(); + bondedRoleTypeComboBox.getSelectionModel().select(bondedRole.getBondedRoleType()); + } else if (proposal instanceof ConfiscateBondProposal) { ConfiscateBondProposal confiscateBondProposal = (ConfiscateBondProposal) proposal; checkNotNull(confiscateBondComboBox, "confiscateBondComboBox must not be null"); - confiscateBondComboBox.getSelectionModel().select(confiscateBondProposal.getHashOfBondId()); + daoFacade.getBondedRoleFromHash(confiscateBondProposal.getHash()) + .ifPresent(bondedRole -> confiscateBondComboBox.getSelectionModel().select(bondedRole)); } int chainHeight; if (txIdTextField != null) { @@ -291,6 +353,7 @@ public class ProposalDisplay { if (bsqAddressTextField != null) bsqAddressTextField.clear(); if (paramComboBox != null) paramComboBox.getSelectionModel().clearSelection(); if (paramValueTextField != null) paramValueTextField.clear(); + if (bondedRoleTypeComboBox != null) bondedRoleTypeComboBox.getSelectionModel().clearSelection(); if (confiscateBondComboBox != null) confiscateBondComboBox.getSelectionModel().clearSelection(); if (txIdTextField != null) txIdTextField.cleanup(); if (descriptionTextArea != null) descriptionTextArea.textProperty().removeListener(descriptionTextAreaListener); @@ -299,8 +362,10 @@ public class ProposalDisplay { public void fillWithMock() { uidTextField.setText(UUID.randomUUID().toString()); nameTextField.setText("Manfred Karrer"); - titleTextField.setText("Development work November 2017"); - descriptionTextArea.setText("Development work"); + if (titleTextField != null) + titleTextField.setText("Development work November 2017"); + if (descriptionTextArea != null) + descriptionTextArea.setText("Development work"); linkInputTextField.setText("https://github.com/bisq-network/compensation/issues/12"); if (requestedBsqTextField != null) requestedBsqTextField.setText("14000"); @@ -315,8 +380,10 @@ public class ProposalDisplay { public void setEditable(boolean isEditable) { nameTextField.setEditable(isEditable); - titleTextField.setEditable(isEditable); - descriptionTextArea.setEditable(isEditable); + if (titleTextField != null) + titleTextField.setEditable(isEditable); + if (descriptionTextArea != null) + descriptionTextArea.setEditable(isEditable); linkInputTextField.setEditable(isEditable); if (requestedBsqTextField != null) requestedBsqTextField.setEditable(isEditable); @@ -331,6 +398,9 @@ public class ProposalDisplay { if (confiscateBondComboBox != null) confiscateBondComboBox.setDisable(!isEditable); + if (bondedRoleTypeComboBox != null) + bondedRoleTypeComboBox.setDisable(!isEditable); + linkInputTextField.setVisible(true); linkInputTextField.setManaged(true); linkHyperlinkWithIcon.setVisible(false); diff --git a/src/main/java/bisq/desktop/main/dao/proposal/ProposalWindow.java b/src/main/java/bisq/desktop/main/dao/proposal/ProposalWindow.java index f5f93b6227..04dda005e7 100644 --- a/src/main/java/bisq/desktop/main/dao/proposal/ProposalWindow.java +++ b/src/main/java/bisq/desktop/main/dao/proposal/ProposalWindow.java @@ -59,7 +59,7 @@ public class ProposalWindow extends Overlay { proposalDisplay = new ProposalDisplay(gridPane, bsqFormatter, bsqWalletService, daoFacade); proposalDisplay.createAllFields(Res.get("dao.proposal.details"), 1, Layout.GROUP_DISTANCE, - proposal.getType(), false, true); + proposal.getType(), false); proposalDisplay.setEditable(false); proposalDisplay.applyProposalPayload(proposal); 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 5734eb4af2..c5b95a0087 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 @@ -123,7 +123,7 @@ public class ActiveProposalsView extends ProposalItemsView { //noinspection IfCanBeSwitch,IfCanBeSwitch,IfCanBeSwitch if (phase == DaoPhase.Phase.PROPOSAL) { if (selectedBaseProposalListItem != null && selectedBaseProposalListItem.getProposal() != null) { - button.setText(Res.get("dao.proposal.active.remove")); + button.setText(Res.get("shared.remove")); final boolean isMyProposal = daoFacade.isMyProposal(selectedBaseProposalListItem.getProposal()); button.setVisible(isMyProposal); button.setManaged(isMyProposal); 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 313403f616..ed626a24a5 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 @@ -30,6 +30,7 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.InsufficientBsqException; import bisq.core.btc.wallet.WalletsSetup; import bisq.core.dao.DaoFacade; +import bisq.core.dao.role.BondedRole; import bisq.core.dao.state.BsqStateListener; import bisq.core.dao.state.blockchain.Block; import bisq.core.dao.state.ext.Param; @@ -65,6 +66,8 @@ import javafx.util.StringConverter; import java.io.IOException; import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -120,7 +123,7 @@ public class MakeProposalView extends ActivatableView implements proposalTypeComboBox.setConverter(new StringConverter() { @Override public String toString(ProposalType proposalType) { - return Res.get("dao.proposal.type." + proposalType.name()); + return proposalType.getDisplayName(); } @Override @@ -135,7 +138,12 @@ public class MakeProposalView extends ActivatableView implements addProposalDisplay(); }; - proposalTypeComboBox.setItems(FXCollections.observableArrayList(Arrays.asList(ProposalType.values()))); + //TODO remove filter once all are implemented + List proposalTypes = Arrays.stream(ProposalType.values()) + .filter(proposalType -> proposalType != ProposalType.GENERIC && + proposalType != ProposalType.REMOVE_ALTCOIN) + .collect(Collectors.toList()); + proposalTypeComboBox.setItems(FXCollections.observableArrayList(proposalTypes)); } @Override @@ -234,31 +242,42 @@ public class MakeProposalView extends ActivatableView implements WalletException, IOException { validateInputs(); - + BondedRole bondedRole; switch (type) { case COMPENSATION_REQUEST: checkNotNull(proposalDisplay.requestedBsqTextField, "proposalDisplay.requestedBsqTextField must not be null"); checkNotNull(proposalDisplay.bsqAddressTextField, "proposalDisplay.bsqAddressTextField must not be null"); + checkNotNull(proposalDisplay.titleTextField, + "proposalDisplay.titleTextField must not be null"); + checkNotNull(proposalDisplay.descriptionTextArea, + "proposalDisplay.descriptionTextArea must not be null"); return daoFacade.getCompensationProposalWithTransaction(proposalDisplay.nameTextField.getText(), proposalDisplay.titleTextField.getText(), proposalDisplay.descriptionTextArea.getText(), proposalDisplay.linkInputTextField.getText(), bsqFormatter.parseToCoin(proposalDisplay.requestedBsqTextField.getText()), proposalDisplay.bsqAddressTextField.getText()); - case GENERIC: + case BONDED_ROLE: + checkNotNull(proposalDisplay.bondedRoleTypeComboBox, + "proposalDisplay.bondedRoleTypeComboBox must not be null"); + bondedRole = new BondedRole(proposalDisplay.nameTextField.getText(), + proposalDisplay.linkInputTextField.getText(), + proposalDisplay.bondedRoleTypeComboBox.getSelectionModel().getSelectedItem()); + return daoFacade.getBondedRoleProposalWithTransaction(bondedRole); + case REMOVE_ALTCOIN: //TODO throw new RuntimeException("Not implemented yet"); - /* - return genericBallotFactory.makeTxAndGetGenericProposal( - proposalDisplay.nameTextField.getText(), - proposalDisplay.titleTextField.getText(), - proposalDisplay.descriptionTextArea.getText(), - proposalDisplay.linkInputTextField.getText());*/ case CHANGE_PARAM: - checkNotNull(proposalDisplay.paramComboBox, "proposalDisplay.paramComboBox must no tbe null"); - checkNotNull(proposalDisplay.paramValueTextField, "proposalDisplay.paramValueTextField must no tbe null"); + checkNotNull(proposalDisplay.paramComboBox, + "proposalDisplay.paramComboBox must no tbe null"); + checkNotNull(proposalDisplay.paramValueTextField, + "proposalDisplay.paramValueTextField must no tbe null"); + checkNotNull(proposalDisplay.titleTextField, + "proposalDisplay.titleTextField must not be null"); + checkNotNull(proposalDisplay.descriptionTextArea, + "proposalDisplay.descriptionTextArea must not be null"); Param selectedParam = proposalDisplay.paramComboBox.getSelectionModel().getSelectedItem(); if (selectedParam == null) throw new ValidationException("selectedParam is null"); @@ -272,26 +291,28 @@ public class MakeProposalView extends ActivatableView implements throw new ValidationException("paramValue is not a long value", t); } //TODO add more custom param validation - return daoFacade.getParamProposalWithTransaction(proposalDisplay.nameTextField.getText(), proposalDisplay.titleTextField.getText(), proposalDisplay.descriptionTextArea.getText(), proposalDisplay.linkInputTextField.getText(), selectedParam, paramValue); - case REMOVE_ALTCOIN: + case GENERIC: //TODO throw new RuntimeException("Not implemented yet"); case CONFISCATE_BOND: - byte[] hashOfBondId = proposalDisplay.confiscateBondComboBox.getSelectionModel().getSelectedItem(); - if (hashOfBondId == null || hashOfBondId.length == 0) - throw new ValidationException("Invalid bond id, null or zero length"); - + checkNotNull(proposalDisplay.confiscateBondComboBox, + "proposalDisplay.confiscateBondComboBox must not be null"); + checkNotNull(proposalDisplay.titleTextField, + "proposalDisplay.titleTextField must not be null"); + checkNotNull(proposalDisplay.descriptionTextArea, + "proposalDisplay.descriptionTextArea must not be null"); + bondedRole = proposalDisplay.confiscateBondComboBox.getSelectionModel().getSelectedItem(); return daoFacade.getConfiscateBondProposalWithTransaction(proposalDisplay.nameTextField.getText(), proposalDisplay.titleTextField.getText(), proposalDisplay.descriptionTextArea.getText(), proposalDisplay.linkInputTextField.getText(), - hashOfBondId); + bondedRole.getHash()); default: final String msg = "Undefined ProposalType " + selectedProposalType; log.error(msg); @@ -303,8 +324,9 @@ public class MakeProposalView extends ActivatableView implements if (selectedProposalType != null) { proposalDisplay = new ProposalDisplay(root, bsqFormatter, bsqWalletService, daoFacade); proposalDisplay.createAllFields(Res.get("dao.proposal.create.createNew"), 1, Layout.GROUP_DISTANCE, - selectedProposalType, true, true); - proposalDisplay.fillWithMock(); + selectedProposalType, true); + + // proposalDisplay.fillWithMock(); createButton = addButtonAfterGroup(root, proposalDisplay.incrementAndGetGridRow(), Res.get("dao.proposal.create.create.button")); setCreateButtonHandler(); @@ -332,10 +354,11 @@ public class MakeProposalView extends ActivatableView implements private void validateInputs() { // We check in proposalDisplay that no invalid input as allowed - checkArgument(ProposalConsensus.isDescriptionSizeValid(proposalDisplay.descriptionTextArea.getText()), - "descriptionText must not be longer than " + - ProposalConsensus.getMaxLengthDescriptionText() + " chars"); - + if (proposalDisplay.descriptionTextArea != null) { + checkArgument(ProposalConsensus.isDescriptionSizeValid(proposalDisplay.descriptionTextArea.getText()), + "descriptionText must not be longer than " + + ProposalConsensus.getMaxLengthDescriptionText() + " chars"); + } // TODO add more checks for all input fields } } diff --git a/src/main/java/bisq/desktop/main/dao/results/ResultsView.java b/src/main/java/bisq/desktop/main/dao/results/ResultsView.java index b7a74bc922..1c815e871b 100644 --- a/src/main/java/bisq/desktop/main/dao/results/ResultsView.java +++ b/src/main/java/bisq/desktop/main/dao/results/ResultsView.java @@ -243,6 +243,7 @@ public class ResultsView extends ActivatableViewAndModel(); tableView.setPlaceholder(new AutoTooltipLabel(Res.get("table.placeholder.noData"))); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);