Add supply and transactions sections

This commit is contained in:
Christoph Atteneder 2019-03-27 12:19:28 +01:00
parent b00f96d06e
commit 44764d1fb7
No known key found for this signature in database
GPG Key ID: CD5DC1C529CDFD3B
12 changed files with 773 additions and 262 deletions

View File

@ -421,6 +421,10 @@ public class BSFormatter {
} }
} }
public String formatPrice(Price price, boolean appendCurrencyCode) {
return formatPrice(price, fiatPriceFormat, true);
}
public String formatPrice(Price price) { public String formatPrice(Price price) {
return formatPrice(price, fiatPriceFormat, false); return formatPrice(price, fiatPriceFormat, false);
} }

View File

@ -124,6 +124,10 @@ public class BsqFormatter extends BSFormatter {
return super.formatCoin(satoshi, coinFormat); return super.formatCoin(satoshi, coinFormat);
} }
public String formatBSQSatoshisWithCode(long satoshi) {
return super.formatCoinWithCode(satoshi, coinFormat);
}
public String formatBTCSatoshis(long satoshi) { public String formatBTCSatoshis(long satoshi) {
return super.formatCoin(satoshi, btcCoinFormat); return super.formatCoin(satoshi, btcCoinFormat);
} }

View File

@ -198,6 +198,7 @@ shared.actions=Actions
shared.buyerUpperCase=Buyer shared.buyerUpperCase=Buyer
shared.sellerUpperCase=Seller shared.sellerUpperCase=Seller
shared.new=NEW shared.new=NEW
shared.new=NEW
#################################################################### ####################################################################
# UI views # UI views
@ -1756,29 +1757,6 @@ dao.wallet.menuItem.receive=Receive
dao.wallet.menuItem.transactions=Transactions dao.wallet.menuItem.transactions=Transactions
dao.wallet.dashboard.myBalance=My wallet balance dao.wallet.dashboard.myBalance=My wallet balance
dao.wallet.dashboard.distribution=Distribution of all BSQ
dao.wallet.dashboard.locked=Global state of locked BSQ
dao.wallet.dashboard.market=Market data
dao.wallet.dashboard.genesis=Genesis transaction
dao.wallet.dashboard.txDetails=BSQ transactions statistics
dao.wallet.dashboard.genesisBlockHeight=Genesis block height
dao.wallet.dashboard.genesisTxId=Genesis transaction ID
dao.wallet.dashboard.genesisIssueAmount=BSQ issued at genesis transaction
dao.wallet.dashboard.compRequestIssueAmount=BSQ issued for compensation requests
dao.wallet.dashboard.reimbursementAmount=BSQ issued for reimbursement requests
dao.wallet.dashboard.availableAmount=Total available BSQ
dao.wallet.dashboard.burntAmount=Burned BSQ (fees)
dao.wallet.dashboard.totalLockedUpAmount=Locked up in bonds
dao.wallet.dashboard.totalUnlockingAmount=Unlocking BSQ from bonds
dao.wallet.dashboard.totalUnlockedAmount=Unlocked BSQ from bonds
dao.wallet.dashboard.totalConfiscatedAmount=Confiscated BSQ from bonds
dao.wallet.dashboard.allTx=No. of all BSQ transactions
dao.wallet.dashboard.utxo=No. of all unspent transaction outputs
dao.wallet.dashboard.compensationIssuanceTx=No. of all compensation request issuance transactions
dao.wallet.dashboard.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions
dao.wallet.dashboard.burntTx=No. of all fee payments transactions
dao.wallet.dashboard.price=Latest BSQ/BTC trade price (in Bisq)
dao.wallet.dashboard.marketCap=Market capitalisation (based on trade price)
dao.wallet.receive.fundYourWallet=Your BSQ receive address dao.wallet.receive.fundYourWallet=Your BSQ receive address
dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address) dao.wallet.receive.bsqAddress=BSQ wallet address (Fresh unused address)
@ -1940,6 +1918,37 @@ dao.monitor.blindVote.table.hash=Hash of blind vote state
dao.monitor.blindVote.table.prev=Previous hash dao.monitor.blindVote.table.prev=Previous hash
dao.monitor.blindVote.table.numBlindVotes=No. blind votes dao.monitor.blindVote.table.numBlindVotes=No. blind votes
dao.factsAndFigures.menuItem.supply=Supply
dao.factsAndFigures.menuItem.transactions=Transactions
dao.factsAndFigures.dashboard.marketPrice=Market data
dao.factsAndFigures.dashboard.price=Latest BSQ/BTC trade price (in Bisq)
dao.factsAndFigures.dashboard.marketCap=Market capitalisation (based on trade price)
dao.factsAndFigures.dashboard.availableAmount=Total available BSQ
dao.factsAndFigures.supply.issued=BSQ issued
dao.factsAndFigures.supply.genesisIssueAmount=BSQ issued at genesis transaction
dao.factsAndFigures.supply.compRequestIssueAmount=BSQ issued for compensation requests
dao.factsAndFigures.supply.reimbursementAmount=BSQ issued for reimbursement requests
dao.factsAndFigures.supply.burnt=BSQ burnt
dao.factsAndFigures.supply.locked=Global state of locked BSQ
dao.factsAndFigures.supply.totalLockedUpAmount=Locked up in bonds
dao.factsAndFigures.supply.totalUnlockingAmount=Unlocking BSQ from bonds
dao.factsAndFigures.supply.totalUnlockedAmount=Unlocked BSQ from bonds
dao.factsAndFigures.supply.totalConfiscatedAmount=Confiscated BSQ from bonds
dao.factsAndFigures.supply.burntAmount=Burned BSQ (fees)
dao.factsAndFigures.transactions.genesis=Genesis transaction
dao.factsAndFigures.transactions.genesisBlockHeight=Genesis block height
dao.factsAndFigures.transactions.genesisTxId=Genesis transaction ID
dao.factsAndFigures.transactions.txDetails=BSQ transactions statistics
dao.factsAndFigures.transactions.allTx=No. of all BSQ transactions
dao.factsAndFigures.transactions.utxo=No. of all unspent transaction outputs
dao.factsAndFigures.transactions.compensationIssuanceTx=No. of all compensation request issuance transactions
dao.factsAndFigures.transactions.reimbursementIssuanceTx=No. of all reimbursement request issuance transactions
dao.factsAndFigures.transactions.burntTx=No. of all fee payments transactions
#################################################################### ####################################################################
# Windows # Windows

