From 9f42c5ac61ef75e24f3f3b5bd8e102399e705d83 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Thu, 31 Mar 2016 20:38:25 +0200 Subject: [PATCH] Add spinner to create offer UI --- .../createoffer/CreateOfferDataModel.java | 7 +- .../offer/createoffer/CreateOfferView.java | 74 +++- .../createoffer/CreateOfferViewModel.java | 26 ++ .../main/offer/takeoffer/TakeOfferView.java | 374 +++++++++--------- 4 files changed, 279 insertions(+), 202 deletions(-) diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java index c47e227cf6..3acf88d15e 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java @@ -92,9 +92,10 @@ class CreateOfferDataModel extends ActivatableDataModel { final StringProperty btcCode = new SimpleStringProperty(); final BooleanProperty isWalletFunded = new SimpleBooleanProperty(); - final BooleanProperty useMBTC = new SimpleBooleanProperty(); + final BooleanProperty isMainNet = new SimpleBooleanProperty(); + final BooleanProperty isFeeFromFundingTxSufficient = new SimpleBooleanProperty(); + final ObjectProperty feeFromFundingTxProperty = new SimpleObjectProperty(Coin.NEGATIVE_SATOSHI); - final ObjectProperty amountAsCoin = new SimpleObjectProperty<>(); final ObjectProperty minAmountAsCoin = new SimpleObjectProperty<>(); final ObjectProperty priceAsFiat = new SimpleObjectProperty<>(); @@ -134,6 +135,8 @@ class CreateOfferDataModel extends ActivatableDataModel { this.blockchainService = blockchainService; this.formatter = formatter; + isMainNet.set(preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET); + offerId = UUID.randomUUID().toString(); addressEntry = walletService.getTradeAddressEntry(offerId); offerFeeAsCoin = FeePolicy.getCreateOfferFee(); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java index 4f875b8628..ae660ee8ff 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java @@ -65,6 +65,8 @@ import net.glxn.qrgen.image.ImageType; import org.bitcoinj.core.Coin; import org.bitcoinj.uri.BitcoinURI; import org.controlsfx.control.PopOver; +import org.fxmisc.easybind.EasyBind; +import org.fxmisc.easybind.Subscription; import org.jetbrains.annotations.NotNull; import javax.inject.Inject; @@ -87,12 +89,13 @@ public class CreateOfferView extends ActivatableViewAndModel paymentAccountsComboBox; private ComboBox currencyComboBox; @@ -119,6 +122,7 @@ public class CreateOfferView extends ActivatableViewAndModel balanceListener; private HBox fundingHBox; + private Subscription isSpinnerVisibleSubscription; /////////////////////////////////////////////////////////////////////////////////////////// @@ -165,6 +169,10 @@ public class CreateOfferView extends ActivatableViewAndModel - model.tradeCurrencyCode.get() + "/" + model.btcCode.get(), model.btcCode, model.tradeCurrencyCode)); + priceCurrencyLabel.textProperty().bind(createStringBinding(() -> model.tradeCurrencyCode.get() + "/" + model.btcCode.get(), model.btcCode, model.tradeCurrencyCode)); volumeCurrencyLabel.textProperty().bind(model.tradeCurrencyCode); minAmountBtcLabel.textProperty().bind(model.btcCode); - - priceDescriptionLabel.textProperty().bind(createStringBinding(() -> - BSResources.get("createOffer.amountPriceBox.priceDescription", model.tradeCurrencyCode.get()), model.tradeCurrencyCode)); - - volumeDescriptionLabel.textProperty().bind(createStringBinding(model.volumeDescriptionLabel::get, model.tradeCurrencyCode, model - .volumeDescriptionLabel)); - + priceDescriptionLabel.textProperty().bind(createStringBinding(() -> BSResources.get("createOffer.amountPriceBox.priceDescription", model.tradeCurrencyCode.get()), model.tradeCurrencyCode)); + volumeDescriptionLabel.textProperty().bind(createStringBinding(model.volumeDescriptionLabel::get, model.tradeCurrencyCode, model.volumeDescriptionLabel)); amountTextField.textProperty().bindBidirectional(model.amount); minAmountTextField.textProperty().bindBidirectional(model.minAmount); priceTextField.textProperty().bindBidirectional(model.price); volumeTextField.textProperty().bindBidirectional(model.volume); volumeTextField.promptTextProperty().bind(model.volumePromptLabel); - totalToPayTextField.textProperty().bind(model.totalToPay); addressTextField.amountAsCoinProperty().bind(model.dataModel.missingCoin); @@ -404,10 +411,14 @@ public class CreateOfferView extends ActivatableViewAndModel spinner.setProgress(isSpinnerVisible ? -1 : 0)); + + } + + private void removeSubscriptions() { + isSpinnerVisibleSubscription.unsubscribe(); } private void createListeners() { @@ -787,7 +819,13 @@ public class CreateOfferView extends ActivatableViewAndModel amountValidationResult = new SimpleObjectProperty<>(); final ObjectProperty minAmountValidationResult = new @@ -151,8 +154,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel onPaymentAccountsComboBoxSelected()); - + addBindings(); + addSubscriptions(); amountTextField.focusedProperty().addListener(amountFocusedListener); - amountBtcLabel.textProperty().bind(model.btcCode); - amountTextField.textProperty().bindBidirectional(model.amount); - volumeTextField.textProperty().bindBidirectional(model.volume); - totalToPayTextField.textProperty().bind(model.totalToPay); - addressTextField.amountAsCoinProperty().bind(model.dataModel.missingCoin); - amountTextField.validationResultProperty().bind(model.amountValidationResult); - takeOfferButton.disableProperty().bind(model.isTakeOfferButtonDisabled); - takeOfferButton.visibleProperty().bind(model.dataModel.isWalletFunded.and(model.showPayFundsScreenDisplayed)); - takeOfferButton.managedProperty().bind(model.dataModel.isWalletFunded.and(model.showPayFundsScreenDisplayed)); - fundingHBox.visibleProperty().bind(model.dataModel.isWalletFunded.not().and(model.showPayFundsScreenDisplayed)); - fundingHBox.managedProperty().bind(model.dataModel.isWalletFunded.not().and(model.showPayFundsScreenDisplayed)); - - spinner.visibleProperty().bind(model.isSpinnerVisible); - spinner.managedProperty().bind(model.isSpinnerVisible); - - spinnerInfoLabel.visibleProperty().bind(model.isSpinnerVisible); - spinnerInfoLabel.managedProperty().bind(model.isSpinnerVisible); - spinnerInfoLabel.textProperty().bind(model.spinnerInfoText); - - priceCurrencyLabel.textProperty().bind(createStringBinding(() -> - model.dataModel.getCurrencyCode() + "/" + model.btcCode.get(), model.btcCode)); - - volumeCurrencyLabel.setText(model.dataModel.getCurrencyCode()); - amountRangeBtcLabel.textProperty().bind(model.btcCode); - - priceDescriptionLabel.setText(BSResources.get("createOffer.amountPriceBox.priceDescription", model.dataModel.getCurrencyCode())); - volumeDescriptionLabel.setText(model.volumeDescriptionLabel.get()); - - errorPopupDisplayed = new SimpleBooleanProperty(); - offerWarningSubscription = EasyBind.subscribe(model.offerWarning, newValue -> { - if (newValue != null) { - if (offerDetailsWindowDisplayed) - offerDetailsWindow.hide(); - - UserThread.runAfter(() -> new Popup().warning(newValue + "\n\n" + - "If you have already paid in funds you can withdraw it in the " + - "\"Funds/Available for withdrawal\" screen.") - .actionButtonText("Go to \"Available for withdrawal\"") - .onAction(() -> { - errorPopupDisplayed.set(true); - model.resetOfferWarning(); - close(); - navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class); - }) - .onClose(() -> { - errorPopupDisplayed.set(true); - model.resetOfferWarning(); - close(); - }) - .show(), 100, TimeUnit.MILLISECONDS); - } - }); - - errorMessageSubscription = EasyBind.subscribe(model.errorMessage, newValue -> { - if (newValue != null) { - new Popup().error(BSResources.get("takeOffer.error.message", model.errorMessage.get()) + - "Please try to restart you application and check your network connection to see if you can resolve the issue.") - .onClose(() -> { - errorPopupDisplayed.set(true); - model.resetErrorMessage(); - close(); - }) - .show(); - } - }); - isOfferAvailableSubscription = EasyBind.subscribe(model.isOfferAvailable, newValue -> { - if (newValue) { - offerAvailabilitySpinner.setProgress(0); - offerAvailabilitySpinner.setVisible(false); - offerAvailabilitySpinnerLabel.setVisible(false); - } - }); - - isSpinnerVisibleSubscription = EasyBind.subscribe(model.isSpinnerVisible, - isSpinnerVisible -> spinner.setProgress(isSpinnerVisible ? -1 : 0)); - - showWarningInvalidBtcDecimalPlacesSubscription = EasyBind.subscribe(model.showWarningInvalidBtcDecimalPlaces, newValue -> { - if (newValue) { - new Popup().warning(BSResources.get("takeOffer.amountPriceBox.warning.invalidBtcDecimalPlaces")).show(); - model.showWarningInvalidBtcDecimalPlaces.set(false); - } - }); - showTransactionPublishedScreenSubscription = EasyBind.subscribe(model.showTransactionPublishedScreen, newValue -> { - if (newValue && BitsquareApp.DEV_MODE) { - close(); - navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class); - } else if (newValue && model.getTrade() != null && model.getTrade().errorMessageProperty().get() == null) { - String key = "takeOfferSuccessInfo"; - if (preferences.showAgain(key)) { - UserThread.runAfter(() -> new Popup().headLine(BSResources.get("takeOffer.success.headline")) - .feedback(BSResources.get("takeOffer.success.info")) - .actionButtonText("Go to \"Open trades\"") - .dontShowAgainId(key, preferences) - .onAction(() -> { - UserThread.runAfter( - () -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class) - , 100, TimeUnit.MILLISECONDS); - close(); - }) - .onClose(this::close) - .show(), 1); - } else { - close(); - } - } - }); - - if (model.getPossiblePaymentAccounts().size() > 1) { - paymentAccountsComboBox.setItems(model.getPossiblePaymentAccounts()); - paymentAccountsComboBox.getSelectionModel().select(0); - } - - noSufficientFeeBinding = EasyBind.combine(model.dataModel.isWalletFunded, model.dataModel.isMainNet, model.dataModel.isFeeFromFundingTxSufficient, - (isWalletFunded, isMainNet, isFeeSufficient) -> isWalletFunded && isMainNet && !isFeeSufficient); - noSufficientFeeSubscription = noSufficientFeeBinding.subscribe((observable, oldValue, newValue) -> { - if (newValue) - new Popup().warning("The mining fee from your funding transaction is not sufficiently high.\n\n" + - "You need to use at least a mining fee of " + - model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) + ".\n\n" + - "The fee used in your funding transaction was only " + - model.formatter.formatCoinWithCode(model.dataModel.feeFromFundingTx) + ".\n\n" + - "The trade transactions might take too much time to be included in " + - "a block if the fee is too low.\n" + - "Please check at your external wallet that you set the required fee and " + - "do a funding again with the correct fee.\n\n" + - "In the \"Funds/Open for withdrawal\" section you can withdraw those funds.") - .closeButtonText("Close") - .onClose(() -> { - close(); - navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class); - }) - .show(); - }); - if (offerAvailabilitySpinner != null && offerAvailabilitySpinner.isVisible()) offerAvailabilitySpinner.setProgress(-1); if (spinner != null && spinner.isVisible()) spinner.setProgress(-1); - balanceSubscription = EasyBind.subscribe(model.dataModel.balance, newValue -> balanceTextField.setBalance(newValue)); - totalToPaySubscription = EasyBind.subscribe(model.dataModel.totalToPayAsCoin, newValue -> balanceTextField.setTargetAmount(newValue)); + volumeCurrencyLabel.setText(model.dataModel.getCurrencyCode()); + priceDescriptionLabel.setText(BSResources.get("createOffer.amountPriceBox.priceDescription", model.dataModel.getCurrencyCode())); + volumeDescriptionLabel.setText(model.volumeDescriptionLabel.get()); + + if (model.getPossiblePaymentAccounts().size() > 1) { + paymentAccountsComboBox.setItems(model.getPossiblePaymentAccounts()); + paymentAccountsComboBox.getSelectionModel().select(0); + } } @Override protected void deactivate() { - paymentAccountsComboBox.setOnAction(null); - + removeBindings(); + removeSubscriptions(); amountTextField.focusedProperty().removeListener(amountFocusedListener); - amountBtcLabel.textProperty().unbind(); - amountTextField.textProperty().unbindBidirectional(model.amount); - volumeTextField.textProperty().unbindBidirectional(model.volume); - totalToPayTextField.textProperty().unbind(); - addressTextField.amountAsCoinProperty().unbind(); - amountTextField.validationResultProperty().unbind(); - priceCurrencyLabel.textProperty().unbind(); - volumeCurrencyLabel.textProperty().unbind(); - amountRangeBtcLabel.textProperty().unbind(); - priceDescriptionLabel.textProperty().unbind(); - volumeDescriptionLabel.textProperty().unbind(); - - fundingHBox.visibleProperty().unbind(); - fundingHBox.managedProperty().unbind(); - takeOfferButton.visibleProperty().unbind(); - takeOfferButton.managedProperty().unbind(); - takeOfferButton.disableProperty().unbind(); - spinner.visibleProperty().unbind(); - spinner.managedProperty().unbind(); - spinnerInfoLabel.visibleProperty().unbind(); - spinnerInfoLabel.managedProperty().unbind(); - spinnerInfoLabel.textProperty().unbind(); - - offerWarningSubscription.unsubscribe(); - errorMessageSubscription.unsubscribe(); - isOfferAvailableSubscription.unsubscribe(); - isSpinnerVisibleSubscription.unsubscribe(); - showWarningInvalidBtcDecimalPlacesSubscription.unsubscribe(); - showTransactionPublishedScreenSubscription.unsubscribe(); - - noSufficientFeeSubscription.unsubscribe(); - if (balanceTextField != null) - balanceTextField.cleanup(); - if (offerAvailabilitySpinner != null) offerAvailabilitySpinner.setProgress(0); if (spinner != null) spinner.setProgress(0); - balanceSubscription.unsubscribe(); - totalToPaySubscription.unsubscribe(); + if (balanceTextField != null) + balanceTextField.cleanup(); } @@ -444,10 +283,6 @@ public class TakeOfferView extends ActivatableViewAndModel model.dataModel.getCurrencyCode() + "/" + model.btcCode.get(), model.btcCode)); + amountRangeBtcLabel.textProperty().bind(model.btcCode); + nextButton.disableProperty().bind(model.isNextButtonDisabled); + + + // funding + fundingHBox.visibleProperty().bind(model.dataModel.isWalletFunded.not().and(model.showPayFundsScreenDisplayed)); + fundingHBox.managedProperty().bind(model.dataModel.isWalletFunded.not().and(model.showPayFundsScreenDisplayed)); + spinner.visibleProperty().bind(model.isSpinnerVisible); + spinner.managedProperty().bind(model.isSpinnerVisible); + spinnerInfoLabel.visibleProperty().bind(model.isSpinnerVisible); + spinnerInfoLabel.managedProperty().bind(model.isSpinnerVisible); + spinnerInfoLabel.textProperty().bind(model.spinnerInfoText); + takeOfferButton.disableProperty().bind(model.isTakeOfferButtonDisabled); + takeOfferButton.visibleProperty().bind(model.dataModel.isWalletFunded.and(model.showPayFundsScreenDisplayed)); + takeOfferButton.managedProperty().bind(model.dataModel.isWalletFunded.and(model.showPayFundsScreenDisplayed)); + } + + private void removeBindings() { + amountBtcLabel.textProperty().unbind(); + amountTextField.textProperty().unbindBidirectional(model.amount); + volumeTextField.textProperty().unbindBidirectional(model.volume); + totalToPayTextField.textProperty().unbind(); + addressTextField.amountAsCoinProperty().unbind(); + amountTextField.validationResultProperty().unbind(); + priceCurrencyLabel.textProperty().unbind(); + amountRangeBtcLabel.textProperty().unbind(); + nextButton.disableProperty().unbind(); + + // funding + fundingHBox.visibleProperty().unbind(); + fundingHBox.managedProperty().unbind(); + spinner.visibleProperty().unbind(); + spinner.managedProperty().unbind(); + spinnerInfoLabel.visibleProperty().unbind(); + spinnerInfoLabel.managedProperty().unbind(); + spinnerInfoLabel.textProperty().unbind(); + takeOfferButton.visibleProperty().unbind(); + takeOfferButton.managedProperty().unbind(); + takeOfferButton.disableProperty().unbind(); + } + + private void addSubscriptions() { + errorPopupDisplayed = new SimpleBooleanProperty(); + offerWarningSubscription = EasyBind.subscribe(model.offerWarning, newValue -> { + if (newValue != null) { + if (offerDetailsWindowDisplayed) + offerDetailsWindow.hide(); + + UserThread.runAfter(() -> new Popup().warning(newValue + "\n\n" + + "If you have already paid in funds you can withdraw it in the " + + "\"Funds/Available for withdrawal\" screen.") + .actionButtonText("Go to \"Available for withdrawal\"") + .onAction(() -> { + errorPopupDisplayed.set(true); + model.resetOfferWarning(); + close(); + navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class); + }) + .onClose(() -> { + errorPopupDisplayed.set(true); + model.resetOfferWarning(); + close(); + }) + .show(), 100, TimeUnit.MILLISECONDS); + } + }); + + errorMessageSubscription = EasyBind.subscribe(model.errorMessage, newValue -> { + if (newValue != null) { + new Popup().error(BSResources.get("takeOffer.error.message", model.errorMessage.get()) + + "Please try to restart you application and check your network connection to see if you can resolve the issue.") + .onClose(() -> { + errorPopupDisplayed.set(true); + model.resetErrorMessage(); + close(); + }) + .show(); + } + }); + + isOfferAvailableSubscription = EasyBind.subscribe(model.isOfferAvailable, newValue -> { + if (newValue) { + offerAvailabilitySpinner.setProgress(0); + offerAvailabilitySpinner.setVisible(false); + offerAvailabilitySpinnerLabel.setVisible(false); + } + }); + + isSpinnerVisibleSubscription = EasyBind.subscribe(model.isSpinnerVisible, + isSpinnerVisible -> spinner.setProgress(isSpinnerVisible ? -1 : 0)); + + showWarningInvalidBtcDecimalPlacesSubscription = EasyBind.subscribe(model.showWarningInvalidBtcDecimalPlaces, newValue -> { + if (newValue) { + new Popup().warning(BSResources.get("takeOffer.amountPriceBox.warning.invalidBtcDecimalPlaces")).show(); + model.showWarningInvalidBtcDecimalPlaces.set(false); + } + }); + + showTransactionPublishedScreenSubscription = EasyBind.subscribe(model.showTransactionPublishedScreen, newValue -> { + if (newValue && BitsquareApp.DEV_MODE) { + close(); + navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class); + } else if (newValue && model.getTrade() != null && model.getTrade().errorMessageProperty().get() == null) { + String key = "takeOfferSuccessInfo"; + if (preferences.showAgain(key)) { + UserThread.runAfter(() -> new Popup().headLine(BSResources.get("takeOffer.success.headline")) + .feedback(BSResources.get("takeOffer.success.info")) + .actionButtonText("Go to \"Open trades\"") + .dontShowAgainId(key, preferences) + .onAction(() -> { + UserThread.runAfter( + () -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class) + , 100, TimeUnit.MILLISECONDS); + close(); + }) + .onClose(this::close) + .show(), 1); + } else { + close(); + } + } + }); + + noSufficientFeeBinding = EasyBind.combine(model.dataModel.isWalletFunded, model.dataModel.isMainNet, model.dataModel.isFeeFromFundingTxSufficient, + (isWalletFunded, isMainNet, isFeeSufficient) -> isWalletFunded && isMainNet && !isFeeSufficient); + noSufficientFeeSubscription = noSufficientFeeBinding.subscribe((observable, oldValue, newValue) -> { + if (newValue) + new Popup().warning("The mining fee from your funding transaction is not sufficiently high.\n\n" + + "You need to use at least a mining fee of " + + model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) + ".\n\n" + + "The fee used in your funding transaction was only " + + model.formatter.formatCoinWithCode(model.dataModel.feeFromFundingTx) + ".\n\n" + + "The trade transactions might take too much time to be included in " + + "a block if the fee is too low.\n" + + "Please check at your external wallet that you set the required fee and " + + "do a funding again with the correct fee.\n\n" + + "In the \"Funds/Open for withdrawal\" section you can withdraw those funds.") + .closeButtonText("Close") + .onClose(() -> { + close(); + navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class); + }) + .show(); + }); + + balanceSubscription = EasyBind.subscribe(model.dataModel.balance, newValue -> balanceTextField.setBalance(newValue)); + totalToPaySubscription = EasyBind.subscribe(model.dataModel.totalToPayAsCoin, newValue -> balanceTextField.setTargetAmount(newValue)); + } + + private void removeSubscriptions() { + offerWarningSubscription.unsubscribe(); + errorMessageSubscription.unsubscribe(); + isOfferAvailableSubscription.unsubscribe(); + isSpinnerVisibleSubscription.unsubscribe(); + showWarningInvalidBtcDecimalPlacesSubscription.unsubscribe(); + showTransactionPublishedScreenSubscription.unsubscribe(); + noSufficientFeeSubscription.unsubscribe(); + balanceSubscription.unsubscribe(); + totalToPaySubscription.unsubscribe(); + } + + /////////////////////////////////////////////////////////////////////////////////////////// // Build UI elements /////////////////////////////////////////////////////////////////////////////////////////// @@ -597,6 +605,8 @@ public class TakeOfferView extends ActivatableViewAndModel model.onPaymentAccountSelected(paymentAccountsComboBox.getSelectionModel().getSelectedItem())); + Tuple2 tuple2 = addLabelTextField(gridPane, gridRow, "Payment method:", "", Layout.FIRST_ROW_DISTANCE); paymentMethodLabel = tuple2.first; paymentMethodTextField = tuple2.second; @@ -630,14 +640,14 @@ public class TakeOfferView extends ActivatableViewAndModel onShowPayFundsScreen()); + //UserThread.runAfter(() -> nextButton.requestFocus(), 100, TimeUnit.MILLISECONDS); cancelButton1 = new Button(BSResources.get("shared.cancel")); - cancelButton1.setOnAction(e -> close()); cancelButton1.setDefaultButton(false); cancelButton1.setId("cancel-button"); + cancelButton1.setOnAction(e -> close()); offerAvailabilitySpinner = new ProgressIndicator(0); offerAvailabilitySpinner.setPrefSize(18, 18);