mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-08-02 11:46:11 -04:00
perc. price UI (WIP)
This commit is contained in:
parent
517ee371db
commit
d5118b048b
7 changed files with 176 additions and 38 deletions
|
@ -106,7 +106,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||||
private final long date;
|
private final long date;
|
||||||
private final long protocolVersion;
|
private final long protocolVersion;
|
||||||
private final long fiatPrice;
|
private final long fiatPrice;
|
||||||
private final double percentagePrice;
|
private final double percentageBasedPrice;
|
||||||
private final boolean usePercentageBasedPrice;
|
private final boolean usePercentageBasedPrice;
|
||||||
private final long amount;
|
private final long amount;
|
||||||
private final long minAmount;
|
private final long minAmount;
|
||||||
|
@ -140,7 +140,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||||
PubKeyRing pubKeyRing,
|
PubKeyRing pubKeyRing,
|
||||||
Direction direction,
|
Direction direction,
|
||||||
long fiatPrice,
|
long fiatPrice,
|
||||||
double percentagePrice,
|
double percentageBasedPrice,
|
||||||
boolean usePercentageBasedPrice,
|
boolean usePercentageBasedPrice,
|
||||||
long amount,
|
long amount,
|
||||||
long minAmount,
|
long minAmount,
|
||||||
|
@ -157,7 +157,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||||
this.pubKeyRing = pubKeyRing;
|
this.pubKeyRing = pubKeyRing;
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
this.fiatPrice = fiatPrice;
|
this.fiatPrice = fiatPrice;
|
||||||
this.percentagePrice = percentagePrice;
|
this.percentageBasedPrice = percentageBasedPrice;
|
||||||
this.usePercentageBasedPrice = usePercentageBasedPrice;
|
this.usePercentageBasedPrice = usePercentageBasedPrice;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.minAmount = minAmount;
|
this.minAmount = minAmount;
|
||||||
|
@ -321,8 +321,8 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||||
return Fiat.valueOf(currencyCode, fiatPrice);
|
return Fiat.valueOf(currencyCode, fiatPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getPercentagePrice() {
|
public double getPercentageBasedPrice() {
|
||||||
return percentagePrice;
|
return percentageBasedPrice;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUsePercentageBasedPrice() {
|
public boolean isUsePercentageBasedPrice() {
|
||||||
|
@ -408,7 +408,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||||
Offer offer = (Offer) o;
|
Offer offer = (Offer) o;
|
||||||
if (date != offer.date) return false;
|
if (date != offer.date) return false;
|
||||||
if (fiatPrice != offer.fiatPrice) 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 (usePercentageBasedPrice != offer.usePercentageBasedPrice) return false;
|
||||||
if (amount != offer.amount) return false;
|
if (amount != offer.amount) return false;
|
||||||
if (minAmount != offer.minAmount) 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 + (currencyCode != null ? currencyCode.hashCode() : 0);
|
||||||
result = 31 * result + (int) (date ^ (date >>> 32));
|
result = 31 * result + (int) (date ^ (date >>> 32));
|
||||||
result = 31 * result + (int) (fiatPrice ^ (fiatPrice >>> 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 + (int) (temp ^ (temp >>> 32));
|
||||||
result = 31 * result + (usePercentageBasedPrice ? 1 : 0);
|
result = 31 * result + (usePercentageBasedPrice ? 1 : 0);
|
||||||
result = 31 * result + (int) (amount ^ (amount >>> 32));
|
result = 31 * result + (int) (amount ^ (amount >>> 32));
|
||||||
|
@ -467,7 +467,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||||
"\n\tcurrencyCode='" + currencyCode + '\'' +
|
"\n\tcurrencyCode='" + currencyCode + '\'' +
|
||||||
"\n\tdate=" + date +
|
"\n\tdate=" + date +
|
||||||
"\n\tfiatPrice=" + fiatPrice +
|
"\n\tfiatPrice=" + fiatPrice +
|
||||||
"\n\tpercentagePrice=" + percentagePrice +
|
"\n\tpercentagePrice=" + percentageBasedPrice +
|
||||||
"\n\tusePercentageBasedPrice=" + usePercentageBasedPrice +
|
"\n\tusePercentageBasedPrice=" + usePercentageBasedPrice +
|
||||||
"\n\tamount=" + amount +
|
"\n\tamount=" + amount +
|
||||||
"\n\tminAmount=" + minAmount +
|
"\n\tminAmount=" + minAmount +
|
||||||
|
|
|
@ -404,7 +404,7 @@ public class MainViewModel implements ViewModel {
|
||||||
result = numPeersString + " / synchronized with " + btcNetworkAsString;
|
result = numPeersString + " / synchronized with " + btcNetworkAsString;
|
||||||
btcSplashSyncIconId.set("image-connection-synced");
|
btcSplashSyncIconId.set("image-connection-synced");
|
||||||
} else if (percentage > 0.0) {
|
} else if (percentage > 0.0) {
|
||||||
result = numPeersString + " / synchronizing with " + btcNetworkAsString + ": " + formatter.formatToPercent(percentage);
|
result = numPeersString + " / synchronizing with " + btcNetworkAsString + ": " + formatter.formatToPercentWithSymbol(percentage);
|
||||||
} else {
|
} else {
|
||||||
result = numPeersString + " / connecting to " + btcNetworkAsString;
|
result = numPeersString + " / connecting to " + btcNetworkAsString;
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,6 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||||
final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>();
|
final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Coin> minAmountAsCoin = new SimpleObjectProperty<>();
|
final ObjectProperty<Coin> minAmountAsCoin = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Fiat> priceAsFiat = new SimpleObjectProperty<>();
|
final ObjectProperty<Fiat> priceAsFiat = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Double> priceAsPercentage = new SimpleObjectProperty<>();
|
|
||||||
final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>();
|
final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Coin> missingCoin = new SimpleObjectProperty<>(Coin.ZERO);
|
final ObjectProperty<Coin> missingCoin = new SimpleObjectProperty<>(Coin.ZERO);
|
||||||
|
@ -110,6 +109,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||||
private Notification walletFundedNotification;
|
private Notification walletFundedNotification;
|
||||||
boolean useSavingsWallet;
|
boolean useSavingsWallet;
|
||||||
Coin totalAvailableBalance;
|
Coin totalAvailableBalance;
|
||||||
|
private double percentageBasedPrice = 0;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -256,7 +256,6 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||||
Offer createAndGetOffer() {
|
Offer createAndGetOffer() {
|
||||||
long fiatPrice = priceAsFiat.get() != null ? priceAsFiat.get().getValue() : 0L;
|
long fiatPrice = priceAsFiat.get() != null ? priceAsFiat.get().getValue() : 0L;
|
||||||
|
|
||||||
double percentagePrice = 0;
|
|
||||||
long amount = amountAsCoin.get() != null ? amountAsCoin.get().getValue() : 0L;
|
long amount = amountAsCoin.get() != null ? amountAsCoin.get().getValue() : 0L;
|
||||||
long minAmount = minAmountAsCoin.get() != null ? minAmountAsCoin.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;
|
String countryCode = paymentAccount instanceof CountryBasedPaymentAccount ? ((CountryBasedPaymentAccount) paymentAccount).getCountry().code : null;
|
||||||
|
|
||||||
checkNotNull(p2PService.getAddress(), "Address must not be 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,
|
return new Offer(offerId,
|
||||||
p2PService.getAddress(),
|
p2PService.getAddress(),
|
||||||
keyRing.getPubKeyRing(),
|
keyRing.getPubKeyRing(),
|
||||||
direction,
|
direction,
|
||||||
fiatPrice,
|
fiatPrice,
|
||||||
percentagePrice,
|
percentageBasedPrice,
|
||||||
usePercentageBasedPrice.get(),
|
usePercentageBasedPrice.get(),
|
||||||
amount,
|
amount,
|
||||||
minAmount,
|
minAmount,
|
||||||
|
@ -494,4 +496,12 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||||
walletService.swapTradeEntryToAvailableEntry(offerId, AddressEntry.Context.OFFER_FUNDING);
|
walletService.swapTradeEntryToAvailableEntry(offerId, AddressEntry.Context.OFFER_FUNDING);
|
||||||
walletService.swapTradeEntryToAvailableEntry(offerId, AddressEntry.Context.RESERVED_FOR_TRADE);
|
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> amountFocusedListener;
|
||||||
private ChangeListener<Boolean> minAmountFocusedListener;
|
private ChangeListener<Boolean> minAmountFocusedListener;
|
||||||
private ChangeListener<Boolean> priceFocusedListener;
|
private ChangeListener<Boolean> priceFocusedListener, priceAsPercentageFocusedListener;
|
||||||
private ChangeListener<Boolean> volumeFocusedListener;
|
private ChangeListener<Boolean> volumeFocusedListener;
|
||||||
private ChangeListener<Boolean> showWarningInvalidBtcDecimalPlacesListener;
|
private ChangeListener<Boolean> showWarningInvalidBtcDecimalPlacesListener;
|
||||||
private ChangeListener<Boolean> showWarningInvalidFiatDecimalPlacesPlacesListener;
|
private ChangeListener<Boolean> showWarningInvalidFiatDecimalPlacesPlacesListener;
|
||||||
|
@ -286,6 +286,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
amountTextField.setMouseTransparent(true);
|
amountTextField.setMouseTransparent(true);
|
||||||
minAmountTextField.setMouseTransparent(true);
|
minAmountTextField.setMouseTransparent(true);
|
||||||
priceTextField.setMouseTransparent(true);
|
priceTextField.setMouseTransparent(true);
|
||||||
|
priceAsPercentageTextField.setMouseTransparent(true);
|
||||||
|
fixedPriceButton.setMouseTransparent(true);
|
||||||
|
percentagePriceButton.setMouseTransparent(true);
|
||||||
volumeTextField.setMouseTransparent(true);
|
volumeTextField.setMouseTransparent(true);
|
||||||
currencyComboBox.setMouseTransparent(true);
|
currencyComboBox.setMouseTransparent(true);
|
||||||
paymentAccountsComboBox.setMouseTransparent(true);
|
paymentAccountsComboBox.setMouseTransparent(true);
|
||||||
|
@ -414,7 +417,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
amountBtcLabel.textProperty().bind(model.btcCode);
|
amountBtcLabel.textProperty().bind(model.btcCode);
|
||||||
priceCurrencyLabel.textProperty().bind(createStringBinding(() -> model.tradeCurrencyCode.get() + "/" + model.btcCode.get(), model.btcCode, model.tradeCurrencyCode));
|
priceCurrencyLabel.textProperty().bind(createStringBinding(() -> model.tradeCurrencyCode.get() + "/" + model.btcCode.get(), model.btcCode, model.tradeCurrencyCode));
|
||||||
priceTextField.disableProperty().bind(model.dataModel.usePercentageBasedPrice);
|
priceTextField.disableProperty().bind(model.dataModel.usePercentageBasedPrice);
|
||||||
|
priceCurrencyLabel.disableProperty().bind(model.dataModel.usePercentageBasedPrice);
|
||||||
priceAsPercentageTextField.disableProperty().bind(model.dataModel.usePercentageBasedPrice.not());
|
priceAsPercentageTextField.disableProperty().bind(model.dataModel.usePercentageBasedPrice.not());
|
||||||
|
priceAsPercentageLabel.disableProperty().bind(model.dataModel.usePercentageBasedPrice.not());
|
||||||
priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty());
|
priceAsPercentageLabel.prefWidthProperty().bind(priceCurrencyLabel.widthProperty());
|
||||||
volumeCurrencyLabel.textProperty().bind(model.tradeCurrencyCode);
|
volumeCurrencyLabel.textProperty().bind(model.tradeCurrencyCode);
|
||||||
minAmountBtcLabel.textProperty().bind(model.btcCode);
|
minAmountBtcLabel.textProperty().bind(model.btcCode);
|
||||||
|
@ -423,6 +428,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
amountTextField.textProperty().bindBidirectional(model.amount);
|
amountTextField.textProperty().bindBidirectional(model.amount);
|
||||||
minAmountTextField.textProperty().bindBidirectional(model.minAmount);
|
minAmountTextField.textProperty().bindBidirectional(model.minAmount);
|
||||||
priceTextField.textProperty().bindBidirectional(model.price);
|
priceTextField.textProperty().bindBidirectional(model.price);
|
||||||
|
priceAsPercentageTextField.textProperty().bindBidirectional(model.priceAsPercentage);
|
||||||
volumeTextField.textProperty().bindBidirectional(model.volume);
|
volumeTextField.textProperty().bindBidirectional(model.volume);
|
||||||
volumeTextField.promptTextProperty().bind(model.volumePromptLabel);
|
volumeTextField.promptTextProperty().bind(model.volumePromptLabel);
|
||||||
totalToPayTextField.textProperty().bind(model.totalToPay);
|
totalToPayTextField.textProperty().bind(model.totalToPay);
|
||||||
|
@ -462,7 +468,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
amountBtcLabel.textProperty().unbind();
|
amountBtcLabel.textProperty().unbind();
|
||||||
priceCurrencyLabel.textProperty().unbind();
|
priceCurrencyLabel.textProperty().unbind();
|
||||||
priceTextField.disableProperty().unbind();
|
priceTextField.disableProperty().unbind();
|
||||||
|
priceCurrencyLabel.disableProperty().unbind();
|
||||||
priceAsPercentageTextField.disableProperty().unbind();
|
priceAsPercentageTextField.disableProperty().unbind();
|
||||||
|
priceAsPercentageLabel.disableProperty().unbind();
|
||||||
volumeCurrencyLabel.textProperty().unbind();
|
volumeCurrencyLabel.textProperty().unbind();
|
||||||
minAmountBtcLabel.textProperty().unbind();
|
minAmountBtcLabel.textProperty().unbind();
|
||||||
priceDescriptionLabel.textProperty().unbind();
|
priceDescriptionLabel.textProperty().unbind();
|
||||||
|
@ -470,6 +478,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
amountTextField.textProperty().unbindBidirectional(model.amount);
|
amountTextField.textProperty().unbindBidirectional(model.amount);
|
||||||
minAmountTextField.textProperty().unbindBidirectional(model.minAmount);
|
minAmountTextField.textProperty().unbindBidirectional(model.minAmount);
|
||||||
priceTextField.textProperty().unbindBidirectional(model.price);
|
priceTextField.textProperty().unbindBidirectional(model.price);
|
||||||
|
priceAsPercentageTextField.textProperty().unbindBidirectional(model.priceAsPercentage);
|
||||||
priceAsPercentageLabel.prefWidthProperty().unbind();
|
priceAsPercentageLabel.prefWidthProperty().unbind();
|
||||||
volumeTextField.textProperty().unbindBidirectional(model.volume);
|
volumeTextField.textProperty().unbindBidirectional(model.volume);
|
||||||
volumeTextField.promptTextProperty().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());
|
model.onFocusOutPriceTextField(oldValue, newValue, priceTextField.getText());
|
||||||
priceTextField.setText(model.price.get());
|
priceTextField.setText(model.price.get());
|
||||||
};
|
};
|
||||||
|
priceAsPercentageFocusedListener = (o, oldValue, newValue) -> {
|
||||||
|
model.onFocusOutPriceAsPercentageTextField(oldValue, newValue, priceAsPercentageTextField.getText());
|
||||||
|
priceAsPercentageTextField.setText(model.priceAsPercentage.get());
|
||||||
|
};
|
||||||
volumeFocusedListener = (o, oldValue, newValue) -> {
|
volumeFocusedListener = (o, oldValue, newValue) -> {
|
||||||
model.onFocusOutVolumeTextField(oldValue, newValue, volumeTextField.getText());
|
model.onFocusOutVolumeTextField(oldValue, newValue, volumeTextField.getText());
|
||||||
volumeTextField.setText(model.volume.get());
|
volumeTextField.setText(model.volume.get());
|
||||||
|
@ -595,6 +608,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
|
|
||||||
tradeCurrencyCodeListener = (observable, oldValue, newValue) -> {
|
tradeCurrencyCodeListener = (observable, oldValue, newValue) -> {
|
||||||
priceTextField.clear();
|
priceTextField.clear();
|
||||||
|
priceAsPercentageTextField.clear();
|
||||||
volumeTextField.clear();
|
volumeTextField.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -633,6 +647,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
amountTextField.focusedProperty().addListener(amountFocusedListener);
|
amountTextField.focusedProperty().addListener(amountFocusedListener);
|
||||||
minAmountTextField.focusedProperty().addListener(minAmountFocusedListener);
|
minAmountTextField.focusedProperty().addListener(minAmountFocusedListener);
|
||||||
priceTextField.focusedProperty().addListener(priceFocusedListener);
|
priceTextField.focusedProperty().addListener(priceFocusedListener);
|
||||||
|
priceAsPercentageTextField.focusedProperty().addListener(priceAsPercentageFocusedListener);
|
||||||
volumeTextField.focusedProperty().addListener(volumeFocusedListener);
|
volumeTextField.focusedProperty().addListener(volumeFocusedListener);
|
||||||
|
|
||||||
// warnings
|
// warnings
|
||||||
|
@ -656,6 +671,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||||
amountTextField.focusedProperty().removeListener(amountFocusedListener);
|
amountTextField.focusedProperty().removeListener(amountFocusedListener);
|
||||||
minAmountTextField.focusedProperty().removeListener(minAmountFocusedListener);
|
minAmountTextField.focusedProperty().removeListener(minAmountFocusedListener);
|
||||||
priceTextField.focusedProperty().removeListener(priceFocusedListener);
|
priceTextField.focusedProperty().removeListener(priceFocusedListener);
|
||||||
|
priceAsPercentageTextField.focusedProperty().removeListener(priceAsPercentageFocusedListener);
|
||||||
volumeTextField.focusedProperty().removeListener(volumeFocusedListener);
|
volumeTextField.focusedProperty().removeListener(volumeFocusedListener);
|
||||||
|
|
||||||
// warnings
|
// warnings
|
||||||
|
|
|
@ -71,6 +71,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||||
final StringProperty amount = new SimpleStringProperty();
|
final StringProperty amount = new SimpleStringProperty();
|
||||||
final StringProperty minAmount = new SimpleStringProperty();
|
final StringProperty minAmount = new SimpleStringProperty();
|
||||||
final StringProperty price = new SimpleStringProperty();
|
final StringProperty price = new SimpleStringProperty();
|
||||||
|
final StringProperty priceAsPercentage = new SimpleStringProperty();
|
||||||
final StringProperty volume = new SimpleStringProperty();
|
final StringProperty volume = new SimpleStringProperty();
|
||||||
final StringProperty volumeDescriptionLabel = new SimpleStringProperty();
|
final StringProperty volumeDescriptionLabel = new SimpleStringProperty();
|
||||||
final StringProperty volumePromptLabel = new SimpleStringProperty();
|
final StringProperty volumePromptLabel = new SimpleStringProperty();
|
||||||
|
@ -103,7 +104,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||||
|
|
||||||
private ChangeListener<String> amountListener;
|
private ChangeListener<String> amountListener;
|
||||||
private ChangeListener<String> minAmountListener;
|
private ChangeListener<String> minAmountListener;
|
||||||
private ChangeListener<String> priceListener;
|
private ChangeListener<String> priceListener, priceAsPercentageListener;
|
||||||
private ChangeListener<String> volumeListener;
|
private ChangeListener<String> volumeListener;
|
||||||
private ChangeListener<Coin> amountAsCoinListener;
|
private ChangeListener<Coin> amountAsCoinListener;
|
||||||
private ChangeListener<Coin> minAmountAsCoinListener;
|
private ChangeListener<Coin> minAmountAsCoinListener;
|
||||||
|
@ -114,6 +115,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||||
private ChangeListener<String> errorMessageListener;
|
private ChangeListener<String> errorMessageListener;
|
||||||
private Offer offer;
|
private Offer offer;
|
||||||
private Timer timeoutTimer;
|
private Timer timeoutTimer;
|
||||||
|
private PriceFeed.Type priceFeedType;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -235,9 +237,61 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||||
setPriceToModel();
|
setPriceToModel();
|
||||||
calculateVolume();
|
calculateVolume();
|
||||||
dataModel.calculateTotalToPay();
|
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();
|
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) -> {
|
volumeListener = (ov, oldValue, newValue) -> {
|
||||||
if (isFiatInputValid(newValue).isValid) {
|
if (isFiatInputValid(newValue).isValid) {
|
||||||
setVolumeToModel();
|
setVolumeToModel();
|
||||||
|
@ -266,6 +320,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||||
amount.addListener(amountListener);
|
amount.addListener(amountListener);
|
||||||
minAmount.addListener(minAmountListener);
|
minAmount.addListener(minAmountListener);
|
||||||
price.addListener(priceListener);
|
price.addListener(priceListener);
|
||||||
|
priceAsPercentage.addListener(priceAsPercentageListener);
|
||||||
volume.addListener(volumeListener);
|
volume.addListener(volumeListener);
|
||||||
|
|
||||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||||
|
@ -282,6 +337,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||||
amount.removeListener(amountListener);
|
amount.removeListener(amountListener);
|
||||||
minAmount.removeListener(minAmountListener);
|
minAmount.removeListener(minAmountListener);
|
||||||
price.removeListener(priceListener);
|
price.removeListener(priceListener);
|
||||||
|
priceAsPercentage.removeListener(priceAsPercentageListener);
|
||||||
volume.removeListener(volumeListener);
|
volume.removeListener(volumeListener);
|
||||||
|
|
||||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
// 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)
|
if (dataModel.paymentAccount != null)
|
||||||
btcValidator.setMaxTradeLimitInBitcoin(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimit());
|
btcValidator.setMaxTradeLimitInBitcoin(dataModel.paymentAccount.getPaymentMethod().getMaxTradeLimit());
|
||||||
|
|
||||||
|
priceFeedType = direction == Offer.Direction.BUY ? PriceFeed.Type.ASK : PriceFeed.Type.BID;
|
||||||
|
|
||||||
return result;
|
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) {
|
void onFocusOutVolumeTextField(boolean oldValue, boolean newValue, String userInput) {
|
||||||
if (oldValue && !newValue) {
|
if (oldValue && !newValue) {
|
||||||
InputValidator.ValidationResult result = isFiatInputValid(volume.get());
|
InputValidator.ValidationResult result = isFiatInputValid(volume.get());
|
||||||
|
@ -492,21 +556,12 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||||
public boolean isPriceInRange() {
|
public boolean isPriceInRange() {
|
||||||
MarketPrice marketPrice = priceFeed.getMarketPrice(getTradeCurrency().getCode());
|
MarketPrice marketPrice = priceFeed.getMarketPrice(getTradeCurrency().getCode());
|
||||||
if (marketPrice != null) {
|
if (marketPrice != null) {
|
||||||
double marketPriceAsDouble = marketPrice.getPrice(PriceFeed.Type.LAST);
|
double marketPriceAsDouble = marketPrice.getPrice(priceFeedType);
|
||||||
Fiat priceAsFiat = dataModel.priceAsFiat.get();
|
Fiat priceAsFiat = dataModel.priceAsFiat.get();
|
||||||
long shiftDivisor = checkedPow(10, priceAsFiat.smallestUnitExponent());
|
long shiftDivisor = checkedPow(10, priceAsFiat.smallestUnitExponent());
|
||||||
double offerPrice = ((double) priceAsFiat.longValue()) / ((double) shiftDivisor);
|
double offerPrice = ((double) priceAsFiat.longValue()) / ((double) shiftDivisor);
|
||||||
if (marketPriceAsDouble != 0 && Math.abs(1 - (offerPrice / marketPriceAsDouble)) > preferences.getMaxPriceDistanceInPercent()) {
|
if (marketPriceAsDouble != 0 && Math.abs(1 - (offerPrice / marketPriceAsDouble)) > preferences.getMaxPriceDistanceInPercent()) {
|
||||||
Popup popup = new Popup();
|
displayPriceOutofRangePopup();
|
||||||
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();
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
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() {
|
BSFormatter getFormatter() {
|
||||||
return formatter;
|
return formatter;
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,19 +296,16 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
|
||||||
|
|
||||||
deviationListener = (observable, oldValue, newValue) -> {
|
deviationListener = (observable, oldValue, newValue) -> {
|
||||||
try {
|
try {
|
||||||
String input = newValue.replace("%", "");
|
double value = formatter.parsePercentStringToDouble(newValue);
|
||||||
input = input.replace(",", ".");
|
preferences.setMaxPriceDistanceInPercent(value);
|
||||||
input = input.replace(" ", "");
|
} catch (NumberFormatException t) {
|
||||||
double value = Double.parseDouble(input);
|
|
||||||
preferences.setMaxPriceDistanceInPercent(value / 100);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
log.error("Exception at parseDouble deviation: " + t.toString());
|
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) -> {
|
deviationFocusedListener = (observable1, oldValue1, newValue1) -> {
|
||||||
if (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;
|
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()));
|
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.textProperty().addListener(deviationListener);
|
||||||
deviationInputTextField.focusedProperty().addListener(deviationFocusedListener);
|
deviationInputTextField.focusedProperty().addListener(deviationFocusedListener);
|
||||||
|
|
||||||
|
|
|
@ -325,11 +325,58 @@ public class BSFormatter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String formatToPercent(double value) {
|
public String formatToPercent(double value) {
|
||||||
|
return formatToPercent(value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String formatToPercent(double value, int digits) {
|
||||||
DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(locale);
|
DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(locale);
|
||||||
decimalFormat.setMinimumFractionDigits(1);
|
decimalFormat.setMinimumFractionDigits(digits);
|
||||||
decimalFormat.setMaximumFractionDigits(1);
|
decimalFormat.setMaximumFractionDigits(digits);
|
||||||
decimalFormat.setGroupingUsed(false);
|
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) {
|
private String cleanInput(String input) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue