Use percentage based price as default, swap input text controls when toggle between fixed price and percentage based price. Hide percentage based inputs if no market price is available.

This commit is contained in:
Manfred Karrer 2017-02-20 11:02:54 -05:00
parent 3777a65b2f
commit 78a8e1ba39
4 changed files with 135 additions and 61 deletions

View File

@ -129,7 +129,7 @@ public final class Preferences implements Persistable {
private boolean useStickyMarketPrice = false; private boolean useStickyMarketPrice = false;
private boolean sortMarketCurrenciesNumerically = true; private boolean sortMarketCurrenciesNumerically = true;
private boolean usePercentageBasedPrice = false; private boolean usePercentageBasedPrice = true;
private Map<String, String> peerTagMap = new HashMap<>(); private Map<String, String> peerTagMap = new HashMap<>();
private String bitcoinNodes = ""; private String bitcoinNodes = "";

View File

@ -612,10 +612,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
void updateTradeFee() { void updateTradeFee() {
createOfferFeeAsCoin = Utilities.getFeePerBtc(feeService.getCreateOfferFeeInBtcPerBtc(), amount.get()); createOfferFeeAsCoin = Utilities.getFeePerBtc(feeService.getCreateOfferFeeInBtcPerBtc(), amount.get());
// We don't want too fractional btc values so we use only a divide by 10 instead of 100 // We don't want too fractional btc values so we use only a divide by 10 instead of 100
createOfferFeeAsCoin = createOfferFeeAsCoin.divide(10).multiply(Math.round(marketPriceMargin * 1_000)); createOfferFeeAsCoin = createOfferFeeAsCoin.divide(10).multiply(Math.round(marketPriceMargin * 1_000));
createOfferFeeAsCoin = Utilities.maxCoin(createOfferFeeAsCoin, feeService.getMinCreateOfferFeeInBtc()); createOfferFeeAsCoin = Utilities.maxCoin(createOfferFeeAsCoin, feeService.getMinCreateOfferFeeInBtc());
} }
} }

View File

