mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 15:10:44 +01:00
Add time interval selector
This commit is contained in:
parent
9dab4fd65a
commit
67d76cda27
9 changed files with 273 additions and 97 deletions
|
@ -21,6 +21,8 @@ import bisq.desktop.common.model.ActivatableViewModel;
|
|||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
@ -28,7 +30,7 @@ import java.util.function.Predicate;
|
|||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class ChartModel extends ActivatableViewModel {
|
||||
public abstract class ChartModel extends ActivatableViewModel {
|
||||
public interface Listener {
|
||||
/**
|
||||
* @param fromDate Epoch date in millis for earliest data
|
||||
|
@ -40,9 +42,12 @@ public class ChartModel extends ActivatableViewModel {
|
|||
protected Number lowerBound, upperBound;
|
||||
protected final Set<Listener> listeners = new HashSet<>();
|
||||
|
||||
private Predicate<Long> predicate = e -> true;
|
||||
|
||||
public ChartModel() {
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Lifecycle
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -84,8 +89,17 @@ public class ChartModel extends ActivatableViewModel {
|
|||
return new Tuple2<>(fromDateSec, toDateSec);
|
||||
}
|
||||
|
||||
Predicate<Long> getPredicate(Tuple2<Double, Double> fromToTuple) {
|
||||
return value -> value >= fromToTuple.first && value <= fromToTuple.second;
|
||||
protected abstract void applyTemporalAdjuster(TemporalAdjuster temporalAdjuster);
|
||||
|
||||
protected abstract TemporalAdjuster getTemporalAdjuster();
|
||||
|
||||
Predicate<Long> getPredicate() {
|
||||
return predicate;
|
||||
}
|
||||
|
||||
Predicate<Long> setAndGetPredicate(Tuple2<Double, Double> fromToTuple) {
|
||||
predicate = value -> value >= fromToTuple.first && value <= fromToTuple.second;
|
||||
return predicate;
|
||||
}
|
||||
|
||||
void notifyListeners(Tuple2<Double, Double> fromToTuple) {
|
||||
|
|
|
@ -19,6 +19,9 @@ package bisq.desktop.components.chart;
|
|||
|
||||
import bisq.desktop.common.view.ActivatableView;
|
||||
import bisq.desktop.components.AutoTooltipSlideToggleButton;
|
||||
import bisq.desktop.components.AutoTooltipToggleButton;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
|
@ -29,8 +32,12 @@ import javafx.scene.chart.NumberAxis;
|
|||
import javafx.scene.chart.XYChart;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.SplitPane;
|
||||
import javafx.scene.control.Toggle;
|
||||
import javafx.scene.control.ToggleButton;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.Priority;
|
||||
|
@ -44,6 +51,8 @@ import javafx.beans.value.ChangeListener;
|
|||
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
|
@ -54,12 +63,15 @@ import java.util.HashMap;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.desktop.util.FormBuilder.getTopLabelWithVBox;
|
||||
|
||||
@Slf4j
|
||||
public abstract class ChartView<T extends ChartModel> extends ActivatableView<VBox, T> {
|
||||
private final Pane center;
|
||||
|
@ -72,28 +84,27 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
private final HBox timelineLabels;
|
||||
private final List<Node> dividerNodes = new ArrayList<>();
|
||||
private final Double[] dividerPositions = new Double[]{0d, 1d};
|
||||
private final ToggleGroup timeUnitToggleGroup = new ToggleGroup();
|
||||
private boolean pressed;
|
||||
private double x;
|
||||
private ChangeListener<Number> widthListener;
|
||||
private int maxSeriesSize;
|
||||
private ChangeListener<Toggle> timeUnitChangeListener;
|
||||
protected String dateFormatPatters = "dd MMM\nyyyy";
|
||||
|
||||
public ChartView(T model) {
|
||||
super(model);
|
||||
|
||||
root = new VBox();
|
||||
Pane left = new Pane();
|
||||
center = new Pane();
|
||||
Pane right = new Pane();
|
||||
splitPane = new SplitPane();
|
||||
splitPane.getItems().addAll(left, center, right);
|
||||
|
||||
// time units
|
||||
Pane timeIntervalBox = getTimeIntervalBox();
|
||||
|
||||
// chart
|
||||
xAxis = getXAxis();
|
||||
yAxis = getYAxis();
|
||||
|
||||
chart = getChart();
|
||||
|
||||
addSeries();
|
||||
|
||||
HBox legendBox1 = getLegendBox(getSeriesForLegend1());
|
||||
Collection<XYChart.Series<Number, Number>> seriesForLegend2 = getSeriesForLegend2();
|
||||
HBox legendBox2 = null;
|
||||
|
@ -101,8 +112,15 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
legendBox2 = getLegendBox(seriesForLegend2);
|
||||
}
|
||||
|
||||
// Time navigation
|
||||
Pane left = new Pane();
|
||||
center = new Pane();
|
||||
Pane right = new Pane();
|
||||
splitPane = new SplitPane();
|
||||
splitPane.getItems().addAll(left, center, right);
|
||||
timelineLabels = new HBox();
|
||||
|
||||
// Container
|
||||
VBox box = new VBox();
|
||||
int paddingRight = 89;
|
||||
int paddingLeft = 15;
|
||||
|
@ -114,14 +132,9 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
VBox.setMargin(legendBox2, new Insets(-20, paddingRight, 0, paddingLeft));
|
||||
box.getChildren().add(legendBox2);
|
||||
}
|
||||
root.getChildren().addAll(chart, box);
|
||||
root.getChildren().addAll(timeIntervalBox, chart, box);
|
||||
}
|
||||
|
||||
protected abstract Collection<XYChart.Series<Number, Number>> getSeriesForLegend1();
|
||||
|
||||
protected abstract Collection<XYChart.Series<Number, Number>> getSeriesForLegend2();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Lifecycle
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -138,24 +151,37 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
splitPane.setDividerPosition(0, dividerPositions[0]);
|
||||
splitPane.setDividerPosition(1, dividerPositions[1]);
|
||||
};
|
||||
|
||||
timeUnitChangeListener = (observable, oldValue, newValue) -> {
|
||||
TemporalAdjusterUtil.Interval interval = (TemporalAdjusterUtil.Interval) newValue.getUserData();
|
||||
applyTemporalAdjuster(interval.getAdjuster());
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activate() {
|
||||
root.widthProperty().addListener(widthListener);
|
||||
timeUnitToggleGroup.selectedToggleProperty().addListener(timeUnitChangeListener);
|
||||
|
||||
splitPane.setOnMousePressed(this::onMousePressedSplitPane);
|
||||
splitPane.setOnMouseDragged(this::onMouseDragged);
|
||||
center.setOnMousePressed(this::onMousePressedCenter);
|
||||
center.setOnMouseReleased(this::onMouseReleasedCenter);
|
||||
|
||||
initData();
|
||||
|
||||
initDividerMouseHandlers();
|
||||
// Need to get called again here as otherwise styles are not applied correctly
|
||||
applySeriesStyles();
|
||||
|
||||
TemporalAdjuster temporalAdjuster = model.getTemporalAdjuster();
|
||||
applyTemporalAdjuster(temporalAdjuster);
|
||||
findToggleByTemporalAdjuster(temporalAdjuster).ifPresent(timeUnitToggleGroup::selectToggle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deactivate() {
|
||||
root.widthProperty().removeListener(widthListener);
|
||||
timeUnitToggleGroup.selectedToggleProperty().removeListener(timeUnitChangeListener);
|
||||
splitPane.setOnMousePressed(null);
|
||||
splitPane.setOnMouseDragged(null);
|
||||
center.setOnMousePressed(null);
|
||||
|
@ -176,6 +202,40 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
// Customisations
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected Pane getTimeIntervalBox() {
|
||||
ToggleButton year = getToggleButton(Res.get("time.year"), TemporalAdjusterUtil.Interval.YEAR,
|
||||
timeUnitToggleGroup, "toggle-left");
|
||||
ToggleButton month = getToggleButton(Res.get("time.month"), TemporalAdjusterUtil.Interval.MONTH,
|
||||
timeUnitToggleGroup, "toggle-center");
|
||||
ToggleButton week = getToggleButton(Res.get("time.week"), TemporalAdjusterUtil.Interval.WEEK,
|
||||
timeUnitToggleGroup, "toggle-center");
|
||||
ToggleButton day = getToggleButton(Res.get("time.day"), TemporalAdjusterUtil.Interval.DAY,
|
||||
timeUnitToggleGroup, "toggle-center");
|
||||
|
||||
HBox toggleBox = new HBox();
|
||||
toggleBox.setSpacing(0);
|
||||
toggleBox.setAlignment(Pos.CENTER_LEFT);
|
||||
toggleBox.getChildren().addAll(year, month, week, day);
|
||||
|
||||
Tuple2<Label, VBox> topLabelWithVBox = getTopLabelWithVBox(Res.get("shared.interval"), toggleBox);
|
||||
AnchorPane pane = new AnchorPane();
|
||||
VBox vBox = topLabelWithVBox.second;
|
||||
pane.getChildren().add(vBox);
|
||||
AnchorPane.setRightAnchor(vBox, 90d);
|
||||
return pane;
|
||||
}
|
||||
|
||||
protected ToggleButton getToggleButton(String label,
|
||||
TemporalAdjusterUtil.Interval interval,
|
||||
ToggleGroup toggleGroup,
|
||||
String style) {
|
||||
ToggleButton toggleButton = new AutoTooltipToggleButton(label);
|
||||
toggleButton.setUserData(interval);
|
||||
toggleButton.setToggleGroup(toggleGroup);
|
||||
toggleButton.setId(style);
|
||||
return toggleButton;
|
||||
}
|
||||
|
||||
protected NumberAxis getXAxis() {
|
||||
NumberAxis xAxis;
|
||||
xAxis = new NumberAxis();
|
||||
|
@ -214,7 +274,7 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
toggle.setText(seriesName);
|
||||
toggle.setId("charts-legend-toggle" + seriesIndexMap.get(seriesName));
|
||||
toggle.setSelected(true);
|
||||
toggle.setOnAction(e -> onSelectToggle(series, toggle.isSelected()));
|
||||
toggle.setOnAction(e -> onSelectLegendToggle(series, toggle.isSelected()));
|
||||
hBox.getChildren().add(toggle);
|
||||
});
|
||||
Region spacer = new Region();
|
||||
|
@ -223,27 +283,32 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
return hBox;
|
||||
}
|
||||
|
||||
private void onSelectToggle(XYChart.Series<Number, Number> series, boolean isSelected) {
|
||||
protected abstract Collection<XYChart.Series<Number, Number>> getSeriesForLegend1();
|
||||
|
||||
protected abstract Collection<XYChart.Series<Number, Number>> getSeriesForLegend2();
|
||||
|
||||
private void onSelectLegendToggle(XYChart.Series<Number, Number> series, boolean isSelected) {
|
||||
if (isSelected) {
|
||||
chart.getData().add(series);
|
||||
} else {
|
||||
chart.getData().remove(series);
|
||||
}
|
||||
applySeriesStyles();
|
||||
applyTooltip();
|
||||
}
|
||||
|
||||
protected void hideSeries(XYChart.Series<Number, Number> series) {
|
||||
toggleBySeriesName.get(series.getName()).setSelected(false);
|
||||
onSelectToggle(series, false);
|
||||
onSelectLegendToggle(series, false);
|
||||
}
|
||||
|
||||
protected StringConverter<Number> getTimeAxisStringConverter() {
|
||||
return new StringConverter<>() {
|
||||
@Override
|
||||
public String toString(Number value) {
|
||||
DateFormat f = new SimpleDateFormat("YYYY-MM");
|
||||
DateFormat format = new SimpleDateFormat(dateFormatPatters);
|
||||
Date date = new Date(Math.round(value.doubleValue()) * 1000);
|
||||
return f.format(date);
|
||||
return format.format(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -267,6 +332,30 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
};
|
||||
}
|
||||
|
||||
protected void applyTemporalAdjuster(TemporalAdjuster temporalAdjuster) {
|
||||
model.applyTemporalAdjuster(temporalAdjuster);
|
||||
findToggleByTemporalAdjuster(temporalAdjuster)
|
||||
.map(e -> (TemporalAdjusterUtil.Interval) e.getUserData())
|
||||
.ifPresent(interval -> setDateFormatPatters(interval));
|
||||
|
||||
updateData(model.getPredicate());
|
||||
}
|
||||
|
||||
private void setDateFormatPatters(TemporalAdjusterUtil.Interval interval) {
|
||||
switch (interval) {
|
||||
case YEAR:
|
||||
dateFormatPatters = "yyyy";
|
||||
break;
|
||||
case MONTH:
|
||||
dateFormatPatters = "MMM\nyyyy";
|
||||
break;
|
||||
default:
|
||||
dateFormatPatters = "MMM dd\nyyyy";
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected abstract void initData();
|
||||
|
||||
protected abstract void updateData(Predicate<Long> predicate);
|
||||
|
@ -274,20 +363,25 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
protected void applyTooltip() {
|
||||
chart.getData().forEach(series -> {
|
||||
series.getData().forEach(data -> {
|
||||
String xValue = getXAxis().getTickLabelFormatter().toString(data.getXValue());
|
||||
String yValue = getYAxis().getTickLabelFormatter().toString(data.getYValue());
|
||||
Node node = data.getNode();
|
||||
Tooltip.install(node, new Tooltip(yValue + "\n" + xValue));
|
||||
|
||||
//Adding class on hover
|
||||
node.setOnMouseEntered(event -> node.getStyleClass().add("onHover"));
|
||||
|
||||
//Removing class on exit
|
||||
node.setOnMouseExited(event -> node.getStyleClass().remove("onHover"));
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
String xValue = getTooltipDateConverter(data.getXValue());
|
||||
String yValue = getYAxisStringConverter().toString(data.getYValue());
|
||||
Tooltip.install(node, new Tooltip(Res.get("dao.factsAndFigures.supply.chart.tradeFee.toolTip", yValue, xValue)));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected String getTooltipDateConverter(Number date) {
|
||||
return getTimeAxisStringConverter().toString(date).replace("\n", " ");
|
||||
}
|
||||
|
||||
protected String getTooltipValueConverter(Number value) {
|
||||
return getYAxisStringConverter().toString(value);
|
||||
}
|
||||
|
||||
// Only called once when initial data are applied. We want the min. and max. values so we have the max. scale for
|
||||
// navigation.
|
||||
protected void setTimeLineLabels() {
|
||||
|
@ -317,7 +411,7 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
|
||||
// The chart framework assigns the colored depending on the order it got added, but want to keep colors
|
||||
// the same so they match with the legend toggle.
|
||||
private void applySeriesStyles() {
|
||||
protected void applySeriesStyles() {
|
||||
for (int index = 0; index < chart.getData().size(); index++) {
|
||||
XYChart.Series<Number, Number> series = chart.getData().get(index);
|
||||
int staticIndex = seriesIndexMap.get(series.getName());
|
||||
|
@ -350,6 +444,12 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
return maxSeriesSize;
|
||||
}
|
||||
|
||||
private Optional<Toggle> findToggleByTemporalAdjuster(TemporalAdjuster adjuster) {
|
||||
return timeUnitToggleGroup.getToggles().stream()
|
||||
.filter(toggle -> ((TemporalAdjusterUtil.Interval) toggle.getUserData()).getAdjuster().equals(adjuster))
|
||||
.findAny();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Timeline navigation
|
||||
|
@ -371,8 +471,7 @@ public abstract class ChartView<T extends ChartModel> extends ActivatableView<VB
|
|||
dividerPositions[1] = rightPos;
|
||||
splitPane.setDividerPositions(leftPos, rightPos);
|
||||
Tuple2<Double, Double> fromToTuple = model.timelinePositionToEpochSeconds(leftPos, rightPos);
|
||||
updateData(model.getPredicate(fromToTuple));
|
||||
applySeriesStyles();
|
||||
updateData(model.setAndGetPredicate(fromToTuple));
|
||||
model.notifyListeners(fromToTuple);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.components.chart;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class TemporalAdjusterUtil {
|
||||
public enum Interval {
|
||||
YEAR(TemporalAdjusters.firstDayOfYear()),
|
||||
MONTH(TemporalAdjusters.firstDayOfMonth()),
|
||||
WEEK(TemporalAdjusters.ofDateAdjuster(date -> date.plusWeeks(1))),
|
||||
DAY(TemporalAdjusters.ofDateAdjuster(d -> d));
|
||||
|
||||
@Getter
|
||||
private final TemporalAdjuster adjuster;
|
||||
|
||||
Interval(TemporalAdjuster adjuster) {
|
||||
this.adjuster = adjuster;
|
||||
}
|
||||
}
|
||||
|
||||
private static final ZoneId ZONE_ID = ZoneId.systemDefault();
|
||||
|
||||
public static long toTimeInterval(Instant instant, TemporalAdjuster temporalAdjuster) {
|
||||
return instant
|
||||
.atZone(ZONE_ID)
|
||||
.toLocalDate()
|
||||
.with(temporalAdjuster)
|
||||
.atStartOfDay(ZONE_ID)
|
||||
.toInstant()
|
||||
.getEpochSecond();
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package bisq.desktop.main.dao.economy.supply;
|
||||
|
||||
import bisq.desktop.components.chart.TemporalAdjusterUtil;
|
||||
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
import bisq.core.dao.state.model.blockchain.Tx;
|
||||
import bisq.core.dao.state.model.governance.Issuance;
|
||||
|
@ -26,9 +28,7 @@ import javax.inject.Inject;
|
|||
import javax.inject.Singleton;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
@ -46,11 +46,10 @@ import lombok.extern.slf4j.Slf4j;
|
|||
@Slf4j
|
||||
@Singleton
|
||||
public class DaoEconomyDataProvider {
|
||||
private static final ZoneId ZONE_ID = ZoneId.systemDefault();
|
||||
private static final TemporalAdjuster FIRST_DAY_OF_MONTH = TemporalAdjusters.firstDayOfMonth();
|
||||
|
||||
private final DaoStateService daoStateService;
|
||||
private final Function<Integer, Long> blockHeightToEpochSeconds;
|
||||
private final Function<Issuance, Long> blockTimeOfIssuanceFunction;
|
||||
|
||||
private TemporalAdjuster temporalAdjuster = TemporalAdjusterUtil.Interval.MONTH.getAdjuster();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -62,8 +61,23 @@ public class DaoEconomyDataProvider {
|
|||
super();
|
||||
this.daoStateService = daoStateService;
|
||||
|
||||
blockHeightToEpochSeconds = memoize(height ->
|
||||
toStartOfMonth(Instant.ofEpochMilli(daoStateService.getBlockTime(height))));
|
||||
blockTimeOfIssuanceFunction = memoize(issuance -> {
|
||||
int height = daoStateService.getStartHeightOfCurrentCycle(issuance.getChainHeight()).orElse(0);
|
||||
return daoStateService.getBlockTime(height);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void setTemporalAdjuster(TemporalAdjuster temporalAdjuster) {
|
||||
this.temporalAdjuster = temporalAdjuster;
|
||||
}
|
||||
|
||||
public TemporalAdjuster getTemporalAdjuster() {
|
||||
return temporalAdjuster;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,7 +123,7 @@ public class DaoEconomyDataProvider {
|
|||
|
||||
public Map<Long, Long> getBurnedBsqByMonth(Collection<Tx> txs, Predicate<Long> predicate) {
|
||||
return txs.stream()
|
||||
.collect(Collectors.groupingBy(tx -> toStartOfMonth(Instant.ofEpochMilli(tx.getTime()))))
|
||||
.collect(Collectors.groupingBy(tx -> toTimeInterval(Instant.ofEpochMilli(tx.getTime()))))
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> predicate.test(entry.getKey()))
|
||||
|
@ -125,8 +139,8 @@ public class DaoEconomyDataProvider {
|
|||
// as adjuster would be more complicate (though could be done in future).
|
||||
public Map<Long, Long> getIssuedBsqByMonth(Set<Issuance> issuanceSet, Predicate<Long> predicate) {
|
||||
return issuanceSet.stream()
|
||||
.collect(Collectors.groupingBy(blockHeightToEpochSeconds.compose(issuance ->
|
||||
daoStateService.getStartHeightOfCurrentCycle(issuance.getChainHeight()).orElse(0))))
|
||||
.collect(Collectors.groupingBy(issuance ->
|
||||
toTimeInterval(Instant.ofEpochMilli(blockTimeOfIssuanceFunction.apply(issuance)))))
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(entry -> predicate.test(entry.getKey()))
|
||||
|
@ -165,18 +179,13 @@ public class DaoEconomyDataProvider {
|
|||
// as we have clearly separated that now. In case we have duplicate data for a months we use the static data.
|
||||
return historicalData.entrySet().stream()
|
||||
.filter(e -> predicate.test(e.getKey()))
|
||||
.collect(Collectors.toMap(e -> toStartOfMonth(Instant.ofEpochSecond(e.getKey())),
|
||||
Map.Entry::getValue));
|
||||
.collect(Collectors.toMap(e -> toTimeInterval(Instant.ofEpochSecond(e.getKey())),
|
||||
Map.Entry::getValue,
|
||||
(a, b) -> a + b));
|
||||
}
|
||||
|
||||
public static long toStartOfMonth(Instant instant) {
|
||||
return instant
|
||||
.atZone(ZONE_ID)
|
||||
.toLocalDate()
|
||||
.with(FIRST_DAY_OF_MONTH)
|
||||
.atStartOfDay(ZONE_ID)
|
||||
.toInstant()
|
||||
.getEpochSecond();
|
||||
public long toTimeInterval(Instant instant) {
|
||||
return TemporalAdjusterUtil.toTimeInterval(instant, temporalAdjuster);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -44,8 +44,6 @@ import javafx.scene.layout.VBox;
|
|||
|
||||
import javafx.geometry.Insets;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
|
||||
import static bisq.desktop.util.FormBuilder.addTopLabelReadOnlyTextField;
|
||||
|
||||
|
@ -104,6 +102,7 @@ public class SupplyView extends ActivatableView<GridPane, Void> implements DaoSt
|
|||
protected void deactivate() {
|
||||
daoEconomyChartView.removeListener(this);
|
||||
daoFacade.removeBsqStateListener(this);
|
||||
daoEconomyChartView.deactivate();
|
||||
}
|
||||
|
||||
|
||||
|
@ -115,8 +114,6 @@ public class SupplyView extends ActivatableView<GridPane, Void> implements DaoSt
|
|||
public void onDateFilterChanged(long fromDate, long toDate) {
|
||||
this.fromDate = fromDate;
|
||||
this.toDate = toDate;
|
||||
log.error(new Date(fromDate).toString());
|
||||
log.error(new Date(toDate).toString());
|
||||
updateEconomicsData();
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@ import javax.inject.Inject;
|
|||
|
||||
import javafx.scene.chart.XYChart;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
@ -47,11 +50,21 @@ public class DaoEconomyChartModel extends ChartModel {
|
|||
@Inject
|
||||
public DaoEconomyChartModel(DaoStateService daoStateService, DaoEconomyDataProvider daoEconomyDataProvider) {
|
||||
super();
|
||||
this.daoStateService = daoStateService;
|
||||
|
||||
this.daoStateService = daoStateService;
|
||||
this.daoEconomyDataProvider = daoEconomyDataProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyTemporalAdjuster(TemporalAdjuster temporalAdjuster) {
|
||||
daoEconomyDataProvider.setTemporalAdjuster(temporalAdjuster);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TemporalAdjuster getTemporalAdjuster() {
|
||||
return daoEconomyDataProvider.getTemporalAdjuster();
|
||||
}
|
||||
|
||||
List<XYChart.Data<Number, Number>> getBsqTradeFeeChartData(Predicate<Long> predicate) {
|
||||
return toChartData(daoEconomyDataProvider.getBurnedBsqByMonth(daoStateService.getTradeFeeTxs(), predicate));
|
||||
}
|
||||
|
@ -91,6 +104,10 @@ public class DaoEconomyChartModel extends ChartModel {
|
|||
upperBound = Math.max(xMinMaxTradeFee.second, xMinMaxCompensationRequest.second);
|
||||
}
|
||||
|
||||
long toTimeInterval(Instant ofEpochSecond) {
|
||||
return daoEconomyDataProvider.toTimeInterval(ofEpochSecond);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package bisq.desktop.main.dao.economy.supply.chart;
|
||||
|
||||
import bisq.desktop.components.chart.ChartView;
|
||||
import bisq.desktop.main.dao.economy.supply.DaoEconomyDataProvider;
|
||||
import bisq.desktop.util.DisplayUtils;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
|
@ -32,7 +31,6 @@ import javafx.scene.Node;
|
|||
import javafx.scene.chart.LineChart;
|
||||
import javafx.scene.chart.NumberAxis;
|
||||
import javafx.scene.chart.XYChart;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.text.Text;
|
||||
|
||||
import javafx.geometry.Side;
|
||||
|
@ -145,8 +143,8 @@ public class DaoEconomyChartView extends ChartView<DaoEconomyChartModel> {
|
|||
return new StringConverter<>() {
|
||||
@Override
|
||||
public String toString(Number epochSeconds) {
|
||||
Date date = new Date(DaoEconomyDataProvider.toStartOfMonth(Instant.ofEpochSecond(epochSeconds.longValue())) * 1000);
|
||||
return DisplayUtils.formatDateAxis(date, "dd/MMM\nyyyy");
|
||||
Date date = new Date(model.toTimeInterval(Instant.ofEpochSecond(epochSeconds.longValue())) * 1000);
|
||||
return DisplayUtils.formatDateAxis(date, dateFormatPatters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -171,6 +169,11 @@ public class DaoEconomyChartView extends ChartView<DaoEconomyChartModel> {
|
|||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTooltipValueConverter(Number value) {
|
||||
return bsqFormatter.formatBSQSatoshisWithCode(value.longValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSeries() {
|
||||
seriesTotalIssued = new XYChart.Series<>();
|
||||
|
@ -241,7 +244,9 @@ public class DaoEconomyChartView extends ChartView<DaoEconomyChartModel> {
|
|||
seriesCompensation.getData().setAll(model.getCompensationChartData(predicate));
|
||||
|
||||
updateOtherSeries(predicate);
|
||||
|
||||
applyTooltip();
|
||||
applySeriesStyles();
|
||||
}
|
||||
|
||||
private void updateOtherSeries(Predicate<Long> predicate) {
|
||||
|
@ -250,22 +255,4 @@ public class DaoEconomyChartView extends ChartView<DaoEconomyChartModel> {
|
|||
seriesTotalIssued.getData().setAll(model.getTotalIssuedChartData(predicate));
|
||||
seriesTotalBurned.getData().setAll(model.getTotalBurnedChartData(predicate));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyTooltip() {
|
||||
chart.getData().forEach(series -> {
|
||||
String format = series == seriesCompensation || series == seriesReimbursement || series == seriesTotalIssued ?
|
||||
"dd MMM yyyy" :
|
||||
"MMM yyyy";
|
||||
series.getData().forEach(data -> {
|
||||
String xValue = DisplayUtils.formatDateAxis(new Date(data.getXValue().longValue() * 1000), format);
|
||||
String yValue = bsqFormatter.formatBSQSatoshisWithCode(data.getYValue().longValue());
|
||||
Node node = data.getNode();
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
Tooltip.install(node, new Tooltip(Res.get("dao.factsAndFigures.supply.chart.tradeFee.toolTip", yValue, xValue)));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,16 +135,16 @@
|
|||
-bs-white: white;
|
||||
-bs-prompt-text: -bs-color-gray-3;
|
||||
-bs-decimals: #db6300;
|
||||
-bs-soft-red: #680000;
|
||||
-bs-turquoise-light: #BEDB39;
|
||||
-bs-soft-red: #aa4c3b;
|
||||
-bs-turquoise-light: #00dcdd;
|
||||
|
||||
/* dao chart colors */
|
||||
-bs-chart-dao-line1: -bs-color-green-5;
|
||||
-bs-chart-dao-line2: -bs-color-blue-2;
|
||||
-bs-chart-dao-line1: -bs-color-blue-5;
|
||||
-bs-chart-dao-line2: -bs-color-green-3;
|
||||
-bs-chart-dao-line3: -bs-turquoise;
|
||||
-bs-chart-dao-line4: -bs-yellow;
|
||||
-bs-chart-dao-line5: -bs-soft-red;
|
||||
-bs-chart-dao-line6: -bs-turquoise-light;
|
||||
-bs-chart-dao-line4: -bs-turquoise-light;
|
||||
-bs-chart-dao-line5: -bs-yellow;
|
||||
-bs-chart-dao-line6: -bs-soft-red;
|
||||
|
||||
/* Monero orange color code */
|
||||
-xmr-orange: #f26822;
|
||||
|
|
|
@ -102,16 +102,16 @@
|
|||
-bs-progress-bar-track: #e0e0e0;
|
||||
-bs-white: white;
|
||||
-bs-prompt-text: -fx-control-inner-background;
|
||||
-bs-soft-red: #E74C3B;
|
||||
-bs-turquoise-light: #00DCFF;
|
||||
-bs-soft-red: #aa4c3b;
|
||||
-bs-turquoise-light: #00dcdd;
|
||||
|
||||
/* dao chart colors */
|
||||
-bs-chart-dao-line1: -bs-color-green-3;
|
||||
-bs-chart-dao-line2: -bs-color-blue-5;
|
||||
-bs-chart-dao-line1: -bs-color-blue-5;
|
||||
-bs-chart-dao-line2: -bs-color-green-3;
|
||||
-bs-chart-dao-line3: -bs-turquoise;
|
||||
-bs-chart-dao-line4: -bs-yellow;
|
||||
-bs-chart-dao-line5: -bs-soft-red;
|
||||
-bs-chart-dao-line6: -bs-turquoise-light;
|
||||
-bs-chart-dao-line4: -bs-turquoise-light;
|
||||
-bs-chart-dao-line5: -bs-yellow;
|
||||
-bs-chart-dao-line6: -bs-soft-red;
|
||||
|
||||
/* Monero orange color code */
|
||||
-xmr-orange: #f26822;
|
||||
|
|
Loading…
Add table
Reference in a new issue