From 9f01e9e4e020f3512f6268be9528846514f024fd Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 15 Jul 2018 10:52:40 +0200 Subject: [PATCH] Add voteResult view --- src/main/java/bisq/desktop/bisq.css | 8 + .../java/bisq/desktop/main/dao/DaoView.fxml | 3 - .../java/bisq/desktop/main/dao/DaoView.java | 22 +- .../main/dao/cycles/CyclesListItem.java | 73 +++ .../CyclesView.fxml} | 14 +- .../desktop/main/dao/cycles/CyclesView.java | 374 ++++++++++++++++ .../dao/cycles/cycle/CycleDetailsWindow.java | 88 ++++ .../main/dao/cycles/cycle/CycleDisplay.java | 419 ++++++++++++++++++ .../main/dao/cycles/cycle/CycleListItem.java | 99 +++++ .../main/dao/cycles/model/CycleResult.java | 76 ++++ .../main/dao/proposal/ProposalDisplay.java | 2 +- .../main/dao/proposal/ProposalView.java | 16 +- .../dashboard/ProposalDashboardView.java | 27 +- .../dao/proposal/make/MakeProposalView.java | 4 +- .../dao/proposal/myvotes/MyVotesView.java | 363 --------------- .../dao/proposal/myvotes/VoteListItem.java | 196 -------- .../main/dao/proposal/votes/VotesView.fxml | 33 -- .../main/dao/proposal/votes/VotesView.java | 50 --- src/main/java/bisq/desktop/util/GUIUtil.java | 11 +- .../java/bisq/desktop/AwesomeFontDemo.java | 2 +- 20 files changed, 1192 insertions(+), 688 deletions(-) create mode 100644 src/main/java/bisq/desktop/main/dao/cycles/CyclesListItem.java rename src/main/java/bisq/desktop/main/dao/{proposal/myvotes/MyVotesView.fxml => cycles/CyclesView.fxml} (76%) create mode 100644 src/main/java/bisq/desktop/main/dao/cycles/CyclesView.java create mode 100644 src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleDetailsWindow.java create mode 100644 src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleDisplay.java create mode 100644 src/main/java/bisq/desktop/main/dao/cycles/cycle/CycleListItem.java create mode 100644 src/main/java/bisq/desktop/main/dao/cycles/model/CycleResult.java delete mode 100644 src/main/java/bisq/desktop/main/dao/proposal/myvotes/MyVotesView.java delete mode 100644 src/main/java/bisq/desktop/main/dao/proposal/myvotes/VoteListItem.java delete mode 100644 src/main/java/bisq/desktop/main/dao/proposal/votes/VotesView.fxml delete mode 100644 src/main/java/bisq/desktop/main/dao/proposal/votes/VotesView.java 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 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 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(); } }