@ -129,6 +129,12 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private List<Node> editOfferElements = new ArrayList<>(); private List<Node> editOfferElements = new ArrayList<>();
private boolean isActivated; private boolean isActivated;
private Label xLabel; private Label xLabel;
private VBox fixedPriceBox;
private VBox percentagePriceBox;
private HBox secondRowHBox;
private HBox firstRowHBox;
private HBox toggleButtonsHBox;
private ChangeListener<Number> marketPriceAvailableListener;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -190,9 +196,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
if (waitingForFundsBusyAnimation != null) if (waitingForFundsBusyAnimation != null)
waitingForFundsBusyAnimation.play(); waitingForFundsBusyAnimation.play();
useMarketBasedPriceButton.setSelected(model.dataModel.useMarketBasedPrice.get());
fixedPriceButton.setSelected(!model.dataModel.useMarketBasedPrice.get());
directionLabel.setText(model.getDirectionLabel()); directionLabel.setText(model.getDirectionLabel());
amountDescriptionLabel.setText(model.getAmountDescription()); amountDescriptionLabel.setText(model.getAmountDescription());
addressTextField.setAddress(model.getAddressAsString()); addressTextField.setAddress(model.getAddressAsString());
@ -437,10 +440,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private void addBindings() { private void addBindings() {
amountBtcLabel.textProperty().bind(model.btcCode); amountBtcLabel.textProperty().bind(model.btcCode);
priceCurrencyLabel.textProperty().bind(createStringBinding(() -> formatter.getCounterCurrency(model.tradeCurrencyCode.get()), model.btcCode, model.tradeCurrencyCode)); priceCurrencyLabel.textProperty().bind(createStringBinding(() -> formatter.getCounterCurrency(model.tradeCurrencyCode.get()), model.btcCode, model.tradeCurrencyCode));
fixedPriceTextField.disableProperty().bind(model.dataModel.useMarketBasedPrice);
priceCurrencyLabel.disableProperty().bind(model.dataModel.useMarketBasedPrice);
marketBasedPriceTextField.disableProperty().bind(model.dataModel.useMarketBasedPrice.not());
marketBasedPriceLabel.disableProperty().bind(model.dataModel.useMarketBasedPrice.not());
marketBasedPriceLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty()); marketBasedPriceLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty());
volumeCurrencyLabel.textProperty().bind(model.tradeCurrencyCode); volumeCurrencyLabel.textProperty().bind(model.tradeCurrencyCode);
minAmountBtcLabel.textProperty().bind(model.btcCode); minAmountBtcLabel.textProperty().bind(model.btcCode);
@ -645,10 +645,24 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
} }
} }
}; };
marketPriceAvailableListener = (observable, oldValue, newValue) -> {
if (newValue.intValue() > -1) {
boolean isMarketPriceAvailable = newValue.intValue() == 1;
percentagePriceBox.setVisible(isMarketPriceAvailable);
percentagePriceBox.setManaged(isMarketPriceAvailable);
toggleButtonsHBox.setVisible(isMarketPriceAvailable);
toggleButtonsHBox.setManaged(isMarketPriceAvailable);
boolean fixedPriceSelected = !model.dataModel.useMarketBasedPrice.get() || !isMarketPriceAvailable;
updateToggleButtons(fixedPriceSelected);
}
};
} }
private void addListeners() { private void addListeners() {
model.tradeCurrencyCode.addListener(tradeCurrencyCodeListener); model.tradeCurrencyCode.addListener(tradeCurrencyCodeListener);
model.marketPriceAvailableProperty.addListener(marketPriceAvailableListener);
// focus out // focus out
amountTextField.focusedProperty().addListener(amountFocusedListener); amountTextField.focusedProperty().addListener(amountFocusedListener);
@ -670,6 +684,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private void removeListeners() { private void removeListeners() {
model.tradeCurrencyCode.removeListener(tradeCurrencyCodeListener); model.tradeCurrencyCode.removeListener(tradeCurrencyCodeListener);
model.marketPriceAvailableProperty.removeListener(marketPriceAvailableListener);
// focus out // focus out
amountTextField.focusedProperty().removeListener(amountFocusedListener); amountTextField.focusedProperty().removeListener(amountFocusedListener);
@ -944,17 +959,16 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
xLabel.setMinWidth(14); xLabel.setMinWidth(14);
xLabel.setMaxWidth(14); xLabel.setMaxWidth(14);
// price as fiat // price as percent
Tuple3<HBox, InputTextField, Label> priceValueCurrencyBoxTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.price.prompt")); Tuple3<HBox, InputTextField, Label> priceAsPercentageTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.price.prompt"));
HBox priceValueCurrencyBox = priceValueCurrencyBoxTuple.first; HBox priceAsPercentageValueCurrencyBox = priceAsPercentageTuple.first;
fixedPriceTextField = priceValueCurrencyBoxTuple.second; marketBasedPriceTextField = priceAsPercentageTuple.second;
editOfferElements.add(fixedPriceTextField); editOfferElements.add(marketBasedPriceTextField);
priceCurrencyLabel = priceValueCurrencyBoxTuple.third; marketBasedPriceLabel = priceAsPercentageTuple.third;
editOfferElements.add(priceCurrencyLabel); editOfferElements.add(marketBasedPriceLabel);
Tuple2<Label, VBox> priceInputBoxTuple = getTradeInputBox(priceValueCurrencyBox, ""); Tuple2<Label, VBox> priceAsPercentageInputBoxTuple = getTradeInputBox(priceAsPercentageValueCurrencyBox, "Distance in % from market price");
priceDescriptionLabel = priceInputBoxTuple.first; priceAsPercentageInputBoxTuple.first.setPrefWidth(200);
editOfferElements.add(priceDescriptionLabel); percentagePriceBox = priceAsPercentageInputBoxTuple.second;
VBox priceBox = priceInputBoxTuple.second;
// Fixed/Percentage toggle // Fixed/Percentage toggle
ToggleGroup toggleGroup = new ToggleGroup(); ToggleGroup toggleGroup = new ToggleGroup();
@ -963,8 +977,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
fixedPriceButton.setId("toggle-price-left"); fixedPriceButton.setId("toggle-price-left");
fixedPriceButton.setToggleGroup(toggleGroup); fixedPriceButton.setToggleGroup(toggleGroup);
fixedPriceButton.selectedProperty().addListener((ov, oldValue, newValue) -> { fixedPriceButton.selectedProperty().addListener((ov, oldValue, newValue) -> {
model.dataModel.setUseMarketBasedPrice(!newValue); updateToggleButtons(newValue);
useMarketBasedPriceButton.setSelected(!newValue);
}); });
useMarketBasedPriceButton = new ToggleButton("Percentage"); useMarketBasedPriceButton = new ToggleButton("Percentage");
@ -972,13 +985,12 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
useMarketBasedPriceButton.setId("toggle-price-right"); useMarketBasedPriceButton.setId("toggle-price-right");
useMarketBasedPriceButton.setToggleGroup(toggleGroup); useMarketBasedPriceButton.setToggleGroup(toggleGroup);
useMarketBasedPriceButton.selectedProperty().addListener((ov, oldValue, newValue) -> { useMarketBasedPriceButton.selectedProperty().addListener((ov, oldValue, newValue) -> {
model.dataModel.setUseMarketBasedPrice(newValue); updateToggleButtons(!newValue);
fixedPriceButton.setSelected(!newValue);
}); });
HBox toggleButtons = new HBox(); toggleButtonsHBox = new HBox();
toggleButtons.setPadding(new Insets(18, 0, 0, 0)); toggleButtonsHBox.setPadding(new Insets(18, 0, 0, 0));
toggleButtons.getChildren().addAll(fixedPriceButton, useMarketBasedPriceButton); toggleButtonsHBox.getChildren().addAll(fixedPriceButton, useMarketBasedPriceButton);
// = // =
Label resultLabel = new Label("="); Label resultLabel = new Label("=");
@ -997,28 +1009,69 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
editOfferElements.add(volumeDescriptionLabel); editOfferElements.add(volumeDescriptionLabel);
VBox volumeBox = volumeInputBoxTuple.second; VBox volumeBox = volumeInputBoxTuple.second;
HBox hBox = new HBox(); firstRowHBox = new HBox();
hBox.setSpacing(5); firstRowHBox.setSpacing(5);
hBox.setAlignment(Pos.CENTER_LEFT); firstRowHBox.setAlignment(Pos.CENTER_LEFT);
hBox.getChildren().addAll(amountBox, xLabel, priceBox, toggleButtons, resultLabel, volumeBox); firstRowHBox.getChildren().addAll(amountBox, xLabel, percentagePriceBox, toggleButtonsHBox, resultLabel, volumeBox);
GridPane.setRowIndex(hBox, gridRow); GridPane.setRowIndex(firstRowHBox, gridRow);
GridPane.setColumnIndex(hBox, 1); GridPane.setColumnIndex(firstRowHBox, 1);
GridPane.setMargin(hBox, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 10, 0, 0)); GridPane.setMargin(firstRowHBox, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE, 10, 0, 0));
GridPane.setColumnSpan(hBox, 2); GridPane.setColumnSpan(firstRowHBox, 2);
gridPane.getChildren().add(hBox); gridPane.getChildren().add(firstRowHBox);
}
private void updateToggleButtons(boolean fixedPriceSelected) {
int marketPriceAvailable = model.marketPriceAvailableProperty.get();
fixedPriceSelected |= marketPriceAvailable == 0;
if (marketPriceAvailable == 1) {
model.dataModel.setUseMarketBasedPrice(!fixedPriceSelected);
if (!fixedPriceButton.isSelected() && fixedPriceSelected)
fixedPriceButton.setSelected(fixedPriceSelected);
if (useMarketBasedPriceButton.isSelected() && !fixedPriceSelected)
useMarketBasedPriceButton.setSelected(!fixedPriceSelected);
}
fixedPriceButton.setMouseTransparent(fixedPriceSelected);
useMarketBasedPriceButton.setMouseTransparent(!fixedPriceSelected);
fixedPriceButton.setStyle(fixedPriceSelected ? "-fx-background-color: -bs-blue-transparent" : "-fx-background-color: -bs-very-light-grey");
useMarketBasedPriceButton.setStyle(!fixedPriceSelected ? "-fx-background-color: -bs-blue-transparent" : "-fx-background-color: -bs-very-light-grey");
if (fixedPriceSelected) {
if (firstRowHBox.getChildren().contains(percentagePriceBox))
firstRowHBox.getChildren().remove(percentagePriceBox);
if (secondRowHBox.getChildren().contains(fixedPriceBox))
secondRowHBox.getChildren().remove(fixedPriceBox);
if (!firstRowHBox.getChildren().contains(fixedPriceBox))
firstRowHBox.getChildren().add(2, fixedPriceBox);
if (!secondRowHBox.getChildren().contains(percentagePriceBox))
secondRowHBox.getChildren().add(percentagePriceBox);
} else {
if (firstRowHBox.getChildren().contains(fixedPriceBox))
firstRowHBox.getChildren().remove(fixedPriceBox);
if (secondRowHBox.getChildren().contains(percentagePriceBox))
secondRowHBox.getChildren().remove(percentagePriceBox);
if (!firstRowHBox.getChildren().contains(percentagePriceBox))
firstRowHBox.getChildren().add(2, percentagePriceBox);
if (!secondRowHBox.getChildren().contains(fixedPriceBox))
secondRowHBox.getChildren().add(fixedPriceBox);
}
} }
private void addSecondRow() { private void addSecondRow() {
Tuple3<HBox, InputTextField, Label> priceAsPercentageTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.price.prompt")); // price as fiat
HBox priceAsPercentageValueCurrencyBox = priceAsPercentageTuple.first; Tuple3<HBox, InputTextField, Label> priceValueCurrencyBoxTuple = FormBuilder.getValueCurrencyBox(BSResources.get("createOffer.price.prompt"));
marketBasedPriceTextField = priceAsPercentageTuple.second; HBox priceValueCurrencyBox = priceValueCurrencyBoxTuple.first;
editOfferElements.add(marketBasedPriceTextField); fixedPriceTextField = priceValueCurrencyBoxTuple.second;
marketBasedPriceLabel = priceAsPercentageTuple.third; editOfferElements.add(fixedPriceTextField);
editOfferElements.add(marketBasedPriceLabel); priceCurrencyLabel = priceValueCurrencyBoxTuple.third;
editOfferElements.add(priceCurrencyLabel);
Tuple2<Label, VBox> priceAsPercentageInputBoxTuple = getTradeInputBox(priceAsPercentageValueCurrencyBox, "Distance in % from market price"); Tuple2<Label, VBox> priceInputBoxTuple = getTradeInputBox(priceValueCurrencyBox, "");
priceAsPercentageInputBoxTuple.first.setPrefWidth(200); priceDescriptionLabel = priceInputBoxTuple.first;
VBox priceAsPercentageInputBox = priceAsPercentageInputBoxTuple.second; editOfferElements.add(priceDescriptionLabel);
fixedPriceBox = priceInputBoxTuple.second;
marketBasedPriceTextField.setPromptText("Enter % value"); marketBasedPriceTextField.setPromptText("Enter % value");
marketBasedPriceLabel.setText("%"); marketBasedPriceLabel.setText("%");
@ -1039,15 +1092,15 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
xLabel.setPadding(new Insets(14, 3, 0, 3)); xLabel.setPadding(new Insets(14, 3, 0, 3));
xLabel.setVisible(false); // we just use it to get the same layout as the upper row xLabel.setVisible(false); // we just use it to get the same layout as the upper row
HBox hBox = new HBox(); secondRowHBox = new HBox();
hBox.setSpacing(5); secondRowHBox.setSpacing(5);
hBox.setAlignment(Pos.CENTER_LEFT); secondRowHBox.setAlignment(Pos.CENTER_LEFT);
hBox.getChildren().addAll(amountInputBoxTuple.second, xLabel, priceAsPercentageInputBox); secondRowHBox.getChildren().addAll(amountInputBoxTuple.second, xLabel, fixedPriceBox);
GridPane.setRowIndex(hBox, ++gridRow); GridPane.setRowIndex(secondRowHBox, ++gridRow);
GridPane.setColumnIndex(hBox, 1); GridPane.setColumnIndex(secondRowHBox, 1);
GridPane.setMargin(hBox, new Insets(5, 10, 5, 0)); GridPane.setMargin(secondRowHBox, new Insets(5, 10, 5, 0));
GridPane.setColumnSpan(hBox, 2); GridPane.setColumnSpan(secondRowHBox, 2);
gridPane.getChildren().add(hBox); gridPane.getChildren().add(secondRowHBox);
} }

