diff --git a/src/main/java/bisq/desktop/bisq.css b/src/main/java/bisq/desktop/bisq.css
index ae7a42c387a..e619c38fa3b 100644
--- a/src/main/java/bisq/desktop/bisq.css
+++ b/src/main/java/bisq/desktop/bisq.css
@@ -373,6 +373,14 @@ bg color of non edit textFields: fafafa
-fx-text-fill: -bs-green;
}
+.dao-accepted-icon {
+ -fx-text-fill: -bs-green;
+}
+
+.dao-rejected-icon {
+ -fx-text-fill: -bs-error-red;
+}
+
.version {
-fx-text-fill: black;
-fx-underline: false;
diff --git a/src/main/java/bisq/desktop/main/dao/DaoView.fxml b/src/main/java/bisq/desktop/main/dao/DaoView.fxml
index 90bd84c653d..5c75ca6afbd 100644
--- a/src/main/java/bisq/desktop/main/dao/DaoView.fxml
+++ b/src/main/java/bisq/desktop/main/dao/DaoView.fxml
@@ -17,13 +17,10 @@
~ along with Bisq. If not, see .
-->
-
-
-
diff --git a/src/main/java/bisq/desktop/main/dao/DaoView.java b/src/main/java/bisq/desktop/main/dao/DaoView.java
index 93e59ab82e9..73f18eef6d1 100644
--- a/src/main/java/bisq/desktop/main/dao/DaoView.java
+++ b/src/main/java/bisq/desktop/main/dao/DaoView.java
@@ -26,6 +26,7 @@
import bisq.desktop.common.view.ViewLoader;
import bisq.desktop.main.MainView;
import bisq.desktop.main.dao.bonding.BondingView;
+import bisq.desktop.main.dao.cycles.CyclesView;
import bisq.desktop.main.dao.proposal.ProposalView;
import bisq.desktop.main.dao.voting.VotingView;
import bisq.desktop.main.dao.wallet.BsqWalletView;
@@ -49,7 +50,7 @@
public class DaoView extends ActivatableViewAndModel {
@FXML
- Tab bsqWalletTab, proposalsTab, votingTab, bondingTab;
+ Tab bsqWalletTab, proposalsTab, votingTab, resultsTab, bondingTab;
private Navigation.Listener navigationListener;
private ChangeListener tabChangeListener;
@@ -68,22 +69,27 @@ private DaoView(CachingViewLoader viewLoader, Navigation navigation) {
@Override
public void initialize() {
+ bsqWalletTab = new Tab(Res.get("dao.tab.bsqWallet"));
proposalsTab = new Tab(Res.get("dao.tab.proposals"));
votingTab = new Tab(Res.get("dao.tab.voting"));
+ resultsTab = new Tab(Res.get("dao.tab.results"));
bondingTab = new Tab(Res.get("dao.tab.bonding"));
+
+ bsqWalletTab.setClosable(false);
proposalsTab.setClosable(false);
votingTab.setClosable(false);
+ resultsTab.setClosable(false);
bondingTab.setClosable(false);
- root.getTabs().addAll(proposalsTab, votingTab, bondingTab);
+
+ root.getTabs().addAll(bsqWalletTab, proposalsTab, votingTab, resultsTab, bondingTab);
if (!BisqEnvironment.isDAOActivatedAndBaseCurrencySupportingBsq() || !DevEnv.isDaoPhase2Activated()) {
votingTab.setDisable(true);
bondingTab.setDisable(true);
+ resultsTab.setDisable(true);
proposalsTab.setDisable(true);
}
- bsqWalletTab.setText(Res.get("dao.tab.bsqWallet"));
-
navigationListener = viewPath -> {
if (viewPath.size() == 3 && viewPath.indexOf(DaoView.class) == 1) {
if (proposalsTab == null && viewPath.get(2).equals(BsqWalletView.class))
@@ -109,6 +115,9 @@ public void initialize() {
} else if (newValue == votingTab) {
//noinspection unchecked
navigation.navigateTo(MainView.class, DaoView.class, VotingView.class);
+ } else if (newValue == resultsTab) {
+ //noinspection unchecked
+ navigation.navigateTo(MainView.class, DaoView.class, CyclesView.class);
} else if (newValue == bondingTab) {
//noinspection unchecked
navigation.navigateTo(MainView.class, DaoView.class, BondingView.class);
@@ -132,6 +141,9 @@ else if (selectedItem == proposalsTab)
else if (selectedItem == votingTab)
//noinspection unchecked
navigation.navigateTo(MainView.class, DaoView.class, VotingView.class);
+ else if (selectedItem == resultsTab)
+ //noinspection unchecked
+ navigation.navigateTo(MainView.class, DaoView.class, CyclesView.class);
else if (selectedItem == bondingTab)
//noinspection unchecked
navigation.navigateTo(MainView.class, DaoView.class, BondingView.class);
@@ -153,6 +165,8 @@ private void loadView(Class extends View> viewClass) {
selectedTab = proposalsTab;
} else if (view instanceof VotingView) {
selectedTab = votingTab;
+ } else if (view instanceof CyclesView) {
+ selectedTab = resultsTab;
} else if (view instanceof BondingView) {
selectedTab = bondingTab;
}
diff --git a/src/main/java/bisq/desktop/main/dao/cycles/CyclesListItem.java b/src/main/java/bisq/desktop/main/dao/cycles/CyclesListItem.java
new file mode 100644
index 00000000000..f7b1cf13734
--- /dev/null
+++ b/src/main/java/bisq/desktop/main/dao/cycles/CyclesListItem.java
@@ -0,0 +1,73 @@
+/*
+ * 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.cycles;
+
+import bisq.desktop.main.dao.cycles.model.CycleResult;
+
+import bisq.core.dao.voting.proposal.compensation.CompensationProposal;
+import bisq.core.locale.Res;
+import bisq.core.util.BsqFormatter;
+
+import org.bitcoinj.core.Coin;
+
+import java.util.Date;
+
+import lombok.Getter;
+
+public class CyclesListItem {
+ private BsqFormatter bsqFormatter;
+ @Getter
+ private CycleResult cycleResult;
+
+ public CyclesListItem(CycleResult cycleResult,
+ BsqFormatter bsqFormatter) {
+ this.cycleResult = cycleResult;
+ this.bsqFormatter = bsqFormatter;
+ }
+
+ public String getCycle() {
+ int displayIndex = cycleResult.getCycleIndex() + 1;
+ String dateTime = bsqFormatter.formatDateTime(new Date(cycleResult.getCycleStartTime()));
+ return Res.get("dao.results.results.table.item.cycle", displayIndex, dateTime);
+ }
+
+ public String getNumProposals() {
+ return String.valueOf(cycleResult.getEvaluatedProposals().size());
+ }
+
+ public String getNumVotesAsString() {
+ return String.valueOf(cycleResult.getNumVotes());
+ }
+
+ public String getStake() {
+ return bsqFormatter.formatCoinWithCode(Coin.valueOf(cycleResult.getTotalStake()));
+ }
+
+ public String getIssuance() {
+ long totalIssuance = cycleResult.getEvaluatedProposals().stream()
+ .filter(e -> e.getProposal() instanceof CompensationProposal)
+ .map(e -> (CompensationProposal) e.getProposal())
+ .mapToLong(e -> e.getRequestedBsq().value)
+ .sum();
+ return bsqFormatter.formatCoinWithCode(Coin.valueOf(totalIssuance));
+ }
+
+ public Long getCycleStartTime() {
+ return cycleResult.getCycleStartTime();
+ }
+}
diff --git a/src/main/java/bisq/desktop/main/dao/proposal/myvotes/MyVotesView.fxml b/src/main/java/bisq/desktop/main/dao/cycles/CyclesView.fxml
similarity index 76%
rename from src/main/java/bisq/desktop/main/dao/proposal/myvotes/MyVotesView.fxml
rename to src/main/java/bisq/desktop/main/dao/cycles/CyclesView.fxml
index 9250e4571ab..ef067e5f4fd 100644
--- a/src/main/java/bisq/desktop/main/dao/proposal/myvotes/MyVotesView.fxml
+++ b/src/main/java/bisq/desktop/main/dao/cycles/CyclesView.fxml
@@ -17,17 +17,21 @@
~ along with Bisq. If not, see .
-->
-
-
+
+
+
+
+
-
+
diff --git a/src/main/java/bisq/desktop/main/dao/cycles/CyclesView.java b/src/main/java/bisq/desktop/main/dao/cycles/CyclesView.java
new file mode 100644
index 00000000000..e68c8a3d90f
--- /dev/null
+++ b/src/main/java/bisq/desktop/main/dao/cycles/CyclesView.java
@@ -0,0 +1,374 @@
+/*
+ * 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.cycles;
+
+
+import bisq.desktop.common.model.Activatable;
+import bisq.desktop.common.view.ActivatableViewAndModel;
+import bisq.desktop.common.view.FxmlView;
+import bisq.desktop.components.AutoTooltipLabel;
+import bisq.desktop.components.AutoTooltipTableColumn;
+import bisq.desktop.components.TableGroupHeadline;
+import bisq.desktop.main.dao.cycles.cycle.CycleDisplay;
+import bisq.desktop.main.dao.cycles.model.CycleResult;
+
+import bisq.core.btc.wallet.BsqWalletService;
+import bisq.core.dao.DaoFacade;
+import bisq.core.dao.state.ChainHeightListener;
+import bisq.core.dao.state.StateService;
+import bisq.core.dao.state.period.Cycle;
+import bisq.core.dao.state.period.CycleService;
+import bisq.core.dao.voting.proposal.Proposal;
+import bisq.core.dao.voting.proposal.ProposalService;
+import bisq.core.dao.voting.proposal.storage.appendonly.ProposalPayload;
+import bisq.core.dao.voting.voteresult.EvaluatedProposal;
+import bisq.core.dao.voting.voteresult.VoteResultService;
+import bisq.core.locale.Res;
+import bisq.core.util.BsqFormatter;
+
+import javax.inject.Inject;
+
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TableCell;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.Priority;
+
+import javafx.geometry.Insets;
+
+import javafx.beans.property.ReadOnlyObjectWrapper;
+import javafx.beans.value.ChangeListener;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.collections.transformation.SortedList;
+
+import javafx.util.Callback;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@FxmlView
+public class CyclesView extends ActivatableViewAndModel implements ChainHeightListener {
+ private final DaoFacade daoFacade;
+ // TODO use daoFacade once dev work completed
+ private final StateService stateService;
+ private final CycleService cycleService;
+ private final VoteResultService voteResultService;
+ private final ProposalService proposalService;
+ private final BsqWalletService bsqWalletService;
+ private final BsqFormatter bsqFormatter;
+
+ private int gridRow = 0;
+ private TableView tableView;
+
+ private final ObservableList itemList = FXCollections.observableArrayList();
+ private final SortedList sortedList = new SortedList<>(itemList);
+ private ChangeListener selectedItemListener;
+ private CyclesListItem selectedItem;
+
+ private GridPane resultGridPane;
+ private CycleDisplay proposalsDisplay;
+ private ScrollPane resultDisplayView;
+ private boolean resultDisplayInitialized;
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor, lifecycle
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Inject
+ private CyclesView(DaoFacade daoFacade,
+ StateService stateService,
+ CycleService cycleService,
+ VoteResultService voteResultService,
+ ProposalService proposalService,
+ BsqWalletService bsqWalletService,
+ BsqFormatter bsqFormatter) {
+ this.daoFacade = daoFacade;
+ this.stateService = stateService;
+ this.cycleService = cycleService;
+ this.voteResultService = voteResultService;
+ this.proposalService = proposalService;
+ this.bsqWalletService = bsqWalletService;
+ this.bsqFormatter = bsqFormatter;
+ }
+
+ @Override
+ public void initialize() {
+ daoFacade.addChainHeightListener(this);
+
+ resultGridPane = new GridPane();
+ createResultsTable();
+ createResultDisplay();
+
+ selectedItemListener = (observable, oldValue, newValue) -> onCycleListItemSelected(newValue);
+ }
+
+ @Override
+ protected void activate() {
+ tableView.getSelectionModel().selectedItemProperty().addListener(selectedItemListener);
+ fillCycleList();
+ }
+
+ @Override
+ protected void deactivate() {
+ tableView.getSelectionModel().selectedItemProperty().removeListener(selectedItemListener);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // ChainHeightListener
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void onChainHeightChanged(int blockHeight) {
+ fillCycleList();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // UI handlers
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ private void onCycleListItemSelected(CyclesListItem item) {
+ selectedItem = item;
+ if (selectedItem != null)
+ createAllFieldsOnResultDisplay(selectedItem);
+ else
+ hideResultDisplay();
+
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Create views
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ private void createResultsTable() {
+ TableGroupHeadline headline = new TableGroupHeadline(Res.get("dao.results.results.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);
+ tableView.setPrefHeight(200);
+
+ createColumns(tableView);
+ GridPane.setRowIndex(tableView, gridRow);
+ GridPane.setMargin(tableView, new Insets(20, -10, 5, -10));
+ GridPane.setColumnSpan(tableView, 2);
+ GridPane.setHgrow(tableView, Priority.ALWAYS);
+ root.getChildren().add(tableView);
+
+ tableView.setItems(sortedList);
+ sortedList.comparatorProperty().bind(tableView.comparatorProperty());
+ }
+
+ private void createResultDisplay() {
+ proposalsDisplay = new CycleDisplay(resultGridPane, bsqWalletService, bsqFormatter);
+ resultDisplayView = proposalsDisplay.getView();
+ GridPane.setMargin(resultDisplayView, new Insets(10, -10, 0, -10));
+ GridPane.setRowIndex(resultDisplayView, ++gridRow);
+ GridPane.setColumnSpan(resultDisplayView, 2);
+ root.getChildren().add(resultDisplayView);
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Private
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ private void fillCycleList() {
+ itemList.clear();
+ stateService.getCycles().forEach(this::addCycleListItem);
+ Collections.reverse(itemList);
+ }
+
+ private void addCycleListItem(Cycle cycle) {
+ List proposalsForCycle = proposalService.getAppendOnlyStoreList().stream()
+ .filter(proposalPayload -> cycleService.isTxInCycle(cycle, proposalPayload.getProposal().getTxId()))
+ .map(ProposalPayload::getProposal)
+ .collect(Collectors.toList());
+
+ List evaluatedProposalsForCycle = voteResultService.getAllEvaluatedProposals().stream()
+ .filter(evaluatedProposal -> cycleService.isTxInCycle(cycle, evaluatedProposal.getProposal().getTxId()))
+ .collect(Collectors.toList());
+
+ long cycleStartTime = stateService.getBlockAtHeight(cycle.getHeightOfFirstBlock())
+ .map(e -> e.getTime() * 1000)
+ .orElse(0L);
+ int cycleIndex = cycleService.getCycleIndex(cycle);
+ CycleResult cycleResult = new CycleResult(cycle,
+ cycleIndex,
+ cycleStartTime,
+ proposalsForCycle,
+ evaluatedProposalsForCycle);
+ CyclesListItem cyclesListItem = new CyclesListItem(cycleResult, bsqFormatter);
+ itemList.add(cyclesListItem);
+ }
+
+ private void hideResultDisplay() {
+ if (resultDisplayInitialized) {
+ proposalsDisplay.removeAllFields();
+ resultDisplayView.setVisible(false);
+ resultDisplayView.setManaged(false);
+ }
+ }
+
+ private void createAllFieldsOnResultDisplay(CyclesListItem cyclesListItem) {
+ resultDisplayView.setVisible(true);
+ resultDisplayView.setManaged(true);
+
+ proposalsDisplay.createAllFields(0, cyclesListItem.getCycleResult());
+ resultDisplayInitialized = true;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // TableColumns
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ private void createColumns(TableView tableView) {
+ TableColumn cycleColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.results.table.header.cycle"));
+ cycleColumn.setMinWidth(160);
+ cycleColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ cycleColumn.setCellFactory(
+ new Callback, TableCell>() {
+ @Override
+ public TableCell call(
+ TableColumn column) {
+ return new TableCell() {
+ @Override
+ public void updateItem(final CyclesListItem item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item != null)
+ setText(item.getCycle());
+ else
+ setText("");
+ }
+ };
+ }
+ });
+ cycleColumn.setComparator(Comparator.comparing(CyclesListItem::getCycleStartTime));
+ tableView.getColumns().add(cycleColumn);
+
+ TableColumn proposalsColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.results.table.header.numProposals"));
+ proposalsColumn.setMinWidth(90);
+ proposalsColumn.setMaxWidth(90);
+ proposalsColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ proposalsColumn.setCellFactory(
+ new Callback, TableCell>() {
+ @Override
+ public TableCell call(
+ TableColumn column) {
+ return new TableCell() {
+ @Override
+ public void updateItem(final CyclesListItem item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item != null)
+ setText(item.getNumProposals());
+ else
+ setText("");
+ }
+ };
+ }
+ });
+ proposalsColumn.setComparator(Comparator.comparing(CyclesListItem::getNumProposals));
+ tableView.getColumns().add(proposalsColumn);
+
+ TableColumn votesColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.results.table.header.numVotes"));
+ votesColumn.setMinWidth(70);
+ votesColumn.setMaxWidth(70);
+ votesColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ votesColumn.setCellFactory(
+ new Callback, TableCell>() {
+ @Override
+ public TableCell call(
+ TableColumn column) {
+ return new TableCell() {
+ @Override
+ public void updateItem(final CyclesListItem item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item != null)
+ setText(item.getNumVotesAsString());
+ else
+ setText("");
+ }
+ };
+ }
+ });
+ votesColumn.setComparator(Comparator.comparing(CyclesListItem::getNumProposals));
+ tableView.getColumns().add(votesColumn);
+
+ TableColumn stakeColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.results.table.header.stake"));
+ stakeColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ stakeColumn.setCellFactory(
+ new Callback, TableCell>() {
+ @Override
+ public TableCell call(
+ TableColumn column) {
+ return new TableCell() {
+ @Override
+ public void updateItem(final CyclesListItem item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item != null)
+ setText(item.getStake());
+ else
+ setText("");
+ }
+ };
+ }
+ });
+ stakeColumn.setComparator(Comparator.comparing(CyclesListItem::getNumProposals));
+ tableView.getColumns().add(stakeColumn);
+
+ TableColumn issuanceColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.results.table.header.issuance"));
+ issuanceColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ issuanceColumn.setCellFactory(
+ new Callback, TableCell>() {
+ @Override
+ public TableCell call(
+ TableColumn column) {
+ return new TableCell() {
+ @Override
+ public void updateItem(final CyclesListItem item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item != null)
+ setText(item.getIssuance());
+ else
+ setText("");
+ }
+ };
+ }
+ });
+ issuanceColumn.setComparator(Comparator.comparing(CyclesListItem::getNumProposals));
+ tableView.getColumns().add(issuanceColumn);
+ }
+}
diff --git a/src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleDetailsWindow.java b/src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleDetailsWindow.java
new file mode 100644
index 00000000000..54de42d5f7d
--- /dev/null
+++ b/src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleDetailsWindow.java
@@ -0,0 +1,88 @@
+/*
+ * 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.cycles.cycle;
+
+import bisq.desktop.main.overlays.Overlay;
+import bisq.desktop.util.FormBuilder;
+
+import bisq.core.dao.voting.voteresult.EvaluatedProposal;
+import bisq.core.locale.Res;
+import bisq.core.util.BsqFormatter;
+
+import javafx.scene.control.Button;
+import javafx.scene.control.TextArea;
+
+import javafx.geometry.Insets;
+
+import javafx.beans.value.ChangeListener;
+
+import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
+
+public class CycleDetailsWindow extends Overlay {
+
+ private final BsqFormatter bsqFormatter;
+ private ChangeListener changeListener;
+ private TextArea textArea;
+ private EvaluatedProposal evaluatedProposal;
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Public API
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public CycleDetailsWindow(BsqFormatter bsqFormatter, EvaluatedProposal evaluatedProposal) {
+ this.bsqFormatter = bsqFormatter;
+ this.evaluatedProposal = evaluatedProposal;
+ type = Type.Confirmation;
+ }
+
+ @Override
+ public void show() {
+ rowIndex = -1;
+ width = 850;
+ createGridPane();
+ addContent();
+ display();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Protected
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ protected void createGridPane() {
+ super.createGridPane();
+ gridPane.setPadding(new Insets(35, 40, 30, 40));
+ gridPane.getStyleClass().add("grid-pane");
+ }
+
+ private void addContent() {
+ addTitledGroupBg(gridPane, ++rowIndex, 5, Res.get("dao.results.result.detail.header"));
+
+ //TODO impl
+ //addLabelTextField(gridPane, rowIndex, Res.get("dao.results.result.detail.header"), evaluatedProposal.getProposal().getName(), Layout.FIRST_ROW_DISTANCE);
+
+
+ Button closeButton = FormBuilder.addButtonAfterGroup(gridPane, ++rowIndex, Res.get("shared.close"));
+ closeButton.setOnAction(e -> {
+ closeHandlerOptional.ifPresent(Runnable::run);
+ hide();
+ });
+ }
+}
diff --git a/src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleDisplay.java b/src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleDisplay.java
new file mode 100644
index 00000000000..c269176f057
--- /dev/null
+++ b/src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleDisplay.java
@@ -0,0 +1,419 @@
+/*
+ * 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.cycles.cycle;
+
+import bisq.desktop.components.AutoTooltipLabel;
+import bisq.desktop.components.AutoTooltipTableColumn;
+import bisq.desktop.components.TableGroupHeadline;
+import bisq.desktop.main.dao.cycles.model.CycleResult;
+import bisq.desktop.main.dao.proposal.ProposalDetailsWindow;
+import bisq.desktop.util.FormBuilder;
+import bisq.desktop.util.GUIUtil;
+
+import bisq.core.btc.wallet.BsqWalletService;
+import bisq.core.dao.voting.proposal.ProposalType;
+import bisq.core.locale.Res;
+import bisq.core.util.BsqFormatter;
+
+import org.bitcoinj.core.Coin;
+
+import de.jensd.fx.fontawesome.AwesomeDude;
+
+import javafx.scene.control.Hyperlink;
+import javafx.scene.control.Label;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TableCell;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.Tooltip;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.ColumnConstraints;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.Priority;
+
+import javafx.geometry.HPos;
+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.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class CycleDisplay {
+ private final GridPane gridPane;
+ private final BsqWalletService bsqWalletService;
+ private final BsqFormatter bsqFormatter;
+
+ private int gridRow;
+ private int gridRowStartIndex;
+ private TableView tableView;
+
+
+ private final ObservableList itemList = FXCollections.observableArrayList();
+ private final SortedList sortedList = new SortedList<>(itemList);
+
+
+ public CycleDisplay(GridPane gridPane, BsqWalletService bsqWalletService, BsqFormatter bsqFormatter) {
+ this.gridPane = gridPane;
+ this.bsqWalletService = bsqWalletService;
+ this.bsqFormatter = bsqFormatter;
+ }
+
+ public void createAllFields(int gridRowStartIndex, CycleResult cycleResult) {
+ removeAllFields();
+
+ this.gridRowStartIndex = gridRowStartIndex;
+ this.gridRow = gridRowStartIndex;
+
+ createTableView();
+
+ itemList.setAll(cycleResult.getEvaluatedProposals().stream().map(e -> new CycleListItem(e, bsqFormatter)).collect(Collectors.toList()));
+
+ Map requiredThresholdByType = new HashMap<>();
+ cycleResult.getEvaluatedProposals().forEach(e -> {
+ requiredThresholdByType.putIfAbsent(e.getProposal().getType(), e.getRequiredThreshold());
+ });
+ Map requiredQuorumByType = new HashMap<>();
+ cycleResult.getEvaluatedProposals().forEach(e -> {
+ requiredQuorumByType.putIfAbsent(e.getProposal().getType(), e.getRequiredQuorum());
+ });
+
+ requiredThresholdByType.forEach((key, value) -> {
+ String title = Res.get("dao.results.result.requiredThreshold." + key.name());
+ String requiredThreshold = String.valueOf(value / 100) + "%";
+ FormBuilder.addLabelTextField(gridPane, ++gridRow, title, requiredThreshold, 0);
+ });
+ requiredQuorumByType.forEach((key, value) -> {
+ String title = Res.get("dao.results.result.requiredQuorum." + key.name());
+ String requiredQuorum = bsqFormatter.formatCoinWithCode(Coin.valueOf(value));
+ FormBuilder.addLabelTextField(gridPane, ++gridRow, title, requiredQuorum);
+ });
+ }
+
+ private void createTableView() {
+ TableGroupHeadline headline = new TableGroupHeadline(Res.get("dao.results.result.header"));
+ GridPane.setRowIndex(headline, gridRow);
+ GridPane.setMargin(headline, new Insets(0, -10, -10, -10));
+ GridPane.setColumnSpan(headline, 2);
+ gridPane.getChildren().add(headline);
+
+ tableView = new TableView<>();
+ tableView.setPlaceholder(new AutoTooltipLabel(Res.get("table.placeholder.noData")));
+ tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
+ tableView.setPrefHeight(200);
+
+ createColumns(tableView);
+ GridPane.setRowIndex(tableView, gridRow);
+ GridPane.setMargin(tableView, new Insets(20, -10, 5, -10));
+ GridPane.setColumnSpan(tableView, 2);
+ GridPane.setHgrow(tableView, Priority.ALWAYS);
+ gridPane.getChildren().add(tableView);
+
+ tableView.setItems(sortedList);
+ sortedList.comparatorProperty().bind(tableView.comparatorProperty());
+ }
+
+ private void createColumns(TableView tableView) {
+ TableColumn proposalColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.result.table.header.proposalOwnerName"));
+ proposalColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ proposalColumn.setCellFactory(
+ new Callback, TableCell>() {
+ @Override
+ public TableCell call(
+ TableColumn column) {
+ return new TableCell() {
+ @Override
+ public void updateItem(final CycleListItem item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item != null)
+ setText(item.getProposalOwnerName());
+ else
+ setText("");
+ }
+ };
+ }
+ });
+ proposalColumn.setComparator(Comparator.comparing(CycleListItem::getProposalOwnerName));
+ tableView.getColumns().add(proposalColumn);
+
+ TableColumn proposalIdColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.result.table.header.proposalId"));
+ proposalIdColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+
+ proposalIdColumn.setCellFactory(
+ new Callback, TableCell>() {
+
+ @Override
+ public TableCell call(TableColumn column) {
+ return new TableCell() {
+ private Hyperlink field;
+
+ @Override
+ public void updateItem(final CycleListItem item, boolean empty) {
+ super.updateItem(item, empty);
+
+ // cycleDetailsWindow.show(item.getEvaluatedProposal())
+ if (item != null && !empty) {
+ field = new Hyperlink(item.getProposalId());
+ //TODO setId or getStyleClass.add does not apply color...
+ //field.getStyleClass().add(item.getColorStyleClass());
+ //field.setId(item.getColorStyleClass());
+ field.setStyle(item.getColorStyle());
+ field.setOnAction(event -> new ProposalDetailsWindow(bsqFormatter,
+ bsqWalletService,
+ item.getEvaluatedProposal().getProposal())
+ .show());
+ field.setTooltip(new Tooltip(Res.get("tooltip.openPopupForDetails")));
+ setGraphic(field);
+ } else {
+ setGraphic(null);
+ if (field != null)
+ field.setOnAction(null);
+ }
+ }
+ };
+ }
+ });
+ proposalIdColumn.setComparator(Comparator.comparing(CycleListItem::getProposalOwnerName));
+ tableView.getColumns().add(proposalIdColumn);
+
+ TableColumn acceptedColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.result.table.header.accepted"));
+ acceptedColumn.setMinWidth(80);
+ acceptedColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ acceptedColumn.setCellFactory(
+ new Callback, TableCell>() {
+ private Hyperlink field;
+
+ @Override
+ public TableCell call(
+ TableColumn column) {
+ return new TableCell() {
+ @Override
+ public void updateItem(final CycleListItem item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item != null && !empty) {
+ field = new Hyperlink(item.getAccepted());
+ field.setStyle(item.getColorStyle());
+ field.setOnAction(event -> new CycleDetailsWindow(bsqFormatter, item.getEvaluatedProposal()).show());
+ field.setTooltip(new Tooltip(Res.get("tooltip.openPopupForDetails")));
+ setGraphic(field);
+ } else {
+ setGraphic(null);
+ if (field != null)
+ field.setOnAction(null);
+ }
+ }
+ };
+ }
+ });
+ acceptedColumn.setComparator(Comparator.comparing(CycleListItem::getAccepted));
+ tableView.getColumns().add(acceptedColumn);
+
+ TableColumn rejectedColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.result.table.header.rejected"));
+ rejectedColumn.setMinWidth(80);
+ rejectedColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ rejectedColumn.setCellFactory(
+ new Callback, TableCell>() {
+ private Hyperlink field;
+
+ @Override
+ public TableCell call(
+ TableColumn column) {
+ return new TableCell() {
+ @Override
+ public void updateItem(final CycleListItem item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item != null && !empty) {
+ field = new Hyperlink(item.getRejected());
+ field.setStyle(item.getColorStyle());
+ field.setOnAction(event -> new CycleDetailsWindow(bsqFormatter, item.getEvaluatedProposal()).show());
+ field.setTooltip(new Tooltip(Res.get("tooltip.openPopupForDetails")));
+ setGraphic(field);
+ } else {
+ setGraphic(null);
+ if (field != null)
+ field.setOnAction(null);
+ }
+ }
+ };
+ }
+ });
+ rejectedColumn.setComparator(Comparator.comparing(CycleListItem::getRejected));
+ tableView.getColumns().add(rejectedColumn);
+
+ TableColumn thresholdColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.result.table.header.threshold"));
+ thresholdColumn.setMinWidth(100);
+ thresholdColumn.setMaxWidth(100);
+ thresholdColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ thresholdColumn.setCellFactory(
+ new Callback, TableCell>() {
+ @Override
+ public TableCell call(
+ TableColumn column) {
+ return new TableCell() {
+ @Override
+ public void updateItem(final CycleListItem item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item != null)
+ setText(item.getThreshold());
+ else
+ setText("");
+ }
+ };
+ }
+ });
+ thresholdColumn.setComparator(Comparator.comparing(CycleListItem::getThreshold));
+ tableView.getColumns().add(thresholdColumn);
+
+ TableColumn quorumColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.result.table.header.quorum"));
+ quorumColumn.setMinWidth(130);
+ quorumColumn.setMaxWidth(130);
+ quorumColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ quorumColumn.setCellFactory(
+ new Callback, TableCell>() {
+ @Override
+ public TableCell call(
+ TableColumn column) {
+ return new TableCell() {
+ @Override
+ public void updateItem(final CycleListItem item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item != null)
+ setText(item.getQuorum());
+ else
+ setText("");
+ }
+ };
+ }
+ });
+ quorumColumn.setComparator(Comparator.comparing(CycleListItem::getThreshold));
+ tableView.getColumns().add(quorumColumn);
+
+ TableColumn issuanceColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.result.table.header.issuance"));
+ issuanceColumn.setMinWidth(130);
+ issuanceColumn.setMaxWidth(130);
+ issuanceColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ issuanceColumn.setCellFactory(
+ new Callback, TableCell>() {
+ @Override
+ public TableCell call(
+ TableColumn column) {
+ return new TableCell() {
+ @Override
+ public void updateItem(final CycleListItem item, boolean empty) {
+ super.updateItem(item, empty);
+ if (item != null)
+ setText(item.getIssuance());
+ else
+ setText("");
+ }
+ };
+ }
+ });
+ issuanceColumn.setComparator(Comparator.comparing(CycleListItem::getThreshold));
+ tableView.getColumns().add(issuanceColumn);
+
+
+ TableColumn resultColumn = new AutoTooltipTableColumn<>(Res.get("dao.results.result.table.header.result"));
+ resultColumn.setMinWidth(60);
+ resultColumn.setMaxWidth(60);
+ resultColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
+ resultColumn.setCellFactory(new Callback,
+ TableCell>() {
+
+ @Override
+ public TableCell call(TableColumn column) {
+ return new TableCell() {
+ Label icon;
+
+ @Override
+ public void updateItem(final CycleListItem item, boolean empty) {
+ super.updateItem(item, empty);
+
+ if (item != null && !empty) {
+ icon = new Label();
+ AwesomeDude.setIcon(icon, item.getIcon());
+ icon.getStyleClass().add(item.getColorStyleClass());
+ setGraphic(icon);
+ } else {
+ setGraphic(null);
+ if (icon != null)
+ icon = null;
+ }
+ }
+ };
+ }
+ });
+
+
+ resultColumn.setComparator(Comparator.comparing(CycleListItem::getThreshold));
+ tableView.getColumns().add(resultColumn);
+ }
+
+ public void removeAllFields() {
+ GUIUtil.removeChildrenFromGridPaneRows(gridPane, gridRowStartIndex, gridRow);
+ gridRow = gridRowStartIndex;
+ }
+
+ public ScrollPane getView() {
+ ScrollPane scrollPane = new ScrollPane();
+ scrollPane.setFitToWidth(true);
+ scrollPane.setFitToHeight(true);
+ scrollPane.setMinHeight(280); // just enough to display overview at voting without scroller
+
+ AnchorPane anchorPane = new AnchorPane();
+ scrollPane.setContent(anchorPane);
+
+ gridPane.setHgap(5);
+ gridPane.setVgap(5);
+ ColumnConstraints columnConstraints1 = new ColumnConstraints();
+ columnConstraints1.setHalignment(HPos.RIGHT);
+ columnConstraints1.setHgrow(Priority.SOMETIMES);
+ columnConstraints1.setMinWidth(140);
+ ColumnConstraints columnConstraints2 = new ColumnConstraints();
+ columnConstraints2.setHgrow(Priority.ALWAYS);
+ columnConstraints2.setMinWidth(300);
+
+ gridPane.getColumnConstraints().addAll(columnConstraints1, columnConstraints2);
+ AnchorPane.setBottomAnchor(gridPane, 20d);
+ AnchorPane.setRightAnchor(gridPane, 10d);
+ AnchorPane.setLeftAnchor(gridPane, 10d);
+ AnchorPane.setTopAnchor(gridPane, 20d);
+ anchorPane.getChildren().add(gridPane);
+
+ return scrollPane;
+ }
+}
diff --git a/src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleListItem.java b/src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleListItem.java
new file mode 100644
index 00000000000..86da4fd9e2a
--- /dev/null
+++ b/src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleListItem.java
@@ -0,0 +1,99 @@
+/*
+ * 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.cycles.cycle;
+
+import bisq.core.dao.voting.proposal.compensation.CompensationProposal;
+import bisq.core.dao.voting.voteresult.EvaluatedProposal;
+import bisq.core.dao.voting.voteresult.ProposalVoteResult;
+import bisq.core.locale.Res;
+import bisq.core.util.BsqFormatter;
+
+import org.bitcoinj.core.Coin;
+
+import de.jensd.fx.fontawesome.AwesomeIcon;
+
+import lombok.Getter;
+
+public class CycleListItem {
+ private final ProposalVoteResult proposalVoteResult;
+ private BsqFormatter bsqFormatter;
+ @Getter
+ private EvaluatedProposal evaluatedProposal;
+
+ public CycleListItem(EvaluatedProposal evaluatedProposal, BsqFormatter bsqFormatter) {
+ this.evaluatedProposal = evaluatedProposal;
+ proposalVoteResult = evaluatedProposal.getProposalVoteResult();
+ this.bsqFormatter = bsqFormatter;
+ }
+
+ public String getProposalOwnerName() {
+ return evaluatedProposal.getProposal().getName();
+ }
+
+ public String getProposalId() {
+ return evaluatedProposal.getProposal().getShortId();
+ }
+
+ public String getAccepted() {
+ return Res.get("dao.results.result.table.item.votes",
+ bsqFormatter.formatCoinWithCode(Coin.valueOf(proposalVoteResult.getStakeOfAcceptedVotes())),
+ String.valueOf(proposalVoteResult.getNumAcceptedVotes()));
+ }
+
+ public String getRejected() {
+ return Res.get("dao.results.result.table.item.votes",
+ bsqFormatter.formatCoinWithCode(Coin.valueOf(proposalVoteResult.getStakeOfRejectedVotes())),
+ String.valueOf(proposalVoteResult.getNumRejectedVotes()));
+ }
+
+ public String getThreshold() {
+ return (proposalVoteResult.getThreshold() / 100D) + "%";
+ }
+
+ public String getQuorum() {
+ return bsqFormatter.formatCoinWithCode(Coin.valueOf(proposalVoteResult.getQuorum()));
+ }
+
+ public AwesomeIcon getIcon() {
+ return evaluatedProposal.isAccepted() ? AwesomeIcon.OK_SIGN : AwesomeIcon.BAN_CIRCLE;
+ }
+
+ public String getColorStyleClass() {
+ return evaluatedProposal.isAccepted() ? "dao-accepted-icon" : "dao-rejected-icon";
+ }
+
+ public String getColorStyle() {
+ return evaluatedProposal.isAccepted() ? "-fx-text-fill: -bs-green" : "-fx-text-fill: -bs-error-red";
+ }
+
+ public String getIssuance() {
+ switch (evaluatedProposal.getProposal().getType()) {
+ case COMPENSATION_REQUEST:
+ Coin requestedBsq = evaluatedProposal.isAccepted() ?
+ ((CompensationProposal) evaluatedProposal.getProposal()).getRequestedBsq() :
+ Coin.ZERO;
+ return bsqFormatter.formatCoinWithCode(requestedBsq);
+ case GENERIC:
+ case CHANGE_PARAM:
+ case REMOVE_ALTCOIN:
+ default:
+ return "";
+ }
+
+ }
+}
diff --git a/src/main/java/bisq/desktop/main/dao/cycles/model/CycleResult.java b/src/main/java/bisq/desktop/main/dao/cycles/model/CycleResult.java
new file mode 100644
index 00000000000..4da420c2318
--- /dev/null
+++ b/src/main/java/bisq/desktop/main/dao/cycles/model/CycleResult.java
@@ -0,0 +1,76 @@
+/*
+ * 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.cycles.model;
+
+import bisq.core.dao.state.period.Cycle;
+import bisq.core.dao.voting.proposal.Proposal;
+import bisq.core.dao.voting.voteresult.EvaluatedProposal;
+import bisq.core.util.BsqFormatter;
+
+import java.util.List;
+
+import lombok.Data;
+
+@Data
+public class CycleResult {
+ private final Cycle cycle;
+ private final int cycleIndex;
+ private final int numVotes, numAcceptedVotes, numRejectedVotes;
+ private final long stakeOfAcceptedVotes;
+ private final long stakeOfRejectedVotes;
+ private BsqFormatter bsqFormatter;
+ private long cycleStartTime;
+
+ // All available proposals of cycle
+ private List proposals;
+
+ // Proposals which ended up in voting
+ private final List evaluatedProposals;
+
+ public CycleResult(Cycle cycle,
+ int cycleIndex,
+ long cycleStartTime,
+ List proposals,
+ List evaluatedProposals) {
+ this.cycle = cycle;
+ this.cycleIndex = cycleIndex;
+ this.cycleStartTime = cycleStartTime;
+ this.proposals = proposals;
+ this.evaluatedProposals = evaluatedProposals;
+
+ numVotes = evaluatedProposals.stream()
+ .mapToInt(e -> e.getProposalVoteResult().getNumActiveVotes())
+ .sum();
+ numAcceptedVotes = evaluatedProposals.stream()
+ .mapToInt(e -> e.getProposalVoteResult().getNumActiveVotes())
+ .sum();
+ numRejectedVotes = evaluatedProposals.stream()
+ .mapToInt(e -> e.getProposalVoteResult().getNumRejectedVotes())
+ .sum();
+ stakeOfAcceptedVotes = evaluatedProposals.stream()
+ .mapToLong(e -> e.getProposalVoteResult().getStakeOfAcceptedVotes())
+ .sum();
+ stakeOfRejectedVotes = evaluatedProposals.stream()
+ .mapToLong(e -> e.getProposalVoteResult().getStakeOfRejectedVotes())
+ .sum();
+ }
+
+ public long getTotalStake() {
+ return stakeOfAcceptedVotes + stakeOfRejectedVotes;
+ }
+}
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 5b94cd124e9..1ebb333e212 100644
--- a/src/main/java/bisq/desktop/main/dao/proposal/ProposalDisplay.java
+++ b/src/main/java/bisq/desktop/main/dao/proposal/ProposalDisplay.java
@@ -274,7 +274,7 @@ public void setEditable(boolean isEditable) {
public void removeAllFields() {
if (gridRow > 0) {
clearForm();
- GUIUtil.removeChildrenFromGridPaneRows(gridPane, gridRowStartIndex, gridRow + 1);
+ GUIUtil.removeChildrenFromGridPaneRows(gridPane, gridRowStartIndex, gridRow);
gridRow = gridRowStartIndex;
}
}
diff --git a/src/main/java/bisq/desktop/main/dao/proposal/ProposalView.java b/src/main/java/bisq/desktop/main/dao/proposal/ProposalView.java
index 9c3a4a67a7e..7d3eab51dbb 100644
--- a/src/main/java/bisq/desktop/main/dao/proposal/ProposalView.java
+++ b/src/main/java/bisq/desktop/main/dao/proposal/ProposalView.java
@@ -31,8 +31,6 @@
import bisq.desktop.main.dao.proposal.closed.ClosedProposalsView;
import bisq.desktop.main.dao.proposal.dashboard.ProposalDashboardView;
import bisq.desktop.main.dao.proposal.make.MakeProposalView;
-import bisq.desktop.main.dao.proposal.myvotes.MyVotesView;
-import bisq.desktop.main.dao.proposal.votes.VotesView;
import bisq.core.locale.Res;
@@ -55,7 +53,7 @@ public class ProposalView extends ActivatableViewAndModel {
private final ViewLoader viewLoader;
private final Navigation navigation;
- private MenuItem dashboard, make, active, myVotes, votes, closed;
+ private MenuItem dashboard, make, active, closed;
private Navigation.Listener listener;
@FXML
@@ -89,13 +87,9 @@ public void initialize() {
MakeProposalView.class, AwesomeIcon.EDIT, baseNavPath);
active = new MenuItem(navigation, toggleGroup, Res.get("dao.proposal.menuItem.active"),
ActiveProposalsView.class, AwesomeIcon.LIST_UL, baseNavPath);
- myVotes = new MenuItem(navigation, toggleGroup, Res.get("dao.proposal.menuItem.myVotes"),
- MyVotesView.class, AwesomeIcon.THUMBS_UP, baseNavPath);
- votes = new MenuItem(navigation, toggleGroup, Res.get("dao.proposal.menuItem.votes"),
- VotesView.class, AwesomeIcon.THUMBS_UP_ALT, baseNavPath);
closed = new MenuItem(navigation, toggleGroup, Res.get("dao.proposal.menuItem.closed"),
ClosedProposalsView.class, AwesomeIcon.LIST_ALT, baseNavPath);
- leftVBox.getChildren().addAll(dashboard, make, active, myVotes, votes, closed);
+ leftVBox.getChildren().addAll(dashboard, make, active, closed);
}
@Override
@@ -103,8 +97,6 @@ protected void activate() {
dashboard.activate();
make.activate();
active.activate();
- myVotes.activate();
- votes.activate();
closed.activate();
navigation.addListener(listener);
@@ -129,8 +121,6 @@ protected void deactivate() {
dashboard.deactivate();
make.deactivate();
active.deactivate();
- myVotes.deactivate();
- votes.deactivate();
closed.deactivate();
}
@@ -141,8 +131,6 @@ private void loadView(Class extends View> viewClass) {
if (view instanceof ProposalDashboardView) dashboard.setSelected(true);
else if (view instanceof MakeProposalView) make.setSelected(true);
else if (view instanceof ActiveProposalsView) active.setSelected(true);
- else if (view instanceof MyVotesView) myVotes.setSelected(true);
- else if (view instanceof VotesView) votes.setSelected(true);
else if (view instanceof ClosedProposalsView) closed.setSelected(true);
}
diff --git a/src/main/java/bisq/desktop/main/dao/proposal/dashboard/ProposalDashboardView.java b/src/main/java/bisq/desktop/main/dao/proposal/dashboard/ProposalDashboardView.java
index 8c75d779670..c8adc885aeb 100644
--- a/src/main/java/bisq/desktop/main/dao/proposal/dashboard/ProposalDashboardView.java
+++ b/src/main/java/bisq/desktop/main/dao/proposal/dashboard/ProposalDashboardView.java
@@ -98,20 +98,6 @@ public void initialize() {
addMultilineLabel(root, gridRow, Res.get("dao.cycle.info.details"), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
}
-
- private SeparatedPhaseBars createSeparatedPhaseBars() {
- phaseBarsItems = Arrays.asList(
- new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.PROPOSAL, true),
- new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BREAK1, false),
- new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BLIND_VOTE, true),
- new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BREAK2, false),
- new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.VOTE_REVEAL, true),
- new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BREAK3, false),
- new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.RESULT, false),
- new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BREAK4, false));
- return new SeparatedPhaseBars(phaseBarsItems);
- }
-
@Override
protected void activate() {
super.activate();
@@ -172,6 +158,19 @@ public void onChainHeightChanged(int height) {
voteResultTextField.setText(getPhaseDuration(height, DaoPhase.Phase.RESULT));
}
+ private SeparatedPhaseBars createSeparatedPhaseBars() {
+ phaseBarsItems = Arrays.asList(
+ new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.PROPOSAL, true),
+ new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BREAK1, false),
+ new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BLIND_VOTE, true),
+ new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BREAK2, false),
+ new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.VOTE_REVEAL, true),
+ new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BREAK3, false),
+ new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.RESULT, false),
+ new SeparatedPhaseBars.SeparatedPhaseBarsItem(DaoPhase.Phase.BREAK4, false));
+ return new SeparatedPhaseBars(phaseBarsItems);
+ }
+
private String getPhaseDuration(int height, DaoPhase.Phase phase) {
final long start = daoFacade.getFirstBlockOfPhase(height, phase);
final long end = daoFacade.getLastBlockOfPhase(height, phase);
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 ea17468e479..85a16a731f1 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
@@ -117,8 +117,8 @@ public void initialize() {
Res.getWithCol("dao.proposal.create.proposalType"), Layout.FIRST_ROW_DISTANCE).second;
proposalTypeComboBox.setConverter(new StringConverter() {
@Override
- public String toString(ProposalType object) {
- return Res.get(object.name());
+ public String toString(ProposalType proposalType) {
+ return Res.get("dao.proposal.type." + proposalType.name());
}
@Override
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
deleted file mode 100644
index def2bbaac1b..00000000000
--- a/src/main/java/bisq/desktop/main/dao/proposal/myvotes/MyVotesView.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * 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.proposal.myvotes;
-
-import bisq.desktop.common.view.ActivatableView;
-import bisq.desktop.common.view.FxmlView;
-
-import bisq.core.btc.wallet.BsqWalletService;
-import bisq.core.dao.DaoFacade;
-import bisq.core.user.Preferences;
-import bisq.core.util.BSFormatter;
-import bisq.core.util.BsqFormatter;
-
-import javax.inject.Inject;
-
-import javafx.scene.layout.GridPane;
-
-@FxmlView
-public class MyVotesView extends ActivatableView /*extends BaseProposalView*/ {
- private final Preferences preferences;
- /*
-
- private final ObservableList voteListItems = FXCollections.observableArrayList();
- private SortedList sortedList = new SortedList<>(voteListItems);
- private TableView votesTableView;
- private VoteListItem selectedVoteListItem;
- private Subscription selectedVoteSubscription;
-*/
-
- ///////////////////////////////////////////////////////////////////////////////////////////
- // Constructor, lifecycle
- ///////////////////////////////////////////////////////////////////////////////////////////
-
- @Inject
- private MyVotesView(DaoFacade daoFacade,
- BsqWalletService bsqWalletService,
- BsqFormatter bsqFormatter,
- BSFormatter btcFormatter,
- Preferences preferences) {
-
- // super(daoFacade, bsqWalletService, bsqFormatter, btcFormatter);
- this.preferences = preferences;
- }
-
- /* @Override
- public void initialize() {
- super.initialize();
-
- createVoteTableView();
- createProposalsTableView(Res.get("dao.proposal.myVotes.proposals.header"), Layout.GROUP_DISTANCE - 20);
- createProposalDisplay();
-
- changeProposalViewItemsVisibility(false);
- }
-
- @Override
- protected void activate() {
- super.activate();
-
- selectedVoteSubscription = EasyBind.subscribe(votesTableView.getSelectionModel().selectedItemProperty(),
- this::onSelectVote);
-
- sortedList.comparatorProperty().bind(votesTableView.comparatorProperty());
-
- voteListItems.clear();
- List items = daoFacade.getMyVoteList().stream()
- .map(vote -> new VoteListItem(vote, daoFacade, bsqWalletService, bsqFormatter))
- .collect(Collectors.toList());
- voteListItems.addAll(items);
- }
-
- private void onSelectVote(VoteListItem voteListItem) {
- selectedVoteListItem = voteListItem;
- changeProposalViewItemsVisibility(selectedVoteListItem != null);
- updateListItems();
- }
-
- @Override
- protected void deactivate() {
- super.deactivate();
-
- selectedVoteSubscription.unsubscribe();
-
- changeProposalViewItemsVisibility(false);
- votesTableView.getSelectionModel().clearSelection();
- }
-
-
- ///////////////////////////////////////////////////////////////////////////////////////////
- // Create views
- ///////////////////////////////////////////////////////////////////////////////////////////
-
- private void createVoteTableView() {
- TableGroupHeadline proposalsHeadline = new TableGroupHeadline(Res.get("dao.proposal.myVotes.header"));
- GridPane.setRowIndex(proposalsHeadline, ++gridRow);
- GridPane.setMargin(proposalsHeadline, new Insets(-10, -10, -10, -10));
- GridPane.setColumnSpan(proposalsHeadline, 2);
- root.getChildren().add(proposalsHeadline);
-
- votesTableView = new TableView<>();
- votesTableView.setPlaceholder(new AutoTooltipLabel(Res.get("table.placeholder.noData")));
- votesTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
-
- createVoteColumns(votesTableView);
- GridPane.setRowIndex(votesTableView, gridRow);
- GridPane.setMargin(votesTableView, new Insets(10, -10, 5, -10));
- GridPane.setColumnSpan(votesTableView, 2);
- GridPane.setHgrow(votesTableView, Priority.ALWAYS);
- root.getChildren().add(votesTableView);
-
- votesTableView.setItems(sortedList);
- }
-
-
- ///////////////////////////////////////////////////////////////////////////////////////////
- // Handler
- ///////////////////////////////////////////////////////////////////////////////////////////
-
- private void onShowProposalList(BallotList ballotList) {
-
- }
-
-
- ///////////////////////////////////////////////////////////////////////////////////////////
- // Protected
- ///////////////////////////////////////////////////////////////////////////////////////////
-
- *//* @Override
- protected void updateProposalList() {
- //TODO
- //if (selectedVoteListItem != null)
- // doUpdateProposalList(selectedVoteListItem.getMyVote().getBallotList().getList());
- }*//*
- @Override
- protected List getProposalList() {
- //TODO
- return null;// daoFacade.getActiveOrMyUnconfirmedProposals();
- }
-
- @Override
- protected BaseProposalListItem getListItem(Proposal proposal) {
- return new ActiveProposalListItem(proposal, daoFacade, bsqWalletService, bsqFormatter);
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////
- // TableColumns
- ///////////////////////////////////////////////////////////////////////////////////////////
-
- private void createVoteColumns(TableView tableView) {
- TableColumn dateColumn = new AutoTooltipTableColumn(Res.get("shared.dateTime")) {
- {
- setMinWidth(190);
- setMaxWidth(190);
- }
- };
- dateColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
- dateColumn.setCellFactory(
- new Callback, TableCell>() {
- @Override
- public TableCell call(
- TableColumn column) {
- return new TableCell() {
- @Override
- public void updateItem(final VoteListItem item, boolean empty) {
- super.updateItem(item, empty);
- if (item != null)
- setText(bsqFormatter.formatDateTime(new Date(item.getMyVote()
- .getDate())));
- else
- setText("");
- }
- };
- }
- });
- dateColumn.setComparator(Comparator.comparing(o3 -> o3.getMyVote().getDate()));
- dateColumn.setSortType(TableColumn.SortType.DESCENDING);
- tableView.getColumns().add(dateColumn);
- tableView.getSortOrder().add(dateColumn);
-
-
- TableColumn proposalListColumn = new AutoTooltipTableColumn<>(Res.get("dao.proposal.myVotes.proposalList"));
- proposalListColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
- proposalListColumn.setCellFactory(
- new Callback, TableCell>() {
- @Override
- public TableCell call(
- TableColumn column) {
- return new TableCell() {
- @Override
- public void updateItem(final VoteListItem item, boolean empty) {
- super.updateItem(item, empty);
-
- if (item != null && !empty) {
- BallotList ballotList = item.getMyVote().getBallotList();
- HyperlinkWithIcon field = new HyperlinkWithIcon(Res.get("dao.proposal.myVotes.showProposalList"), AwesomeIcon.INFO_SIGN);
- field.setOnAction(event -> onShowProposalList(ballotList));
- field.setTooltip(new Tooltip(Res.get("dao.proposal.myVotes.tooltip.showProposalList")));
- setGraphic(field);
- } else {
- setGraphic(null);
- }
- }
- };
- }
- });
- proposalListColumn.setSortable(false);
- tableView.getColumns().add(proposalListColumn);
-
- TableColumn stakeColumn = new AutoTooltipTableColumn<>(Res.get("dao.proposal.votes.stake"));
- stakeColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
- stakeColumn.setCellFactory(
- new Callback, TableCell>() {
- @Override
- public TableCell call(
- TableColumn column) {
- return new TableCell() {
- @Override
- public void updateItem(final VoteListItem item, boolean empty) {
- super.updateItem(item, empty);
- if (item != null && !empty) {
- textProperty().bind(item.getStakeAsStringProperty());
- } else {
- textProperty().unbind();
- setText("");
- }
- }
- };
- }
- });
- stakeColumn.setComparator(Comparator.comparing(VoteListItem::getStake));
- tableView.getColumns().add(stakeColumn);
-
- TableColumn txColumn = new AutoTooltipTableColumn<>(Res.get("dao.proposal.myVotes.tx"));
- txColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
- txColumn.setCellFactory(
- new Callback, TableCell>() {
- @Override
- public TableCell call(
- TableColumn column) {
- return new TableCell() {
- @Override
- public void updateItem(final VoteListItem item, boolean empty) {
- super.updateItem(item, empty);
-
- if (item != null && !empty) {
- String txId = item.getMyVote().getTxId();
- HyperlinkWithIcon hyperlinkWithIcon = new HyperlinkWithIcon(txId, AwesomeIcon.EXTERNAL_LINK);
- hyperlinkWithIcon.setOnAction(event -> {
- if (txId != null)
- GUIUtil.openWebPage(preferences.getBlockChainExplorer().txUrl + txId);
- });
- hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", txId)));
- setGraphic(hyperlinkWithIcon);
- } else {
- setGraphic(null);
- }
- }
- };
- }
- });
- txColumn.setComparator(Comparator.comparing(o2 -> o2.getMyVote().getBlindVote().getTxId()));
- tableView.getColumns().add(txColumn);
-
- TableColumn confidenceColumn = new TableColumn<>(Res.get("shared.confirmations"));
- confidenceColumn.setMinWidth(130);
- confidenceColumn.setMaxWidth(confidenceColumn.getMinWidth());
-
- confidenceColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
-
- confidenceColumn.setCellFactory(new Callback,
- TableCell>() {
-
- @Override
- public TableCell call(TableColumn column) {
- return new TableCell() {
-
- @Override
- public void updateItem(final VoteListItem item, boolean empty) {
- super.updateItem(item, empty);
-
- if (item != null && !empty) {
- setGraphic(item.getTxConfidenceIndicator());
- } else {
- setGraphic(null);
- }
- }
- };
- }
- });
- confidenceColumn.setComparator(Comparator.comparing(VoteListItem::getConfirmations));
- tableView.getColumns().add(confidenceColumn);
- }
-
-
- @Override
- protected void createProposalColumns(TableView tableView) {
- super.createProposalColumns(tableView);
-
- TableColumn actionColumn = new TableColumn<>(Res.get("dao.proposal.votes.header"));
- actionColumn.setMinWidth(50);
- actionColumn.setMaxWidth(actionColumn.getMinWidth());
-
- actionColumn.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue()));
-
- actionColumn.setCellFactory(new Callback,
- TableCell>() {
-
- @Override
- public TableCell call(TableColumn column) {
- return new TableCell() {
- ImageView actionButtonIconView;
-
- @Override
- public void updateItem(final BaseProposalListItem item, boolean empty) {
- super.updateItem(item, empty);
-
- if (item != null && !empty) {
- actionButtonIconView = new ImageView();
- //TODO
- *//* Vote vote = item.getProposal().getVote();
- if (vote instanceof BooleanVote) {
- if (((BooleanVote) vote).isAccepted()) {
- actionButtonIconView.setId("accepted");
- } else {
- actionButtonIconView.setId("rejected");
- }
- } else {
- //TODO
- }*//*
-
- setGraphic(actionButtonIconView);
- } else {
- setGraphic(null);
- }
- }
- };
- }
- });
- actionColumn.setComparator(Comparator.comparing(BaseProposalListItem::getConfirmations));
- tableView.getColumns().add(actionColumn);
- }*/
-}
diff --git a/src/main/java/bisq/desktop/main/dao/proposal/myvotes/VoteListItem.java b/src/main/java/bisq/desktop/main/dao/proposal/myvotes/VoteListItem.java
deleted file mode 100644
index 78486006e64..00000000000
--- a/src/main/java/bisq/desktop/main/dao/proposal/myvotes/VoteListItem.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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.proposal.myvotes;
-
-import bisq.desktop.components.indicator.TxConfidenceIndicator;
-
-import bisq.core.btc.listeners.TxConfidenceListener;
-import bisq.core.btc.wallet.BsqWalletService;
-import bisq.core.dao.DaoFacade;
-import bisq.core.dao.state.BlockListener;
-import bisq.core.dao.state.blockchain.Block;
-import bisq.core.dao.state.blockchain.Tx;
-import bisq.core.dao.state.blockchain.TxOutput;
-import bisq.core.dao.voting.myvote.MyVote;
-import bisq.core.locale.Res;
-import bisq.core.util.BsqFormatter;
-
-import org.bitcoinj.core.Coin;
-import org.bitcoinj.core.Transaction;
-import org.bitcoinj.core.TransactionConfidence;
-
-import javafx.scene.control.Tooltip;
-
-import javafx.beans.property.SimpleStringProperty;
-import javafx.beans.property.StringProperty;
-import javafx.beans.value.ChangeListener;
-
-import java.util.Optional;
-
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-import lombok.extern.slf4j.Slf4j;
-
-@ToString
-@Slf4j
-@EqualsAndHashCode
-public class VoteListItem implements BlockListener {
- @Getter
- private final MyVote myVote;
- private final DaoFacade daoFacade;
- private final BsqWalletService bsqWalletService;
- private final BsqFormatter bsqFormatter;
- private final ChangeListener chainHeightListener;
- @Getter
- private TxConfidenceIndicator txConfidenceIndicator;
- @Getter
- private Integer confirmations = 0;
-
- private TxConfidenceListener txConfidenceListener;
- private Tooltip tooltip = new Tooltip(Res.get("confidence.unknown"));
- private Transaction walletTransaction;
- @Setter
- private Runnable onRemoveHandler;
- @Getter
- private long stake = 0;
- @Getter
- private StringProperty stakeAsStringProperty = new SimpleStringProperty("");
-
- VoteListItem(MyVote myVote,
- DaoFacade daoFacade,
- BsqWalletService bsqWalletService,
- BsqFormatter bsqFormatter) {
- this.myVote = myVote;
- this.daoFacade = daoFacade;
- this.bsqWalletService = bsqWalletService;
- this.bsqFormatter = bsqFormatter;
-
- txConfidenceIndicator = new TxConfidenceIndicator();
- txConfidenceIndicator.setId("funds-confidence");
-
- txConfidenceIndicator.setProgress(-1);
- txConfidenceIndicator.setPrefSize(24, 24);
- txConfidenceIndicator.setTooltip(tooltip);
- daoFacade.addBlockListener(this);
-
- chainHeightListener = (observable, oldValue, newValue) -> setupConfidence();
- bsqWalletService.getChainHeightProperty().addListener(chainHeightListener);
- setupConfidence();
- calculateStake();
- }
-
-
- ///////////////////////////////////////////////////////////////////////////////////////////
- // Listener
- ///////////////////////////////////////////////////////////////////////////////////////////
-
- @Override
- public void onBlockAdded(Block block) {
- //TODO do we want that here???
- setupConfidence();
- }
-
- private void setupConfidence() {
- calculateStake();
- final String txId = myVote.getBlindVote().getTxId();
- Optional optionalTx = daoFacade.getTx(txId);
- if (optionalTx.isPresent()) {
- Tx tx = optionalTx.get();
- // We cache the walletTransaction once found
- if (walletTransaction == null) {
- final Optional transactionOptional = bsqWalletService.isWalletTransaction(txId);
- transactionOptional.ifPresent(transaction -> walletTransaction = transaction);
- }
-
- if (walletTransaction != null) {
- // It is our tx so we get confidence updates
- if (txConfidenceListener == null) {
- txConfidenceListener = new TxConfidenceListener(txId) {
- @Override
- public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
- updateConfidence(confidence.getConfidenceType(), confidence.getDepthInBlocks(), confidence.numBroadcastPeers());
- }
- };
- bsqWalletService.addTxConfidenceListener(txConfidenceListener);
- }
- } else {
- // tx from other users, we dont get confidence updates but as we have the bsq tx we can calculate it
- // we get setupConfidence called at each new block from above listener so no need to register a new listener
- int depth = bsqWalletService.getChainHeightProperty().get() - tx.getBlockHeight() + 1;
- if (depth > 0)
- updateConfidence(TransactionConfidence.ConfidenceType.BUILDING, depth, -1);
- }
-
- final TransactionConfidence confidence = bsqWalletService.getConfidenceForTxId(txId);
- if (confidence != null)
- updateConfidence(confidence, confidence.getDepthInBlocks());
- }
- }
-
- private void calculateStake() {
- if (stake == 0) {
- String txId = myVote.getTxId();
- stake = daoFacade.getUnspentBlindVoteStakeTxOutputs().stream()
- .filter(txOutput -> txOutput.getTxId().equals(txId))
- .filter(txOutput -> txOutput.getIndex() == 0)
- .mapToLong(TxOutput::getValue)
- .sum();
- stakeAsStringProperty.set(bsqFormatter.formatCoin(Coin.valueOf(stake)));
- }
- }
-
- private void updateConfidence(TransactionConfidence confidence, int depthInBlocks) {
- if (confidence != null) {
- updateConfidence(confidence.getConfidenceType(), confidence.getDepthInBlocks(), confidence.numBroadcastPeers());
- confirmations = depthInBlocks;
- }
- }
-
- public void cleanup() {
- bsqWalletService.getChainHeightProperty().removeListener(chainHeightListener);
- daoFacade.removeBlockListener(this);
- if (txConfidenceListener != null)
- bsqWalletService.removeTxConfidenceListener(txConfidenceListener);
- }
-
- private void updateConfidence(TransactionConfidence.ConfidenceType confidenceType, int depthInBlocks, int numBroadcastPeers) {
- switch (confidenceType) {
- case UNKNOWN:
- tooltip.setText(Res.get("confidence.unknown"));
- txConfidenceIndicator.setProgress(0);
- break;
- case PENDING:
- tooltip.setText(Res.get("confidence.seen", numBroadcastPeers > -1 ? numBroadcastPeers : Res.get("shared.na")));
- txConfidenceIndicator.setProgress(-1.0);
- break;
- case BUILDING:
- tooltip.setText(Res.get("confidence.confirmed", depthInBlocks));
- txConfidenceIndicator.setProgress(Math.min(1, (double) depthInBlocks / 6.0));
- break;
- case DEAD:
- tooltip.setText(Res.get("confidence.invalid"));
- txConfidenceIndicator.setProgress(0);
- break;
- }
-
- txConfidenceIndicator.setPrefSize(24, 24);
- }
-}
-
diff --git a/src/main/java/bisq/desktop/main/dao/proposal/votes/VotesView.fxml b/src/main/java/bisq/desktop/main/dao/proposal/votes/VotesView.fxml
deleted file mode 100644
index add21b35de1..00000000000
--- a/src/main/java/bisq/desktop/main/dao/proposal/votes/VotesView.fxml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/java/bisq/desktop/main/dao/proposal/votes/VotesView.java b/src/main/java/bisq/desktop/main/dao/proposal/votes/VotesView.java
deleted file mode 100644
index 3aa50c234fb..00000000000
--- a/src/main/java/bisq/desktop/main/dao/proposal/votes/VotesView.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.proposal.votes;
-
-import bisq.desktop.common.view.ActivatableView;
-import bisq.desktop.common.view.FxmlView;
-
-import javax.inject.Inject;
-
-import javafx.scene.layout.GridPane;
-
-@FxmlView
-public class VotesView extends ActivatableView {
-
-
- ///////////////////////////////////////////////////////////////////////////////////////////
- // Constructor, lifecycle
- ///////////////////////////////////////////////////////////////////////////////////////////
-
- @Inject
- private VotesView() {
- }
-
- @Override
- public void initialize() {
- }
-
- @Override
- protected void activate() {
- }
-
- @Override
- protected void deactivate() {
- }
-}
diff --git a/src/main/java/bisq/desktop/util/GUIUtil.java b/src/main/java/bisq/desktop/util/GUIUtil.java
index a040730c634..e4fd0bfe09d 100644
--- a/src/main/java/bisq/desktop/util/GUIUtil.java
+++ b/src/main/java/bisq/desktop/util/GUIUtil.java
@@ -96,6 +96,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@@ -541,8 +542,14 @@ public static void removeChildrenFromGridPaneRows(GridPane gridPane, int start,
childByRowMap.get(rowIndex).add(child);
});
- for (int i = Math.min(start, childByRowMap.size()); i < Math.min(end, childByRowMap.size()); i++) {
- childByRowMap.get(i).forEach(child -> gridPane.getChildren().remove(child));
+ for (int i = Math.min(start, childByRowMap.size()); i < Math.min(end + 1, childByRowMap.size()); i++) {
+ List nodes = childByRowMap.get(i);
+ if (nodes != null) {
+ nodes.stream()
+ .filter(Objects::nonNull)
+ .filter(node -> gridPane.getChildren().contains(node))
+ .forEach(node -> gridPane.getChildren().remove(node));
+ }
}
}
diff --git a/src/test/java/bisq/desktop/AwesomeFontDemo.java b/src/test/java/bisq/desktop/AwesomeFontDemo.java
index 30cbf688e14..3f80353e528 100644
--- a/src/test/java/bisq/desktop/AwesomeFontDemo.java
+++ b/src/test/java/bisq/desktop/AwesomeFontDemo.java
@@ -59,7 +59,7 @@ public void start(Stage primaryStage) {
root.getChildren().add(button);
}
- primaryStage.setScene(new Scene(root, 1200, 950));
+ primaryStage.setScene(new Scene(root, 1400, 1200));
primaryStage.show();
}
}