View File

@ -9,7 +9,7 @@
* Bisq is distributed in the hope that it will be useful, but WITHOUT * Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details. * License for more supply.
* *
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>. * along with Bisq. If not, see <http://www.gnu.org/licenses/>.
@ -28,7 +28,8 @@ import bisq.desktop.components.MenuItem;
import bisq.desktop.main.MainView; import bisq.desktop.main.MainView;
import bisq.desktop.main.dao.DaoView; import bisq.desktop.main.dao.DaoView;
import bisq.desktop.main.dao.economy.dashboard.BsqDashboardView; import bisq.desktop.main.dao.economy.dashboard.BsqDashboardView;
import bisq.desktop.main.dao.economy.details.DetailsView; import bisq.desktop.main.dao.economy.supply.SupplyView;
import bisq.desktop.main.dao.economy.transactions.BSQTransactionsView;
import bisq.core.locale.Res; import bisq.core.locale.Res;
@ -51,7 +52,7 @@ public class EconomyView extends ActivatableViewAndModel {
private final ViewLoader viewLoader; private final ViewLoader viewLoader;
private final Navigation navigation; private final Navigation navigation;
private MenuItem dashboard, details; private MenuItem dashboard, supply, transactions;
private Navigation.Listener listener; private Navigation.Listener listener;
@FXML @FXML
@ -81,20 +82,24 @@ public class EconomyView extends ActivatableViewAndModel {
toggleGroup = new ToggleGroup(); toggleGroup = new ToggleGroup();
List<Class<? extends View>> baseNavPath = Arrays.asList(MainView.class, DaoView.class, EconomyView.class); List<Class<? extends View>> baseNavPath = Arrays.asList(MainView.class, DaoView.class, EconomyView.class);
dashboard = new MenuItem(navigation, toggleGroup, Res.get("shared.dashboard"), BsqDashboardView.class, baseNavPath); dashboard = new MenuItem(navigation, toggleGroup, Res.get("shared.dashboard"), BsqDashboardView.class, baseNavPath);
details = new MenuItem(navigation, toggleGroup, Res.get("shared.details"), DetailsView.class, baseNavPath); supply = new MenuItem(navigation, toggleGroup, Res.get("dao.factsAndFigures.menuItem.supply"), SupplyView.class, baseNavPath);
leftVBox.getChildren().addAll(dashboard, details); transactions = new MenuItem(navigation, toggleGroup, Res.get("dao.factsAndFigures.menuItem.transactions"), BSQTransactionsView.class, baseNavPath);
leftVBox.getChildren().addAll(dashboard, supply, transactions);
// TODO just until DAO is enabled // TODO just until DAO is enabled
if (!DevEnv.isDaoActivated()) { if (!DevEnv.isDaoActivated()) {
dashboard.setDisable(true); dashboard.setDisable(true);
details.setDisable(true); supply.setDisable(true);
transactions.setDisable(true);
} }
} }
@Override @Override
protected void activate() { protected void activate() {
dashboard.activate(); dashboard.activate();
details.activate(); supply.activate();
transactions.activate();
navigation.addListener(listener); navigation.addListener(listener);
ViewPath viewPath = navigation.getCurrentPath(); ViewPath viewPath = navigation.getCurrentPath();
@ -116,6 +121,8 @@ public class EconomyView extends ActivatableViewAndModel {
navigation.removeListener(listener); navigation.removeListener(listener);
dashboard.deactivate(); dashboard.deactivate();
supply.deactivate();
transactions.deactivate();
} }
private void loadView(Class<? extends View> viewClass) { private void loadView(Class<? extends View> viewClass) {
@ -123,6 +130,7 @@ public class EconomyView extends ActivatableViewAndModel {
content.getChildren().setAll(view.getRoot()); content.getChildren().setAll(view.getRoot());
if (view instanceof BsqDashboardView) toggleGroup.selectToggle(dashboard); if (view instanceof BsqDashboardView) toggleGroup.selectToggle(dashboard);
else if (view instanceof DetailsView) toggleGroup.selectToggle(details); else if (view instanceof SupplyView) toggleGroup.selectToggle(supply);
else if (view instanceof BSQTransactionsView) toggleGroup.selectToggle(transactions);
} }
} }

View File

@ -27,7 +27,6 @@
xmlns:fx="http://javafx.com/fxml"> xmlns:fx="http://javafx.com/fxml">
<columnConstraints> <columnConstraints>
<ColumnConstraints percentWidth="50"/> <ColumnConstraints percentWidth="50"/>
<ColumnConstraints minWidth="10" maxWidth="5"/>
<ColumnConstraints percentWidth="50"/> <ColumnConstraints percentWidth="50"/>
</columnConstraints> </columnConstraints>
</GridPane> </GridPane>