View File

@ -126,6 +126,9 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
private boolean inputIsMarketBasedPrice; private boolean inputIsMarketBasedPrice;
private ChangeListener<Boolean> useMarketBasedPriceListener; private ChangeListener<Boolean> useMarketBasedPriceListener;
private boolean ignorePriceStringListener, ignoreVolumeStringListener, ignoreAmountStringListener; private boolean ignorePriceStringListener, ignoreVolumeStringListener, ignoreAmountStringListener;
private MarketPrice marketPrice;
final IntegerProperty marketPriceAvailableProperty = new SimpleIntegerProperty(-1);
private ChangeListener<Number> currenciesUpdateListener;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -161,7 +164,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
UserThread.runAfter(() -> { UserThread.runAfter(() -> {
amount.set("1"); amount.set("1");
minAmount.set(amount.get()); minAmount.set(amount.get());
price.set("1000"); UserThread.runAfter(() -> {
price.set("1000");
onFocusOutPriceAsPercentageTextField(true, false, "");
}, 1);
setAmountToModel(); setAmountToModel();
setMinAmountToModel(); setMinAmountToModel();
@ -247,6 +253,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
updateButtonDisableState(); updateButtonDisableState();
}; };
priceStringListener = (ov, oldValue, newValue) -> { priceStringListener = (ov, oldValue, newValue) -> {
final String currencyCode = dataModel.tradeCurrencyCode.get();
marketPrice = priceFeedService.getMarketPrice(currencyCode);
marketPriceAvailableProperty.set(marketPrice == null ? 0 : 1);
if (!ignorePriceStringListener) { if (!ignorePriceStringListener) {
if (isPriceInputValid(newValue).isValid) { if (isPriceInputValid(newValue).isValid) {
setPriceToModel(); setPriceToModel();
@ -254,8 +264,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
dataModel.calculateTotalToPay(); dataModel.calculateTotalToPay();
if (!inputIsMarketBasedPrice) { if (!inputIsMarketBasedPrice) {
final String currencyCode = dataModel.tradeCurrencyCode.get();
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
if (marketPrice != null) { if (marketPrice != null) {
double marketPriceAsDouble = marketPrice.getPrice(getPriceFeedType()); double marketPriceAsDouble = marketPrice.getPrice(getPriceFeedType());
try { try {
@ -280,8 +288,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
} }
} }
} }
updateButtonDisableState();
} }
updateButtonDisableState();
}; };
marketPriceMarginStringListener = (ov, oldValue, newValue) -> { marketPriceMarginStringListener = (ov, oldValue, newValue) -> {
if (inputIsMarketBasedPrice) { if (inputIsMarketBasedPrice) {
@ -298,7 +306,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
percentage = MathUtils.roundDouble(percentage, 4); percentage = MathUtils.roundDouble(percentage, 4);
dataModel.setMarketPriceMargin(percentage); dataModel.setMarketPriceMargin(percentage);
dataModel.updateTradeFee(); dataModel.updateTradeFee();
double marketPriceAsDouble = marketPrice.getPrice(getPriceFeedType()); double marketPriceAsDouble = marketPrice.getPrice(getPriceFeedType());
double factor; double factor;
if (CurrencyUtil.isCryptoCurrency(currencyCode)) if (CurrencyUtil.isCryptoCurrency(currencyCode))
@ -379,6 +387,13 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
/* feeFromFundingTxListener = (ov, oldValue, newValue) -> { /* feeFromFundingTxListener = (ov, oldValue, newValue) -> {
updateButtonDisableState(); updateButtonDisableState();
};*/ };*/
currenciesUpdateListener = (observable, oldValue, newValue) -> {
final String currencyCode = dataModel.tradeCurrencyCode.get();
marketPrice = priceFeedService.getMarketPrice(currencyCode);
marketPriceAvailableProperty.set(marketPrice == null ? 0 : 1);
updateButtonDisableState();
};
} }
private void addListeners() { private void addListeners() {
@ -399,6 +414,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
// dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener); // dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
dataModel.isWalletFunded.addListener(isWalletFundedListener); dataModel.isWalletFunded.addListener(isWalletFundedListener);
priceFeedService.currenciesUpdateFlagProperty().addListener(currenciesUpdateListener);
} }
private void removeListeners() { private void removeListeners() {
@ -420,6 +437,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
if (offer != null && errorMessageListener != null) if (offer != null && errorMessageListener != null)
offer.errorMessageProperty().removeListener(errorMessageListener); offer.errorMessageProperty().removeListener(errorMessageListener);
priceFeedService.currenciesUpdateFlagProperty().removeListener(currenciesUpdateListener);
} }
@ -497,6 +516,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
public void onCurrencySelected(TradeCurrency tradeCurrency) { public void onCurrencySelected(TradeCurrency tradeCurrency) {
dataModel.onCurrencySelected(tradeCurrency); dataModel.onCurrencySelected(tradeCurrency);
marketPrice = priceFeedService.getMarketPrice(dataModel.tradeCurrencyCode.get());
marketPriceAvailableProperty.set(marketPrice == null ? 0 : 1);
updateButtonDisableState();
} }
void onShowPayFundsScreen() { void onShowPayFundsScreen() {