mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-04-19 23:36:00 -04:00
perc. price UI (WIP)
This commit is contained in:
parent
517ee371db
commit
d5118b048b
@ -106,7 +106,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||
private final long date;
|
||||
private final long protocolVersion;
|
||||
private final long fiatPrice;
|
||||
private final double percentagePrice;
|
||||
private final double percentageBasedPrice;
|
||||
private final boolean usePercentageBasedPrice;
|
||||
private final long amount;
|
||||
private final long minAmount;
|
||||
@ -140,7 +140,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||
PubKeyRing pubKeyRing,
|
||||
Direction direction,
|
||||
long fiatPrice,
|
||||
double percentagePrice,
|
||||
double percentageBasedPrice,
|
||||
boolean usePercentageBasedPrice,
|
||||
long amount,
|
||||
long minAmount,
|
||||
@ -157,7 +157,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||
this.pubKeyRing = pubKeyRing;
|
||||
this.direction = direction;
|
||||
this.fiatPrice = fiatPrice;
|
||||
this.percentagePrice = percentagePrice;
|
||||
this.percentageBasedPrice = percentageBasedPrice;
|
||||
this.usePercentageBasedPrice = usePercentageBasedPrice;
|
||||
this.amount = amount;
|
||||
this.minAmount = minAmount;
|
||||
@ -321,8 +321,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||
return Fiat.valueOf(currencyCode, fiatPrice);
|
||||
}
|
||||
|
||||
public double getPercentagePrice() {
|
||||
return percentagePrice;
|
||||
public double getPercentageBasedPrice() {
|
||||
return percentageBasedPrice;
|
||||
}
|
||||
|
||||
public boolean isUsePercentageBasedPrice() {
|
||||
@ -408,7 +408,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||
Offer offer = (Offer) o;
|
||||
if (date != offer.date) return false;
|
||||
if (fiatPrice != offer.fiatPrice) return false;
|
||||
if (Double.compare(offer.percentagePrice, percentagePrice) != 0) return false;
|
||||
if (Double.compare(offer.percentageBasedPrice, percentageBasedPrice) != 0) return false;
|
||||
if (usePercentageBasedPrice != offer.usePercentageBasedPrice) return false;
|
||||
if (amount != offer.amount) return false;
|
||||
if (minAmount != offer.minAmount) return false;
|
||||
@ -441,7 +441,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||
result = 31 * result + (currencyCode != null ? currencyCode.hashCode() : 0);
|
||||
result = 31 * result + (int) (date ^ (date >>> 32));
|
||||
result = 31 * result + (int) (fiatPrice ^ (fiatPrice >>> 32));
|
||||
long temp = Double.doubleToLongBits(percentagePrice);
|
||||
long temp = Double.doubleToLongBits(percentageBasedPrice);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
result = 31 * result + (usePercentageBasedPrice ? 1 : 0);
|
||||
result = 31 * result + (int) (amount ^ (amount >>> 32));
|
||||
@ -467,7 +467,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||
"\n\tcurrencyCode='" + currencyCode + '\'' +
|
||||
"\n\tdate=" + date +
|
||||
"\n\tfiatPrice=" + fiatPrice +
|
||||
"\n\tpercentagePrice=" + percentagePrice +
|
||||
"\n\tpercentagePrice=" + percentageBasedPrice +
|
||||
"\n\tusePercentageBasedPrice=" + usePercentageBasedPrice +
|
||||
"\n\tamount=" + amount +
|
||||
"\n\tminAmount=" + minAmount +
|
||||
|
@ -404,7 +404,7 @@ public class MainViewModel implements ViewModel {
|
||||
result = numPeersString + " / synchronized with " + btcNetworkAsString;
|
||||
btcSplashSyncIconId.set("image-connection-synced");
|
||||
} else if (percentage > 0.0) {
|
||||
result = numPeersString + " / synchronizing with " + btcNetworkAsString + ": " + formatter.formatToPercent(percentage);
|
||||
result = numPeersString + " / synchronizing with " + btcNetworkAsString + ": " + formatter.formatToPercentWithSymbol(percentage);
|
||||
} else {
|
||||
result = numPeersString + " / connecting to " + btcNetworkAsString;
|
||||
}
|
||||
|
@ -97,7 +97,6 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> minAmountAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Fiat> priceAsFiat = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Double> priceAsPercentage = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> missingCoin = new SimpleObjectProperty<>(Coin.ZERO);
|
||||
@ -110,6 +109,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
private Notification walletFundedNotification;
|
||||
boolean useSavingsWallet;
|
||||
Coin totalAvailableBalance;
|
||||
private double percentageBasedPrice = 0;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -256,7 +256,6 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
Offer createAndGetOffer() {
|
||||
long fiatPrice = priceAsFiat.get() != null ? priceAsFiat.get().getValue() : 0L;
|
||||
|
||||
double percentagePrice = 0;
|
||||
long amount = amountAsCoin.get() != null ? amountAsCoin.get().getValue() : 0L;
|
||||
long minAmount = minAmountAsCoin.get() != null ? minAmountAsCoin.get().getValue() : 0L;
|
||||
|
||||
@ -283,12 +282,15 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
String countryCode = paymentAccount instanceof CountryBasedPaymentAccount ? ((CountryBasedPaymentAccount) paymentAccount).getCountry().code : null;
|
||||
|
||||
checkNotNull(p2PService.getAddress(), "Address must not be null");
|
||||
log.error("fiatPrice " + fiatPrice);
|
||||
log.error("percentageBasedPrice " + percentageBasedPrice);
|
||||
log.error("usePercentageBasedPrice " + usePercentageBasedPrice.get());
|
||||
return new Offer(offerId,
|
||||
p2PService.getAddress(),
|
||||
keyRing.getPubKeyRing(),
|
||||
direction,
|
||||
fiatPrice,
|
||||
percentagePrice,
|
||||
percentageBasedPrice,
|
||||
usePercentageBasedPrice.get(),
|
||||
amount,
|
||||
minAmount,
|
||||
@ -494,4 +496,12 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
walletService.swapTradeEntryToAvailableEntry(offerId, AddressEntry.Context.OFFER_FUNDING);
|
||||
walletService.swapTradeEntryToAvailableEntry(offerId, AddressEntry.Context.RESERVED_FOR_TRADE);
|
||||
}
|
||||
|
||||
double getPercentageBasedPrice() {
|
||||
return percentageBasedPrice;
|
||||
}
|
||||
|
||||
void setPercentageBasedPrice(double percentageBasedPrice) {
|
||||
this.percentageBasedPrice = percentageBasedPrice;
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
|
||||
private ChangeListener<Boolean> amountFocusedListener;
|
||||
private ChangeListener<Boolean> minAmountFocusedListener;
|
||||
private ChangeListener<Boolean> priceFocusedListener;
|
||||
private ChangeListener<Boolean> priceFocusedListener, priceAsPercentageFocusedListener;
|
||||
private ChangeListener<Boolean> volumeFocusedListener;
|
||||
private ChangeListener<Boolean> showWarningInvalidBtcDecimalPlacesListener;
|
||||
private ChangeListener<Boolean> showWarningInvalidFiatDecimalPlacesPlacesListener;
|
||||
@ -286,6 +286,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
amountTextField.setMouseTransparent(true);
|
||||
minAmountTextField.setMouseTransparent(true);
|
||||
priceTextField.setMouseTransparent(true);
|
||||
priceAsPercentageTextField.setMouseTransparent(true);
|
||||
fixedPriceButton.setMouseTransparent(true);
|
||||
percentagePriceButton.setMouseTransparent(true);
|
||||
volumeTextField.setMouseTransparent(true);
|
||||
currencyComboBox.setMouseTransparent(true);
|
||||
paymentAccountsComboBox.setMouseTransparent(true);
|
||||
@ -414,7 +417,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
amountBtcLabel.textProperty().bind(model.btcCode);
|
||||
priceCurrencyLabel.textProperty().bind(createStringBinding(() -> model.tradeCurrencyCode.get() + "/" + model.btcCode.get(), model.btcCode, model.tradeCurrencyCode));
|
||||
priceTextField.disableProperty().bind(model.dataModel.usePercentageBasedPrice);
|
||||
priceCurrencyLabel.disableProperty().bind(model.dataModel.usePercentageBasedPrice);
|
||||
priceAsPercentageTextField.disableProperty().bind(model.dataModel.usePercentageBasedPrice.not());
|
||||
priceAsPercentageLabel.disableProperty().bind(model.dataModel.usePercentageBasedPrice.not());
|
||||
priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty());
|
||||
volumeCurrencyLabel.textProperty().bind(model.tradeCurrencyCode);
|
||||
minAmountBtcLabel.textProperty().bind(model.btcCode);
|
||||
@ -423,6 +428,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
amountTextField.textProperty().bindBidirectional(model.amount);
|
||||
minAmountTextField.textProperty().bindBidirectional(model.minAmount);
|
||||
priceTextField.textProperty().bindBidirectional(model.price);
|
||||
priceAsPercentageTextField.textProperty().bindBidirectional(model.priceAsPercentage);
|
||||
volumeTextField.textProperty().bindBidirectional(model.volume);
|
||||
volumeTextField.promptTextProperty().bind(model.volumePromptLabel);
|
||||
totalToPayTextField.textProperty().bind(model.totalToPay);
|
||||
@ -462,7 +468,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
amountBtcLabel.textProperty().unbind();
|
||||
priceCurrencyLabel.textProperty().unbind();
|
||||
priceTextField.disableProperty().unbind();
|
||||
priceCurrencyLabel.disableProperty().unbind();
|
||||
priceAsPercentageTextField.disableProperty().unbind();
|
||||
priceAsPercentageLabel.disableProperty().unbind();
|
||||
volumeCurrencyLabel.textProperty().unbind();
|
||||
minAmountBtcLabel.textProperty().unbind();
|
||||
priceDescriptionLabel.textProperty().unbind();
|
||||
@ -470,6 +478,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
amountTextField.textProperty().unbindBidirectional(model.amount);
|
||||
minAmountTextField.textProperty().unbindBidirectional(model.minAmount);
|
||||
priceTextField.textProperty().unbindBidirectional(model.price);
|
||||
priceAsPercentageTextField.textProperty().unbindBidirectional(model.priceAsPercentage);
|
||||
priceAsPercentageLabel.prefWidthProperty().unbind();
|
||||
volumeTextField.textProperty().unbindBidirectional(model.volume);
|
||||
volumeTextField.promptTextProperty().unbindBidirectional(model.volume);
|
||||
@ -536,6 +545,10 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
model.onFocusOutPriceTextField(oldValue, newValue, priceTextField.getText());
|
||||
priceTextField.setText(model.price.get());
|
||||
};
|
||||
priceAsPercentageFocusedListener = (o, oldValue, newValue) -> {
|
||||
model.onFocusOutPriceAsPercentageTextField(oldValue, newValue, priceAsPercentageTextField.getText());
|
||||
priceAsPercentageTextField.setText(model.priceAsPercentage.get());
|
||||
};
|
||||
volumeFocusedListener = (o, oldValue, newValue) -> {
|
||||
model.onFocusOutVolumeTextField(oldValue, newValue, volumeTextField.getText());
|
||||
volumeTextField.setText(model.volume.get());
|
||||
@ -595,6 +608,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
|
||||
tradeCurrencyCodeListener = (observable, oldValue, newValue) -> {
|
||||
priceTextField.clear();
|
||||
priceAsPercentageTextField.clear();
|
||||
volumeTextField.clear();
|
||||
};
|
||||
|
||||
@ -633,6 +647,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
amountTextField.focusedProperty().addListener(amountFocusedListener);
|
||||
minAmountTextField.focusedProperty().addListener(minAmountFocusedListener);
|
||||
priceTextField.focusedProperty().addListener(priceFocusedListener);
|
||||
priceAsPercentageTextField.focusedProperty().addListener(priceAsPercentageFocusedListener);
|
||||
volumeTextField.focusedProperty().addListener(volumeFocusedListener);
|
||||
|
||||
// warnings
|
||||
@ -656,6 +671,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
amountTextField.focusedProperty().removeListener(amountFocusedListener);
|
||||
minAmountTextField.focusedProperty().removeListener(minAmountFocusedListener);
|
||||
priceTextField.focusedProperty().removeListener(priceFocusedListener);
|
||||
priceAsPercentageTextField.focusedProperty().removeListener(priceAsPercentageFocusedListener);
|
||||
volumeTextField.focusedProperty().removeListener(volumeFocusedListener);
|
||||
|
||||
// warnings
|
||||
@ -975,7 +991,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
priceAsPercentageTextField.setPromptText("Enter % value");
|
||||
priceAsPercentageLabel.setText("% dist.");
|
||||
priceAsPercentageLabel.setStyle("-fx-alignment: center;");
|
||||
|
||||
|
||||
Tuple3<HBox, InputTextField, Label> amountValueCurrencyBoxTuple = getValueCurrencyBox(BSResources.get("createOffer.amount.prompt"));
|
||||
HBox amountValueCurrencyBox = amountValueCurrencyBoxTuple.first;
|
||||
minAmountTextField = amountValueCurrencyBoxTuple.second;
|
||||
|
@ -71,6 +71,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
final StringProperty amount = new SimpleStringProperty();
|
||||
final StringProperty minAmount = new SimpleStringProperty();
|
||||
final StringProperty price = new SimpleStringProperty();
|
||||
final StringProperty priceAsPercentage = new SimpleStringProperty();
|
||||
final StringProperty volume = new SimpleStringProperty();
|
||||
final StringProperty volumeDescriptionLabel = new SimpleStringProperty();
|
||||
final StringProperty volumePromptLabel = new SimpleStringProperty();
|
||||
@ -103,7 +104,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
|
||||
private ChangeListener<String> amountListener;
|
||||
private ChangeListener<String> minAmountListener;
|
||||
private ChangeListener<String> priceListener;
|
||||
private ChangeListener<String> priceListener, priceAsPercentageListener;
|
||||
private ChangeListener<String> volumeListener;
|
||||
private ChangeListener<Coin> amountAsCoinListener;
|
||||
private ChangeListener<Coin> minAmountAsCoinListener;
|
||||
@ -114,6 +115,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
private ChangeListener<String> errorMessageListener;
|
||||
private Offer offer;
|
||||
private Timer timeoutTimer;
|
||||
private PriceFeed.Type priceFeedType;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -235,9 +237,61 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
setPriceToModel();
|
||||
calculateVolume();
|
||||
dataModel.calculateTotalToPay();
|
||||
|
||||
MarketPrice marketPrice = priceFeed.getMarketPrice(dataModel.tradeCurrencyCode.get());
|
||||
if (marketPrice != null) {
|
||||
double marketPriceAsDouble = marketPrice.getPrice(priceFeedType);
|
||||
try {
|
||||
double priceAsDouble = formatter.parseNumberStringToDouble(price.get());
|
||||
double priceFactor = priceAsDouble / marketPriceAsDouble;
|
||||
priceFactor = dataModel.getDirection() == Offer.Direction.BUY ? 1 - priceFactor : 1 + priceFactor;
|
||||
priceAsPercentage.set(formatter.formatToPercent(priceFactor, 2));
|
||||
} catch (NumberFormatException t) {
|
||||
priceAsPercentage.set("");
|
||||
new Popup().warning("Your input is not a valid number.")
|
||||
.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
updateButtonDisableState();
|
||||
};
|
||||
priceAsPercentageListener = (ov, oldValue, newValue) -> {
|
||||
try {
|
||||
if (!newValue.isEmpty() && !newValue.equals("-")) {
|
||||
double percentageBasedPrice = formatter.parsePercentStringToDouble(newValue);
|
||||
if (percentageBasedPrice >= 1 || percentageBasedPrice <= -1) {
|
||||
dataModel.setPercentageBasedPrice(0);
|
||||
UserThread.execute(() -> priceAsPercentage.set("0"));
|
||||
new Popup().warning("You cannot set a percentage of 100% or larger. Please enter a percentage number like \"5.4\" for 5.4%")
|
||||
.show();
|
||||
} else {
|
||||
MarketPrice marketPrice = priceFeed.getMarketPrice(dataModel.tradeCurrencyCode.get());
|
||||
if (marketPrice != null) {
|
||||
percentageBasedPrice = formatter.roundDouble(percentageBasedPrice, 4);
|
||||
dataModel.setPercentageBasedPrice(percentageBasedPrice);
|
||||
double marketPriceAsDouble = marketPrice.getPrice(priceFeedType);
|
||||
double factor = dataModel.getDirection() == Offer.Direction.BUY ? 1 - percentageBasedPrice : 1 + percentageBasedPrice;
|
||||
double targetPrice = marketPriceAsDouble * factor;
|
||||
price.set(formatter.formatToNumberString(targetPrice, 2));
|
||||
setPriceToModel();
|
||||
calculateVolume();
|
||||
dataModel.calculateTotalToPay();
|
||||
updateButtonDisableState();
|
||||
} else {
|
||||
new Popup().warning("There is no price feed available for that currency. You cannot use percent based price.")
|
||||
.show();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dataModel.setPercentageBasedPrice(0);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
dataModel.setPercentageBasedPrice(0);
|
||||
UserThread.execute(() -> priceAsPercentage.set("0"));
|
||||
new Popup().warning("Your input is not a valid number. Please enter a percentage number like \"5.4\" for 5.4%")
|
||||
.show();
|
||||
}
|
||||
};
|
||||
volumeListener = (ov, oldValue, newValue) -> {
|
||||
if (isFiatInputValid(newValue).isValid) {
|
||||
setVolumeToModel();
|
||||
@ -266,6 +320,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
amount.addListener(amountListener);
|
||||
minAmount.addListener(minAmountListener);
|
||||
price.addListener(priceListener);
|
||||
priceAsPercentage.addListener(priceAsPercentageListener);
|
||||
volume.addListener(volumeListener);
|
||||
|
||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||
@ -282,6 +337,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
amount.removeListener(amountListener);
|
||||
minAmount.removeListener(minAmountListener);
|
||||
price.removeListener(priceListener);
|
||||
priceAsPercentage.removeListener(priceAsPercentageListener);
|
||||
volume.removeListener(volumeListener);
|
||||
|
||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||
@ -307,6 +363,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
if (dataModel.paymentAccount != null)
|
||||
btcValidator.setMaxTradeLimitInBitcoin(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimit());
|
||||
|
||||
priceFeedType = direction == Offer.Direction.BUY ? PriceFeed.Type.ASK : PriceFeed.Type.BID;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -464,6 +522,12 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
}
|
||||
}
|
||||
|
||||
void onFocusOutPriceAsPercentageTextField(boolean oldValue, boolean newValue, String userInput) {
|
||||
if (oldValue && !newValue) {
|
||||
priceAsPercentage.set(formatter.formatToNumberString(dataModel.getPercentageBasedPrice() * 100, 2));
|
||||
}
|
||||
}
|
||||
|
||||
void onFocusOutVolumeTextField(boolean oldValue, boolean newValue, String userInput) {
|
||||
if (oldValue && !newValue) {
|
||||
InputValidator.ValidationResult result = isFiatInputValid(volume.get());
|
||||
@ -492,21 +556,12 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
public boolean isPriceInRange() {
|
||||
MarketPrice marketPrice = priceFeed.getMarketPrice(getTradeCurrency().getCode());
|
||||
if (marketPrice != null) {
|
||||
double marketPriceAsDouble = marketPrice.getPrice(PriceFeed.Type.LAST);
|
||||
double marketPriceAsDouble = marketPrice.getPrice(priceFeedType);
|
||||
Fiat priceAsFiat = dataModel.priceAsFiat.get();
|
||||
long shiftDivisor = checkedPow(10, priceAsFiat.smallestUnitExponent());
|
||||
double offerPrice = ((double) priceAsFiat.longValue()) / ((double) shiftDivisor);
|
||||
if (marketPriceAsDouble != 0 && Math.abs(1 - (offerPrice / marketPriceAsDouble)) > preferences.getMaxPriceDistanceInPercent()) {
|
||||
Popup popup = new Popup();
|
||||
popup.warning("The price you have entered is outside the max. allowed deviation from the market price.\n" +
|
||||
"The max. allowed deviation is " +
|
||||
formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent()) +
|
||||
" and can be adjusted in the preferences.")
|
||||
.actionButtonText("Change price")
|
||||
.onAction(() -> popup.hide())
|
||||
.closeButtonText("Go to \"Preferences\"")
|
||||
.onClose(() -> navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class))
|
||||
.show();
|
||||
displayPriceOutofRangePopup();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
@ -516,6 +571,19 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
}
|
||||
}
|
||||
|
||||
private void displayPriceOutofRangePopup() {
|
||||
Popup popup = new Popup();
|
||||
popup.warning("The price you have entered is outside the max. allowed deviation from the market price.\n" +
|
||||
"The max. allowed deviation is " +
|
||||
formatter.formatToPercentWithSymbol(preferences.getMaxPriceDistanceInPercent()) +
|
||||
" and can be adjusted in the preferences.")
|
||||
.actionButtonText("Change price")
|
||||
.onAction(() -> popup.hide())
|
||||
.closeButtonText("Go to \"Preferences\"")
|
||||
.onClose(() -> navigation.navigateTo(MainView.class, SettingsView.class, PreferencesView.class))
|
||||
.show();
|
||||
}
|
||||
|
||||
BSFormatter getFormatter() {
|
||||
return formatter;
|
||||
}
|
||||
|
@ -296,19 +296,16 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
|
||||
|
||||
deviationListener = (observable, oldValue, newValue) -> {
|
||||
try {
|
||||
String input = newValue.replace("%", "");
|
||||
input = input.replace(",", ".");
|
||||
input = input.replace(" ", "");
|
||||
double value = Double.parseDouble(input);
|
||||
preferences.setMaxPriceDistanceInPercent(value / 100);
|
||||
} catch (Throwable t) {
|
||||
double value = formatter.parsePercentStringToDouble(newValue);
|
||||
preferences.setMaxPriceDistanceInPercent(value);
|
||||
} catch (NumberFormatException t) {
|
||||
log.error("Exception at parseDouble deviation: " + t.toString());
|
||||
UserThread.runAfter(() -> deviationInputTextField.setText(formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent())), 100, TimeUnit.MILLISECONDS);
|
||||
UserThread.runAfter(() -> deviationInputTextField.setText(formatter.formatToPercentWithSymbol(preferences.getMaxPriceDistanceInPercent())), 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
};
|
||||
deviationFocusedListener = (observable1, oldValue1, newValue1) -> {
|
||||
if (oldValue1 && !newValue1)
|
||||
UserThread.runAfter(() -> deviationInputTextField.setText(formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent())), 100, TimeUnit.MILLISECONDS);
|
||||
UserThread.runAfter(() -> deviationInputTextField.setText(formatter.formatToPercentWithSymbol(preferences.getMaxPriceDistanceInPercent())), 100, TimeUnit.MILLISECONDS);
|
||||
};
|
||||
|
||||
transactionFeeInputTextField = addLabelInputTextField(root, ++gridRow, "Withdrawal transaction fee (satoshi/byte):").second;
|
||||
@ -427,7 +424,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
|
||||
});
|
||||
blockChainExplorerComboBox.setOnAction(e -> preferences.setBlockChainExplorer(blockChainExplorerComboBox.getSelectionModel().getSelectedItem()));
|
||||
|
||||
deviationInputTextField.setText(formatter.formatToPercent(preferences.getMaxPriceDistanceInPercent()));
|
||||
deviationInputTextField.setText(formatter.formatToPercentWithSymbol(preferences.getMaxPriceDistanceInPercent()));
|
||||
deviationInputTextField.textProperty().addListener(deviationListener);
|
||||
deviationInputTextField.focusedProperty().addListener(deviationFocusedListener);
|
||||
|
||||
|
@ -325,11 +325,58 @@ public class BSFormatter {
|
||||
}
|
||||
|
||||
public String formatToPercent(double value) {
|
||||
return formatToPercent(value, 1);
|
||||
}
|
||||
|
||||
public String formatToPercent(double value, int digits) {
|
||||
DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(locale);
|
||||
decimalFormat.setMinimumFractionDigits(1);
|
||||
decimalFormat.setMaximumFractionDigits(1);
|
||||
decimalFormat.setMinimumFractionDigits(digits);
|
||||
decimalFormat.setMaximumFractionDigits(digits);
|
||||
decimalFormat.setGroupingUsed(false);
|
||||
return decimalFormat.format(value * 100.0) + " %";
|
||||
return decimalFormat.format(value * 100.0);
|
||||
}
|
||||
|
||||
public String formatToNumberString(double value, int digits) {
|
||||
DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(locale);
|
||||
decimalFormat.setMinimumFractionDigits(digits);
|
||||
decimalFormat.setMaximumFractionDigits(digits);
|
||||
decimalFormat.setGroupingUsed(false);
|
||||
return decimalFormat.format(value);
|
||||
}
|
||||
|
||||
public double parseNumberStringToDouble(String percentString) throws NumberFormatException {
|
||||
try {
|
||||
String input = percentString.replace(",", ".");
|
||||
input = input.replace(" ", "");
|
||||
return Double.parseDouble(input);
|
||||
} catch (NumberFormatException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public String formatToPercentWithSymbol(double value) {
|
||||
return formatToPercent(value) + " %";
|
||||
}
|
||||
|
||||
public double parsePercentStringToDouble(String percentString) throws NumberFormatException {
|
||||
try {
|
||||
String input = percentString.replace("%", "");
|
||||
input = input.replace(",", ".");
|
||||
input = input.replace(" ", "");
|
||||
double value = Double.parseDouble(input);
|
||||
return value / 100;
|
||||
} catch (NumberFormatException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public double roundDouble(double value, int places) {
|
||||
if (places < 0) throw new IllegalArgumentException();
|
||||
|
||||
long factor = (long) Math.pow(10, places);
|
||||
value = value * factor;
|
||||
long tmp = Math.round(value);
|
||||
return (double) tmp / factor;
|
||||
}
|
||||
|
||||
private String cleanInput(String input) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user