View File

@ -19,30 +19,94 @@ package bisq.desktop.main.dao.economy.dashboard;
import bisq.desktop.common.view.ActivatableView; import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView; import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.util.FormBuilder;
import bisq.core.dao.DaoFacade; import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.DaoStateListener; import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Block; import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.governance.IssuanceType;
import bisq.core.locale.Res;
import bisq.core.monetary.Price;
import bisq.core.provider.price.PriceFeedService; import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.TradeStatistics2;
import bisq.core.trade.statistics.TradeStatisticsManager;
import bisq.core.user.Preferences; import bisq.core.user.Preferences;
import bisq.core.util.BSFormatter;
import bisq.core.util.BsqFormatter; import bisq.core.util.BsqFormatter;
import bisq.common.util.Tuple3;
import org.bitcoinj.core.Coin;
import javax.inject.Inject; import javax.inject.Inject;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane; import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.geometry.Insets;
import javafx.geometry.Side;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.util.StringConverter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;
import static bisq.desktop.util.Layout.FIRST_ROW_DISTANCE;
import java.sql.Date;
@FxmlView @FxmlView
public class BsqDashboardView extends ActivatableView<GridPane, Void> implements DaoStateListener { public class BsqDashboardView extends ActivatableView<GridPane, Void> implements DaoStateListener {
private static final String DAY = "day";
private static final Map<String, TemporalAdjuster> ADJUSTERS = new HashMap<>();
private final DaoFacade daoFacade; private final DaoFacade daoFacade;
private final TradeStatisticsManager tradeStatisticsManager;
private final PriceFeedService priceFeedService; private final PriceFeedService priceFeedService;
private final DaoStateService daoStateService;
private final Preferences preferences; private final Preferences preferences;
private final BsqFormatter bsqFormatter; private final BsqFormatter bsqFormatter;
private final BSFormatter btcFormatter;
private ChangeListener<Number> priceChangeListener; private ChangeListener<Number> priceChangeListener;
private AreaChart bsqPriceChart;
private XYChart.Series<Number, Number> seriesBSQAdded, seriesBSQBurnt;
private XYChart.Series<Number, Number> seriesBSQPrice;
private TextField marketCapTextField, priceTextField, availableAmountTextField;
private Coin availableAmount;
private int gridRow = 0;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle // Constructor, lifecycle
@ -50,20 +114,52 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
@Inject @Inject
private BsqDashboardView(DaoFacade daoFacade, private BsqDashboardView(DaoFacade daoFacade,
TradeStatisticsManager tradeStatisticsManager,
PriceFeedService priceFeedService, PriceFeedService priceFeedService,
DaoStateService daoStateService,
Preferences preferences, Preferences preferences,
BsqFormatter bsqFormatter) { BsqFormatter bsqFormatter,
BSFormatter btcFormatter) {
this.daoFacade = daoFacade; this.daoFacade = daoFacade;
this.tradeStatisticsManager = tradeStatisticsManager;
this.priceFeedService = priceFeedService; this.priceFeedService = priceFeedService;
this.daoStateService = daoStateService;
this.preferences = preferences; this.preferences = preferences;
this.bsqFormatter = bsqFormatter; this.bsqFormatter = bsqFormatter;
this.btcFormatter = btcFormatter;
} }
@Override @Override
public void initialize() { public void initialize() {
ADJUSTERS.put(DAY, TemporalAdjusters.ofDateAdjuster(d -> d));
createKPIs();
createChart();
priceChangeListener = (observable, oldValue, newValue) -> updatePrice(); priceChangeListener = (observable, oldValue, newValue) -> updatePrice();
} }
private void createKPIs() {
TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, 5, Res.get("dao.factsAndFigures.dashboard.marketPrice"));
titledGroupBg.getStyleClass().add("last");
Tuple3<Label, TextField, VBox> marketPriceTuple = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.factsAndFigures.dashboard.price"),
FIRST_ROW_DISTANCE);
priceTextField = marketPriceTuple.second;
GridPane.setColumnSpan(marketPriceTuple.third, 2);
marketCapTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.factsAndFigures.dashboard.marketCap")).second;
availableAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, 1,
Res.get("dao.factsAndFigures.dashboard.availableAmount")).second;
}
@Override @Override
protected void activate() { protected void activate() {
daoFacade.addBsqStateListener(this); daoFacade.addBsqStateListener(this);
@ -71,15 +167,16 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
updateWithBsqBlockChainData(); updateWithBsqBlockChainData();
updatePrice(); updatePrice();
updateChartData();
} }
@Override @Override
protected void deactivate() { protected void deactivate() {
daoFacade.removeBsqStateListener(this); daoFacade.removeBsqStateListener(this);
priceFeedService.updateCounterProperty().removeListener(priceChangeListener); priceFeedService.updateCounterProperty().removeListener(priceChangeListener);
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener // DaoStateListener
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -94,10 +191,123 @@ public class BsqDashboardView extends ActivatableView<GridPane, Void> implements
// Private // Private
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void createChart() {
NumberAxis xAxis = new NumberAxis();
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(true);
xAxis.setTickLabelGap(6);
xAxis.setTickMarkVisible(false);
xAxis.setMinorTickVisible(false);
xAxis.setTickLabelFormatter(new StringConverter<>() {
@Override
public String toString(Number timestamp) {
LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(timestamp.longValue(),
0, OffsetDateTime.now(ZoneId.systemDefault()).getOffset());
return localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM));
}
@Override
public Number fromString(String string) {
return 0;
}
});
NumberAxis yAxis = new NumberAxis();
yAxis.setForceZeroInRange(false);
yAxis.setSide(Side.RIGHT);
yAxis.setAutoRanging(true);
yAxis.setTickMarkVisible(false);
yAxis.setMinorTickVisible(false);
yAxis.setTickLabelGap(5);
yAxis.setTickLabelFormatter(new StringConverter<>() {
@Override
public String toString(Number marketPrice) {
return bsqFormatter.formatBTCWithCode(marketPrice.longValue());
}
@Override
public Number fromString(String string) {
return 0;
}
});
seriesBSQPrice = new XYChart.Series<>();
seriesBSQPrice.setName("Price in BTC for 1 BSQ");
bsqPriceChart = new AreaChart<>(xAxis, yAxis);
bsqPriceChart.setLegendVisible(true);
bsqPriceChart.setAnimated(false);
bsqPriceChart.setId("charts");
bsqPriceChart.setMinHeight(250);
bsqPriceChart.setPrefHeight(250);
bsqPriceChart.setCreateSymbols(true);
bsqPriceChart.setPadding(new Insets(0));
bsqPriceChart.getData().addAll(seriesBSQPrice);
GridPane.setRowIndex(bsqPriceChart, ++gridRow);
GridPane.setColumnSpan(bsqPriceChart, 2);
root.getChildren().addAll(bsqPriceChart);
}
private void updateChartData() {
updateBSQPriceData();
}
private void updateBSQPriceData() {
seriesBSQPrice.getData().clear();
Map<LocalDate, List<TradeStatistics2>> bsqPriceByDate = tradeStatisticsManager.getObservableTradeStatisticsSet().stream()
.filter(e -> e.getCurrencyCode().equals("BSQ"))
.sorted(Comparator.comparing(TradeStatistics2::getTradeDate))
.collect(Collectors.groupingBy(item -> new Date(item.getTradeDate().getTime()).toLocalDate()
.with(ADJUSTERS.get(DAY))));
List<XYChart.Data<Number, Number>> updatedBSQPrice = bsqPriceByDate.keySet().stream()
.map(e -> {
ZonedDateTime zonedDateTime = e.atStartOfDay(ZoneId.systemDefault());
return new XYChart.Data<Number, Number>(zonedDateTime.toInstant().getEpochSecond(), bsqPriceByDate.get(e).stream()
.map(TradeStatistics2::getTradePrice)
.mapToDouble(Price::getValue)
.average()
.orElse(Double.NaN)
);
})
.collect(Collectors.toList());
seriesBSQPrice.getData().setAll(updatedBSQPrice);
}
private void updateWithBsqBlockChainData() { private void updateWithBsqBlockChainData() {
Coin issuedAmountFromGenesis = daoFacade.getGenesisTotalSupply();
Coin issuedAmountFromCompRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.COMPENSATION));
Coin issuedAmountFromReimbursementRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT));
Coin burntFee = Coin.valueOf(daoFacade.getTotalBurntFee());
Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs());
availableAmount = issuedAmountFromGenesis
.add(issuedAmountFromCompRequests)
.add(issuedAmountFromReimbursementRequests)
.subtract(burntFee)
.subtract(totalConfiscatedAmount);
availableAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(availableAmount));
} }
private void updatePrice() { private void updatePrice() {
Optional<Price> optionalBsqPrice = priceFeedService.getBsqPrice();
if (optionalBsqPrice.isPresent()) {
Price bsqPrice = optionalBsqPrice.get();
priceTextField.setText(bsqFormatter.formatPrice(bsqPrice) + " BSQ/BTC");
marketCapTextField.setText(bsqFormatter.formatMarketCap(priceFeedService.getMarketPrice("BSQ"),
priceFeedService.getMarketPrice(preferences.getPreferredTradeCurrency().getCode()),
availableAmount));
} else {
priceTextField.setText(Res.get("shared.na"));
marketCapTextField.setText(Res.get("shared.na"));
}
} }
} }

