diff --git a/src/main/java/io/bitsquare/btc/BtcValidator.java b/src/main/java/io/bitsquare/btc/BtcValidator.java deleted file mode 100644 index 50f8ce552f..0000000000 --- a/src/main/java/io/bitsquare/btc/BtcValidator.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of Bitsquare. - * - * Bitsquare 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. - * - * Bitsquare 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 Bitsquare. If not, see . - */ - -package io.bitsquare.btc; - -import com.google.bitcoin.core.Coin; -import com.google.bitcoin.core.NetworkParameters; -import com.google.bitcoin.core.Transaction; - -import javax.inject.Inject; - -// TODO will be probably removed -public class BtcValidator { - private static NetworkParameters params; - - @Inject - public BtcValidator(NetworkParameters params) { - BtcValidator.params = params; - } - - public static boolean isMinSpendableAmount(Coin amount) { - return amount != null && amount.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0; - } - -} diff --git a/src/main/java/io/bitsquare/btc/Restrictions.java b/src/main/java/io/bitsquare/btc/Restrictions.java index 27e32c76d2..fa63cab75a 100644 --- a/src/main/java/io/bitsquare/btc/Restrictions.java +++ b/src/main/java/io/bitsquare/btc/Restrictions.java @@ -18,10 +18,14 @@ package io.bitsquare.btc; import com.google.bitcoin.core.Coin; +import com.google.bitcoin.core.Transaction; // Lets see if we get more restriction otherwise move it to other class public class Restrictions { public static final Coin MIN_TRADE_AMOUNT = Coin.parseCoin("0.0001"); + public static boolean isMinSpendableAmount(Coin amount) { + return amount != null && amount.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0; + } } diff --git a/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalController.java b/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalController.java index 9b43cb2998..5a7f70a1a2 100644 --- a/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalController.java +++ b/src/main/java/io/bitsquare/gui/funds/withdrawal/WithdrawalController.java @@ -18,8 +18,8 @@ package io.bitsquare.gui.funds.withdrawal; import io.bitsquare.btc.AddressEntry; -import io.bitsquare.btc.BtcValidator; import io.bitsquare.btc.FeePolicy; +import io.bitsquare.btc.Restrictions; import io.bitsquare.btc.WalletFacade; import io.bitsquare.gui.CachedViewController; import io.bitsquare.gui.components.Popups; @@ -148,8 +148,8 @@ public class WithdrawalController extends CachedViewController { amountTextField, withdrawFromTextField, withdrawToTextField, changeAddressTextField); BitSquareValidator.textFieldsHasDoubleValueWithReset(amountTextField); - Coin amount = BSFormatter.parseToCoin(amountTextField.getText()); - if (BtcValidator.isMinSpendableAmount(amount)) { + Coin amount = BSFormatter.parseToBtc(amountTextField.getText()); + if (Restrictions.isMinSpendableAmount(amount)) { FutureCallback callback = new FutureCallback() { @Override public void onSuccess(@javax.annotation.Nullable Transaction transaction) { diff --git a/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferCodeBehind.java b/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferCodeBehind.java index 0eb777987a..96a76d58d1 100644 --- a/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferCodeBehind.java +++ b/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferCodeBehind.java @@ -24,6 +24,9 @@ import io.bitsquare.gui.components.btc.AddressTextField; import io.bitsquare.gui.components.btc.BalanceTextField; import io.bitsquare.gui.components.confidence.ConfidenceProgressIndicator; import io.bitsquare.gui.trade.TradeController; +import io.bitsquare.gui.util.validation.BtcValidator; +import io.bitsquare.gui.util.validation.FiatValidator; +import io.bitsquare.gui.util.validation.ValidationHelper; import io.bitsquare.trade.orderbook.OrderBookFilter; import java.net.URL; @@ -34,6 +37,7 @@ import javax.inject.Inject; import javafx.fxml.FXML; import javafx.scene.control.*; +import javafx.scene.layout.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,9 +61,8 @@ import org.slf4j.LoggerFactory; *

* Optional ViewBuilder: * - Replacement for FXML view definitions. - * + *