View File

@ -1,225 +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 <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.main.dao.economy.details;
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.util.FormBuilder;
import bisq.desktop.util.Layout;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.governance.IssuanceType;
import bisq.core.locale.Res;
import bisq.core.monetary.Price;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.user.Preferences;
import bisq.core.util.BsqFormatter;
import bisq.common.util.Tuple3;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.beans.value.ChangeListener;
import java.util.Optional;
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;
@FxmlView
public class DetailsView extends ActivatableView<GridPane, Void> implements DaoStateListener {
private final DaoFacade daoFacade;
private final PriceFeedService priceFeedService;
private final Preferences preferences;
private final BsqFormatter bsqFormatter;
private int gridRow = 0;
private TextField genesisIssueAmountTextField, compRequestIssueAmountTextField, reimbursementAmountTextField, availableAmountTextField,
burntAmountTextField, totalLockedUpAmountTextField, totalUnlockingAmountTextField,
totalUnlockedAmountTextField, totalConfiscatedAmountTextField, allTxTextField, burntTxTextField,
utxoTextField, compensationIssuanceTxTextField,
reimbursementIssuanceTxTextField, priceTextField, marketCapTextField;
private ChangeListener<Number> priceChangeListener;
private Coin availableAmount;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
private DetailsView(DaoFacade daoFacade,
PriceFeedService priceFeedService,
Preferences preferences,
BsqFormatter bsqFormatter) {
this.daoFacade = daoFacade;
this.priceFeedService = priceFeedService;
this.preferences = preferences;
this.bsqFormatter = bsqFormatter;
}
@Override
public void initialize() {
int columnIndex = 2;
int startRow = gridRow;
addTitledGroupBg(root, ++gridRow, 5, Res.get("dao.wallet.dashboard.distribution"));
genesisIssueAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.genesisIssueAmount")).second;
compRequestIssueAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.compRequestIssueAmount")).second;
reimbursementAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.reimbursementAmount")).second;
burntAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.burntAmount")).second;
availableAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.availableAmount")).second;
gridRow = startRow;
addTitledGroupBg(root, ++gridRow, columnIndex, 5, Res.get("dao.wallet.dashboard.locked"), Layout.GROUP_DISTANCE);
totalLockedUpAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalLockedUpAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
totalUnlockingAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockingAmount")).second;
totalUnlockedAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalUnlockedAmount")).second;
totalConfiscatedAmountTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex, Res.get("dao.wallet.dashboard.totalConfiscatedAmount")).second;
gridRow++;
startRow = gridRow;
addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.wallet.dashboard.market"), Layout.GROUP_DISTANCE);
priceTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.price"), Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
marketCapTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.marketCap")).second;
gridRow = startRow;
addTitledGroupBg(root, ++gridRow, columnIndex, 2, Res.get("dao.wallet.dashboard.genesis"), Layout.GROUP_DISTANCE);
String genTxHeight = String.valueOf(daoFacade.getGenesisBlockHeight());
String genesisTxId = daoFacade.getGenesisTxId();
String url = preferences.getBsqBlockChainExplorer().txUrl + genesisTxId;
addTopLabelReadOnlyTextField(root, gridRow, columnIndex, Res.get("dao.wallet.dashboard.genesisBlockHeight"),
genTxHeight, Layout.FIRST_ROW_AND_GROUP_DISTANCE);
// TODO use addTopLabelTxIdTextField
Tuple3<Label, HyperlinkWithIcon, VBox> tuple = FormBuilder.addTopLabelHyperlinkWithIcon(root, ++gridRow, columnIndex,
Res.get("dao.wallet.dashboard.genesisTxId"), genesisTxId, url, 0);
HyperlinkWithIcon hyperlinkWithIcon = tuple.second;
hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", genesisTxId)));
startRow = gridRow;
addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.wallet.dashboard.txDetails"), Layout.GROUP_DISTANCE);
allTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.wallet.dashboard.allTx"),
genTxHeight, Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
utxoTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.wallet.dashboard.utxo")).second;
compensationIssuanceTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.wallet.dashboard.compensationIssuanceTx")).second;
gridRow = startRow;
addTitledGroupBg(root, ++gridRow, columnIndex, 3, "", Layout.GROUP_DISTANCE);
reimbursementIssuanceTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, gridRow, columnIndex,
Res.get("dao.wallet.dashboard.reimbursementIssuanceTx"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
burntTxTextField = FormBuilder.addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex,
Res.get("dao.wallet.dashboard.burntTx")).second;
++gridRow;
priceChangeListener = (observable, oldValue, newValue) -> updatePrice();
}
@Override
protected void activate() {
daoFacade.addBsqStateListener(this);
priceFeedService.updateCounterProperty().addListener(priceChangeListener);
updateWithBsqBlockChainData();
updatePrice();
}
@Override
protected void deactivate() {
daoFacade.removeBsqStateListener(this);
priceFeedService.updateCounterProperty().removeListener(priceChangeListener);
}
///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onParseBlockCompleteAfterBatchProcessing(Block block) {
updateWithBsqBlockChainData();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void updateWithBsqBlockChainData() {
Coin issuedAmountFromGenesis = daoFacade.getGenesisTotalSupply();
genesisIssueAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromGenesis));
Coin issuedAmountFromCompRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.COMPENSATION));
compRequestIssueAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromCompRequests));
Coin issuedAmountFromReimbursementRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT));
reimbursementAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromReimbursementRequests));
Coin burntFee = Coin.valueOf(daoFacade.getTotalBurntFee());
Coin totalLockedUpAmount = Coin.valueOf(daoFacade.getTotalLockupAmount());
Coin totalUnlockingAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockingTxOutputs());
Coin totalUnlockedAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockedTxOutputs());
Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs());
availableAmount = issuedAmountFromGenesis
.add(issuedAmountFromCompRequests)
.add(issuedAmountFromReimbursementRequests)
.subtract(burntFee)
.subtract(totalConfiscatedAmount);
availableAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(availableAmount));
burntAmountTextField.setText("-" + bsqFormatter.formatAmountWithGroupSeparatorAndCode(burntFee));
totalLockedUpAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalLockedUpAmount));
totalUnlockingAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockingAmount));
totalUnlockedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockedAmount));
totalConfiscatedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalConfiscatedAmount));
allTxTextField.setText(String.valueOf(daoFacade.getTxs().size()));
utxoTextField.setText(String.valueOf(daoFacade.getUnspentTxOutputs().size()));
compensationIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.COMPENSATION)));
reimbursementIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.REIMBURSEMENT)));
burntTxTextField.setText(String.valueOf(daoFacade.getFeeTxs().size()));
}
private void updatePrice() {
Optional<Price> optionalBsqPrice = priceFeedService.getBsqPrice();
if (optionalBsqPrice.isPresent()) {
Price bsqPrice = optionalBsqPrice.get();
priceTextField.setText(bsqFormatter.formatPrice(bsqPrice) + " BSQ/BTC");
marketCapTextField.setText(bsqFormatter.formatMarketCap(priceFeedService.getMarketPrice("BSQ"),
priceFeedService.getMarketPrice(preferences.getPreferredTradeCurrency().getCode()),
availableAmount));
} else {
priceTextField.setText(Res.get("shared.na"));
marketCapTextField.setText(Res.get("shared.na"));
}
}
}

View File

@ -20,14 +20,13 @@
<?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.GridPane?>
<GridPane fx:id="root" fx:controller="bisq.desktop.main.dao.economy.details.DetailsView" <GridPane fx:id="root" fx:controller="bisq.desktop.main.dao.economy.supply.SupplyView"
hgap="5.0" vgap="5.0" hgap="5.0" vgap="5.0"
AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="20.0" AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="20.0"
AnchorPane.rightAnchor="25.0" AnchorPane.topAnchor="20.0" AnchorPane.rightAnchor="25.0" AnchorPane.topAnchor="20.0"
xmlns:fx="http://javafx.com/fxml"> xmlns:fx="http://javafx.com/fxml">
<columnConstraints> <columnConstraints>
<ColumnConstraints percentWidth="50"/> <ColumnConstraints percentWidth="50"/>
<ColumnConstraints minWidth="10" maxWidth="5"/>
<ColumnConstraints percentWidth="50"/> <ColumnConstraints percentWidth="50"/>
</columnConstraints> </columnConstraints>
</GridPane> </GridPane>

View File

@ -0,0 +1,322 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.main.dao.economy.supply;
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.util.Layout;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.DaoStateService;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.blockchain.Tx;
import bisq.core.dao.state.model.governance.Issuance;
import bisq.core.dao.state.model.governance.IssuanceType;
import bisq.core.locale.Res;
import bisq.core.util.BsqFormatter;
import bisq.common.util.Tuple3;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.geometry.Insets;
import javafx.geometry.Side;
import javafx.util.StringConverter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.TemporalAdjusters;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;
import java.sql.Date;
@FxmlView
public class SupplyView extends ActivatableView<GridPane, Void> implements DaoStateListener {
private static final String MONTH = "month";
private final DaoFacade daoFacade;
private DaoStateService daoStateService;
private final BsqFormatter bsqFormatter;
private int gridRow = 0;
private TextField genesisIssueAmountTextField, compRequestIssueAmountTextField, reimbursementAmountTextField,
burntAmountTextField, totalLockedUpAmountTextField, totalUnlockingAmountTextField,
totalUnlockedAmountTextField, totalConfiscatedAmountTextField;
private XYChart.Series<Number, Number> seriesBSQIssued, seriesBSQBurnt;
private static final Map<String, TemporalAdjuster> ADJUSTERS = new HashMap<>();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
private SupplyView(DaoFacade daoFacade,
DaoStateService daoStateService,
BsqFormatter bsqFormatter) {
this.daoFacade = daoFacade;
this.daoStateService = daoStateService;
this.bsqFormatter = bsqFormatter;
}
@Override
public void initialize() {
ADJUSTERS.put(MONTH, TemporalAdjusters.firstDayOfMonth());
createSupplyIncreasedInformation();
createSupplyReducedInformation();
createSupplyLockedInformation();
}
@Override
protected void activate() {
daoFacade.addBsqStateListener(this);
updateWithBsqBlockChainData();
updateBSQTokenData();
}
@Override
protected void deactivate() {
daoFacade.removeBsqStateListener(this);
}
///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onParseBlockCompleteAfterBatchProcessing(Block block) {
updateWithBsqBlockChainData();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void createSupplyIncreasedInformation() {
addTitledGroupBg(root, ++gridRow, 3, Res.get("dao.factsAndFigures.supply.issued"));
Tuple3<Label, TextField, VBox> genesisAmountTuple = addTopLabelReadOnlyTextField(root, gridRow,
Res.get("dao.factsAndFigures.supply.genesisIssueAmount"), Layout.FIRST_ROW_DISTANCE);
genesisIssueAmountTextField = genesisAmountTuple.second;
GridPane.setColumnSpan(genesisAmountTuple.third, 2);
compRequestIssueAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.factsAndFigures.supply.compRequestIssueAmount")).second;
reimbursementAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1,
Res.get("dao.factsAndFigures.supply.reimbursementAmount")).second;
seriesBSQIssued = new XYChart.Series<>();
createChart(seriesBSQIssued, Res.get("dao.factsAndFigures.supply.issued"));
}
private void createSupplyReducedInformation() {
addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.factsAndFigures.supply.burnt"), Layout.GROUP_DISTANCE);
Tuple3<Label, TextField, VBox> burntAmountTuple = addTopLabelReadOnlyTextField(root, gridRow,
Res.get("dao.factsAndFigures.supply.burntAmount"), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
burntAmountTextField = burntAmountTuple.second;
GridPane.setColumnSpan(burntAmountTuple.third, 2);
seriesBSQBurnt = new XYChart.Series<>();
createChart(seriesBSQBurnt, Res.get("dao.factsAndFigures.supply.burnt"));
}
private void createSupplyLockedInformation() {
TitledGroupBg titledGroupBg = addTitledGroupBg(root, ++gridRow, 2, Res.get("dao.factsAndFigures.supply.locked"), Layout.GROUP_DISTANCE);
titledGroupBg.getStyleClass().add("last");
totalLockedUpAmountTextField = addTopLabelReadOnlyTextField(root, gridRow,
Res.get("dao.factsAndFigures.supply.totalLockedUpAmount"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
totalUnlockingAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1,
Res.get("dao.factsAndFigures.supply.totalUnlockingAmount"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
totalUnlockedAmountTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.factsAndFigures.supply.totalUnlockedAmount")).second;
totalConfiscatedAmountTextField = addTopLabelReadOnlyTextField(root, gridRow, 1,
Res.get("dao.factsAndFigures.supply.totalConfiscatedAmount")).second;
}
private void createChart(XYChart.Series<Number, Number> series, String seriesLabel) {
NumberAxis xAxis = new NumberAxis();
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(true);
xAxis.setTickLabelGap(6);
xAxis.setTickMarkVisible(false);
xAxis.setMinorTickVisible(false);
xAxis.setTickLabelFormatter(new StringConverter<>() {
@Override
public String toString(Number timestamp) {
LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(timestamp.longValue(),
0, OffsetDateTime.now(ZoneId.systemDefault()).getOffset());
return localDateTime.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM));
}
@Override
public Number fromString(String string) {
return 0;
}
});
NumberAxis yAxis = new NumberAxis();
yAxis.setForceZeroInRange(false);
yAxis.setSide(Side.RIGHT);
yAxis.setAutoRanging(true);
yAxis.setTickMarkVisible(false);
yAxis.setMinorTickVisible(false);
yAxis.setTickLabelGap(5);
yAxis.setTickLabelFormatter(new StringConverter<>() {
@Override
public String toString(Number marketPrice) {
return bsqFormatter.formatBSQSatoshisWithCode(marketPrice.longValue());
}
@Override
public Number fromString(String string) {
return 0;
}
});
series.setName(seriesLabel);
AreaChart<Number, Number> chart = new AreaChart<>(xAxis, yAxis);
chart.setLegendVisible(true);
chart.setAnimated(false);
chart.setId("charts");
chart.setMinHeight(250);
chart.setPrefHeight(250);
chart.setCreateSymbols(true);
chart.setPadding(new Insets(0));
chart.getData().addAll(series);
GridPane.setColumnSpan(chart, 2);
GridPane.setRowIndex(chart, ++gridRow);
root.getChildren().add(chart);
}
private void updateWithBsqBlockChainData() {
Coin issuedAmountFromGenesis = daoFacade.getGenesisTotalSupply();
genesisIssueAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromGenesis));
Coin issuedAmountFromCompRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.COMPENSATION));
compRequestIssueAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromCompRequests));
Coin issuedAmountFromReimbursementRequests = Coin.valueOf(daoFacade.getTotalIssuedAmount(IssuanceType.REIMBURSEMENT));
reimbursementAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(issuedAmountFromReimbursementRequests));
Coin burntFee = Coin.valueOf(daoFacade.getTotalBurntFee());
Coin totalLockedUpAmount = Coin.valueOf(daoFacade.getTotalLockupAmount());
Coin totalUnlockingAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockingTxOutputs());
Coin totalUnlockedAmount = Coin.valueOf(daoFacade.getTotalAmountOfUnLockedTxOutputs());
Coin totalConfiscatedAmount = Coin.valueOf(daoFacade.getTotalAmountOfConfiscatedTxOutputs());
burntAmountTextField.setText("-" + bsqFormatter.formatAmountWithGroupSeparatorAndCode(burntFee));
totalLockedUpAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalLockedUpAmount));
totalUnlockingAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockingAmount));
totalUnlockedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalUnlockedAmount));
totalConfiscatedAmountTextField.setText(bsqFormatter.formatAmountWithGroupSeparatorAndCode(totalConfiscatedAmount));
}
private void updateBSQTokenData() {
seriesBSQIssued.getData().clear();
seriesBSQBurnt.getData().clear();
Map<LocalDate, List<Tx>> feesBurntByMonth = daoStateService.getBurntFeeTxs().stream()
.sorted(Comparator.comparing(Tx::getTime))
.collect(Collectors.groupingBy(item -> new Date(item.getTime()).toLocalDate()
.with(ADJUSTERS.get(MONTH))));
List<XYChart.Data<Number, Number>> updatedBurntBSQ = feesBurntByMonth.keySet().stream()
.map(date -> {
ZonedDateTime zonedDateTime = date.atStartOfDay(ZoneId.systemDefault());
return new XYChart.Data<Number, Number>(zonedDateTime.toInstant().getEpochSecond(), feesBurntByMonth.get(date)
.stream()
.mapToDouble(Tx::getBurntFee)
.sum()
);
})
.collect(Collectors.toList());
seriesBSQBurnt.getData().setAll(updatedBurntBSQ);
Stream<Issuance> bsqByCompensation = daoStateService.getIssuanceSet(IssuanceType.COMPENSATION).stream()
.sorted(Comparator.comparing(Issuance::getChainHeight));
Stream<Issuance> bsqByReImbursement = daoStateService.getIssuanceSet(IssuanceType.REIMBURSEMENT).stream()
.sorted(Comparator.comparing(Issuance::getChainHeight));
Map<LocalDate, List<Issuance>> bsqAddedByVote = Stream.concat(bsqByCompensation, bsqByReImbursement)
.collect(Collectors.groupingBy(item -> new Date(daoFacade.getBlockTime(item.getChainHeight())).toLocalDate()
.with(ADJUSTERS.get(MONTH))));
List<XYChart.Data<Number, Number>> updatedAddedBSQ = bsqAddedByVote.keySet().stream()
.map(date -> {
ZonedDateTime zonedDateTime = date.atStartOfDay(ZoneId.systemDefault());
return new XYChart.Data<Number, Number>(zonedDateTime.toInstant().getEpochSecond(), bsqAddedByVote.get(date)
.stream()
.mapToDouble(Issuance::getAmount)
.sum());
})
.collect(Collectors.toList());
seriesBSQIssued.getData().setAll(updatedAddedBSQ);
}
}

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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 <http://www.gnu.org/licenses/>.
-->
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<GridPane fx:id="root" fx:controller="bisq.desktop.main.dao.economy.transactions.BSQTransactionsView"
hgap="5.0" vgap="5.0"
AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="20.0"
AnchorPane.rightAnchor="25.0" AnchorPane.topAnchor="20.0"
xmlns:fx="http://javafx.com/fxml">
<columnConstraints>
<ColumnConstraints percentWidth="50"/>
<ColumnConstraints percentWidth="50"/>
</columnConstraints>
</GridPane>

View File

@ -0,0 +1,149 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.main.dao.economy.transactions;
import bisq.desktop.common.view.ActivatableView;
import bisq.desktop.common.view.FxmlView;
import bisq.desktop.components.HyperlinkWithIcon;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.util.Layout;
import bisq.core.dao.DaoFacade;
import bisq.core.dao.state.DaoStateListener;
import bisq.core.dao.state.model.blockchain.Block;
import bisq.core.dao.state.model.governance.IssuanceType;
import bisq.core.locale.Res;
import bisq.core.user.Preferences;
import bisq.common.util.Tuple3;
import javax.inject.Inject;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
import static bisq.desktop.util.FormBuilder.addTopLabelHyperlinkWithIcon;
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;
@FxmlView
public class BSQTransactionsView extends ActivatableView<GridPane, Void> implements DaoStateListener {
private final DaoFacade daoFacade;
private final Preferences preferences;
private int gridRow = 0;
private TextField allTxTextField, burntTxTextField,
utxoTextField, compensationIssuanceTxTextField,
reimbursementIssuanceTxTextField;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, lifecycle
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
private BSQTransactionsView(DaoFacade daoFacade,
Preferences preferences) {
this.daoFacade = daoFacade;
this.preferences = preferences;
}
@Override
public void initialize() {
addTitledGroupBg(root, gridRow, 2, Res.get("dao.factsAndFigures.transactions.genesis"));
String genTxHeight = String.valueOf(daoFacade.getGenesisBlockHeight());
String genesisTxId = daoFacade.getGenesisTxId();
String url = preferences.getBsqBlockChainExplorer().txUrl + genesisTxId;
GridPane.setColumnSpan(addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.factsAndFigures.transactions.genesisBlockHeight"),
genTxHeight, Layout.FIRST_ROW_DISTANCE).third, 2);
// TODO use addTopLabelTxIdTextField
Tuple3<Label, HyperlinkWithIcon, VBox> tuple = addTopLabelHyperlinkWithIcon(root, ++gridRow,
Res.get("dao.factsAndFigures.transactions.genesisTxId"), genesisTxId, url, 0);
HyperlinkWithIcon hyperlinkWithIcon = tuple.second;
hyperlinkWithIcon.setTooltip(new Tooltip(Res.get("tooltip.openBlockchainForTx", genesisTxId)));
GridPane.setColumnSpan(tuple.third, 2);
int startRow = ++gridRow;
TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, 3, Res.get("dao.factsAndFigures.transactions.txDetails"), Layout.GROUP_DISTANCE);
titledGroupBg.getStyleClass().add("last");
allTxTextField = addTopLabelReadOnlyTextField(root, gridRow, Res.get("dao.factsAndFigures.transactions.allTx"),
genTxHeight, Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
utxoTextField = addTopLabelReadOnlyTextField(root, ++gridRow, Res.get("dao.factsAndFigures.transactions.utxo")).second;
compensationIssuanceTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow,
Res.get("dao.factsAndFigures.transactions.compensationIssuanceTx")).second;
int columnIndex = 1;
gridRow = startRow;
titledGroupBg = addTitledGroupBg(root, startRow, columnIndex, 3, "", Layout.GROUP_DISTANCE);
titledGroupBg.getStyleClass().add("last");
reimbursementIssuanceTxTextField = addTopLabelReadOnlyTextField(root, gridRow, columnIndex,
Res.get("dao.factsAndFigures.transactions.reimbursementIssuanceTx"),
Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
burntTxTextField = addTopLabelReadOnlyTextField(root, ++gridRow, columnIndex,
Res.get("dao.factsAndFigures.transactions.burntTx")).second;
}
@Override
protected void activate() {
daoFacade.addBsqStateListener(this);
updateWithBsqBlockChainData();
}
@Override
protected void deactivate() {
daoFacade.removeBsqStateListener(this);
}
///////////////////////////////////////////////////////////////////////////////////////////
// DaoStateListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onParseBlockCompleteAfterBatchProcessing(Block block) {
updateWithBsqBlockChainData();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void updateWithBsqBlockChainData() {
allTxTextField.setText(String.valueOf(daoFacade.getTxs().size()));
utxoTextField.setText(String.valueOf(daoFacade.getUnspentTxOutputs().size()));
compensationIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.COMPENSATION)));
reimbursementIssuanceTxTextField.setText(String.valueOf(daoFacade.getNumIssuanceTransactions(IssuanceType.REIMBURSEMENT)));
burntTxTextField.setText(String.valueOf(daoFacade.getFeeTxs().size()));
}
}

View File

@ -336,7 +336,7 @@ public class OfferBookChartView extends ActivatableViewAndModel<VBox, OfferBookC
areaChart.setId("charts"); areaChart.setId("charts");
areaChart.setMinHeight(300); areaChart.setMinHeight(300);
areaChart.setPrefHeight(300); areaChart.setPrefHeight(300);
areaChart.setCreateSymbols(false); areaChart.setCreateSymbols(true);
areaChart.setPadding(new Insets(0, 10, 0, 10)); areaChart.setPadding(new Insets(0, 10, 0, 10));
areaChart.getData().addAll(seriesBuy, seriesSell); areaChart.getData().addAll(seriesBuy, seriesSell);
} }