* Note: Don't assign the root node as it is defined in the base class! - * */ public class CreateOfferCodeBehind extends CachedViewController { private static final Logger log = LoggerFactory.getLogger(CreateOfferCodeBehind.class); @@ -154,15 +157,25 @@ public class CreateOfferCodeBehind extends CachedViewController { /////////////////////////////////////////////////////////////////////////////////////////// private void setupListeners() { - volumeTextField.focusedProperty().addListener((observableValue, oldValue, - newValue) -> presenter.validateVolumeOnFocusOut(oldValue, - newValue, volumeTextField.getText())); - amountTextField.focusedProperty().addListener((observableValue, oldValue, - newValue) -> presenter.onFocusOutAmountTextField(oldValue, - newValue)); - priceTextField.focusedProperty().addListener((observableValue, oldValue, - newValue) -> presenter.onFocusOutPriceTextField(oldValue, - newValue)); + volumeTextField.focusedProperty().addListener((o, oldValue, newValue) -> { + presenter.onFocusOutVolumeTextField(oldValue, newValue, volumeTextField.getText()); + volumeTextField.setText(presenter.volume.get()); + }); + + amountTextField.focusedProperty().addListener((o, oldValue, newValue) -> { + presenter.onFocusOutAmountTextField(oldValue, newValue); + amountTextField.setText(presenter.amount.get()); + }); + + priceTextField.focusedProperty().addListener((o, oldValue, newValue) -> { + presenter.onFocusOutPriceTextField(oldValue, newValue); + priceTextField.setText(presenter.price.get()); + }); + + minAmountTextField.focusedProperty().addListener((o, oldValue, newValue) -> { + presenter.onFocusOutMinAmountTextField(oldValue, newValue); + minAmountTextField.setText(presenter.minAmount.get()); + }); presenter.needsInputValidation.addListener((o, oldValue, newValue) -> { if (newValue) { @@ -173,17 +186,36 @@ public class CreateOfferCodeBehind extends CachedViewController { } }); - presenter.showVolumeAdjustedWarning.addListener((o, oldValue, newValue) -> { + presenter.showWarningInvalidBtcDecimalPlaces.addListener((o, oldValue, newValue) -> { + if (newValue) { + Popups.openWarningPopup("Warning", "The amount you have entered exceeds the number of allowed decimal" + + " places.\nThe amount has been adjusted to 4 decimal places."); + presenter.showWarningInvalidBtcDecimalPlaces.set(false); + } + }); + + presenter.showWarningInvalidFiatDecimalPlaces.addListener((o, oldValue, newValue) -> { + if (newValue) { + Popups.openWarningPopup("Warning", "The amount you have entered exceeds the number of allowed decimal" + + " places.\nThe amount has been adjusted to 2 decimal places."); + presenter.showWarningInvalidFiatDecimalPlaces.set(false); + } + }); + + presenter.showWarningInvalidBtcFractions.addListener((o, oldValue, newValue) -> { if (newValue) { Popups.openWarningPopup("Warning", "The total volume you have entered leads to invalid fractional " + "Bitcoin amounts.\nThe amount has been adjusted and a new total volume be calculated from it."); + presenter.showWarningInvalidBtcFractions.set(false); volumeTextField.setText(presenter.volume.get()); } }); + presenter.requestPlaceOfferFailed.addListener((o, oldValue, newValue) -> { if (newValue) { Popups.openErrorPopup("Error", "An error occurred when placing the offer.\n" + presenter.requestPlaceOfferErrorMessage); + presenter.requestPlaceOfferFailed.set(false); } }); } @@ -231,26 +263,27 @@ public class CreateOfferCodeBehind extends CachedViewController { } private void setupTextFieldValidators() { - /* BtcValidator amountValidator = new BtcValidator(); - amountTextField.setNumberValidator(amountValidator); - amountTextField.setErrorPopupLayoutReference((Region) amountTextField.getParent()); + Region referenceNode = (Region) amountTextField.getParent(); + + BtcValidator amountValidator = new BtcValidator(); + amountTextField.setValidator(amountValidator); + amountTextField.setErrorPopupLayoutReference(referenceNode); - priceTextField.setNumberValidator(new FiatValidator()); - priceTextField.setErrorPopupLayoutReference((Region) amountTextField.getParent()); + priceTextField.setValidator(new FiatValidator()); + priceTextField.setErrorPopupLayoutReference(referenceNode); - BtcValidator volumeValidator = new BtcValidator(); - volumeTextField.setNumberValidator(volumeValidator); - volumeTextField.setErrorPopupLayoutReference((Region) volumeTextField.getParent()); + volumeTextField.setValidator(new FiatValidator()); + volumeTextField.setErrorPopupLayoutReference(referenceNode); BtcValidator minAmountValidator = new BtcValidator(); - minAmountTextField.setNumberValidator(minAmountValidator); + minAmountTextField.setValidator(minAmountValidator); ValidationHelper.setupMinAmountInRangeOfAmountValidation(amountTextField, - minAmountTextField, - presenter.amount, - presenter.minAmount, - amountValidator, - minAmountValidator);*/ + minAmountTextField, + presenter.amount, + presenter.minAmount, + amountValidator, + minAmountValidator); } } diff --git a/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferModel.java b/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferModel.java index 9b110ed50d..77e154cc4c 100644 --- a/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferModel.java +++ b/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferModel.java @@ -75,7 +75,7 @@ class CreateOfferModel { Coin minAmountAsCoin; Coin collateralAsCoin; Fiat priceAsFiat; - Fiat tradeVolumeAsFiat; + Fiat volumeAsFiat; AddressEntry addressEntry; final StringProperty requestPlaceOfferErrorMessage = new SimpleStringProperty(); diff --git a/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferPresenter.java b/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferPresenter.java index 260d1dfd96..3669024301 100644 --- a/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferPresenter.java +++ b/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferPresenter.java @@ -81,7 +81,9 @@ class CreateOfferPresenter { final BooleanProperty isPlaceOfferButtonVisible = new SimpleBooleanProperty(true); final BooleanProperty isPlaceOfferButtonDisabled = new SimpleBooleanProperty(); final BooleanProperty needsInputValidation = new SimpleBooleanProperty(); - final BooleanProperty showVolumeAdjustedWarning = new SimpleBooleanProperty(); + final BooleanProperty showWarningInvalidBtcFractions = new SimpleBooleanProperty(); + final BooleanProperty showWarningInvalidFiatDecimalPlaces = new SimpleBooleanProperty(); + final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty(); final BooleanProperty showTransactionPublishedScreen = new SimpleBooleanProperty(); final BooleanProperty requestPlaceOfferFailed = new SimpleBooleanProperty(); @@ -161,7 +163,7 @@ class CreateOfferPresenter { model.minAmountAsCoin = orderBookFilter.getAmount(); // TODO use Fiat in orderBookFilter - model.priceAsFiat = parseToFiat(String.valueOf(orderBookFilter.getPrice())); + model.priceAsFiat = parseToFiatWith2Decimals(String.valueOf(orderBookFilter.getPrice())); directionLabel.set(model.getDirection() == Direction.BUY ? "Buy:" : "Sell:"); amount.set(formatBtc(model.amountAsCoin)); @@ -175,10 +177,10 @@ class CreateOfferPresenter { /////////////////////////////////////////////////////////////////////////////////////////// void placeOffer() { - model.amountAsCoin = parseToCoin(amount.get()); - model.minAmountAsCoin = parseToCoin(minAmount.get()); - model.priceAsFiat = parseToFiat(price.get()); - model.minAmountAsCoin = parseToCoin(minAmount.get()); + model.amountAsCoin = parseToBtcWith4Decimals(amount.get()); + model.minAmountAsCoin = parseToBtcWith4Decimals(minAmount.get()); + model.priceAsFiat = parseToFiatWith2Decimals(price.get()); + model.minAmountAsCoin = parseToBtcWith4Decimals(minAmount.get()); needsInputValidation.set(true); @@ -200,53 +202,64 @@ class CreateOfferPresenter { // bindBidirectional for amount, price, volume and minAmount amount.addListener(ov -> { - model.amountAsCoin = parseToCoin(amount.get()); + model.amountAsCoin = parseToBtcWith4Decimals(amount.get()); calculateVolume(); calculateTotalToPay(); calculateCollateral(); }); price.addListener(ov -> { - model.priceAsFiat = parseToFiat(price.get()); + model.priceAsFiat = parseToFiatWith2Decimals(price.get()); calculateVolume(); calculateTotalToPay(); calculateCollateral(); }); volume.addListener(ov -> { - model.tradeVolumeAsFiat = parseToFiat(volume.get()); + model.volumeAsFiat = parseToFiatWith2Decimals(volume.get()); calculateAmount(); calculateTotalToPay(); calculateCollateral(); }); } + void onFocusOutAmountTextField(Boolean oldValue, Boolean newValue) { - // We adjust the volume if fractional coins result from volume/price division on focus out - void validateVolumeOnFocusOut(Boolean oldValue, Boolean newValue, String volumeTextFieldText) { if (oldValue && !newValue) { + showWarningInvalidBtcDecimalPlaces.set(!hasBtcValidDecimals(amount.get())); + model.amountAsCoin = parseToBtcWith4Decimals(amount.get()); + amount.set(formatBtc(model.amountAsCoin)); calculateVolume(); - if (!formatFiat(parseToFiat(volumeTextFieldText)).equals(volume.get())) - showVolumeAdjustedWarning.set(true); } - - - // - // only on focus out and ignore focus loss from window - /* if (!newValue && volumeTextField.getScene() != null && volumeTextField.getScene().getWindow().isFocused()) - amountTextField.reValidate();*/ } - void onFocusOutAmountTextField(Boolean oldValue, Boolean newValue) { - // only on focus out and ignore focus loss from window - /* if (!newValue && amountTextField.getScene() != null && amountTextField.getScene().getWindow().isFocused()) - volumeTextField.reValidate();*/ + void onFocusOutMinAmountTextField(Boolean oldValue, Boolean newValue) { + + if (oldValue && !newValue) { + showWarningInvalidBtcDecimalPlaces.set(!hasBtcValidDecimals(minAmount.get())); + model.minAmountAsCoin = parseToBtcWith4Decimals(minAmount.get()); + minAmount.set(formatBtc(model.minAmountAsCoin)); + } + } + + void onFocusOutVolumeTextField(Boolean oldValue, Boolean newValue, String volumeTextFieldText) { + if (oldValue && !newValue) { + showWarningInvalidFiatDecimalPlaces.set(!hasFiatValidDecimals(volume.get())); + model.volumeAsFiat = parseToFiatWith2Decimals(volume.get()); + volume.set(formatFiat(model.volumeAsFiat)); + calculateAmount(); + + showWarningInvalidBtcFractions.set(!formatFiat(parseToFiatWith2Decimals(volumeTextFieldText)).equals + (volume.get())); + } } void onFocusOutPriceTextField(Boolean oldValue, Boolean newValue) { - // only on focus out and ignore focus loss from window - /* if (!newValue && priceTextField.getScene() != null && priceTextField.getScene().getWindow().isFocused()) - volumeTextField.reValidate();*/ + if (oldValue && !newValue) { + showWarningInvalidFiatDecimalPlaces.set(!hasFiatValidDecimals(price.get())); + model.priceAsFiat = parseToFiatWith2Decimals(price.get()); + price.set(formatFiat(model.priceAsFiat)); + } } @@ -269,24 +282,24 @@ class CreateOfferPresenter { } private void calculateVolume() { - model.amountAsCoin = parseToCoin(amount.get()); - model.priceAsFiat = parseToFiat(price.get()); + model.amountAsCoin = parseToBtcWith4Decimals(amount.get()); + model.priceAsFiat = parseToFiatWith2Decimals(price.get()); if (model.priceAsFiat != null && model.amountAsCoin != null && !model.amountAsCoin.isZero()) { - model.tradeVolumeAsFiat = new ExchangeRate(model.priceAsFiat).coinToFiat(model.amountAsCoin); - volume.set(formatFiat(model.tradeVolumeAsFiat)); + model.volumeAsFiat = new ExchangeRate(model.priceAsFiat).coinToFiat(model.amountAsCoin); + volume.set(formatFiat(model.volumeAsFiat)); } } private void calculateAmount() { - model.tradeVolumeAsFiat = parseToFiat(volume.get()); - model.priceAsFiat = parseToFiat(price.get()); + model.volumeAsFiat = parseToFiatWith2Decimals(volume.get()); + model.priceAsFiat = parseToFiatWith2Decimals(price.get()); - if (model.tradeVolumeAsFiat != null && model.priceAsFiat != null && !model.priceAsFiat.isZero()) { - model.amountAsCoin = new ExchangeRate(model.priceAsFiat).fiatToCoin(model.tradeVolumeAsFiat); + if (model.volumeAsFiat != null && model.priceAsFiat != null && !model.priceAsFiat.isZero()) { + model.amountAsCoin = new ExchangeRate(model.priceAsFiat).fiatToCoin(model.volumeAsFiat); // If we got a btc value with more then 4 decimals we convert it to max 4 decimals - model.amountAsCoin = applyFormatRules(model.amountAsCoin); + model.amountAsCoin = reduceto4Dezimals(model.amountAsCoin); amount.set(formatBtc(model.amountAsCoin)); calculateTotalToPay(); calculateCollateral(); diff --git a/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferView.fxml b/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferView.fxml index fd363ac20a..5a54cdb44d 100644 --- a/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferView.fxml +++ b/src/main/java/io/bitsquare/gui/trade/createoffer/CreateOfferView.fxml @@ -48,11 +48,11 @@