Add spinner to create offer UI

This commit is contained in:
Manfred Karrer 2016-03-31 20:38:25 +02:00
parent 04845e4382
commit 9f42c5ac61
4 changed files with 279 additions and 202 deletions

View file

@ -92,9 +92,10 @@ class CreateOfferDataModel extends ActivatableDataModel {
final StringProperty btcCode = new SimpleStringProperty(); final StringProperty btcCode = new SimpleStringProperty();
final BooleanProperty isWalletFunded = new SimpleBooleanProperty(); final BooleanProperty isWalletFunded = new SimpleBooleanProperty();
final BooleanProperty useMBTC = new SimpleBooleanProperty(); final BooleanProperty isMainNet = new SimpleBooleanProperty();
final ObjectProperty<Coin> feeFromFundingTxProperty = new SimpleObjectProperty(Coin.NEGATIVE_SATOSHI); final BooleanProperty isFeeFromFundingTxSufficient = new SimpleBooleanProperty();
final ObjectProperty<Coin> feeFromFundingTxProperty = new SimpleObjectProperty(Coin.NEGATIVE_SATOSHI);
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<>();
@ -134,6 +135,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
this.blockchainService = blockchainService; this.blockchainService = blockchainService;
this.formatter = formatter; this.formatter = formatter;
isMainNet.set(preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET);
offerId = UUID.randomUUID().toString(); offerId = UUID.randomUUID().toString();
addressEntry = walletService.getTradeAddressEntry(offerId); addressEntry = walletService.getTradeAddressEntry(offerId);
offerFeeAsCoin = FeePolicy.getCreateOfferFee(); offerFeeAsCoin = FeePolicy.getCreateOfferFee();

View file

@ -65,6 +65,8 @@ import net.glxn.qrgen.image.ImageType;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.uri.BitcoinURI; import org.bitcoinj.uri.BitcoinURI;
import org.controlsfx.control.PopOver; import org.controlsfx.control.PopOver;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import javax.inject.Inject; import javax.inject.Inject;
@ -87,12 +89,13 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private AddressTextField addressTextField; private AddressTextField addressTextField;
private BalanceTextField balanceTextField; private BalanceTextField balanceTextField;
private TitledGroupBg payFundsPane; private TitledGroupBg payFundsPane;
private ProgressIndicator spinner;
private Button nextButton, cancelButton1, cancelButton2, fundFromSavingsWalletButton, fundFromExternalWalletButton, placeOfferButton; private Button nextButton, cancelButton1, cancelButton2, fundFromSavingsWalletButton, fundFromExternalWalletButton, placeOfferButton;
private InputTextField amountTextField, minAmountTextField, priceTextField, volumeTextField; private InputTextField amountTextField, minAmountTextField, priceTextField, volumeTextField;
private TextField currencyTextField; private TextField currencyTextField;
private Label directionLabel, amountDescriptionLabel, addressLabel, balanceLabel, totalToPayLabel, totalToPayInfoIconLabel, amountBtcLabel, priceCurrencyLabel, private Label directionLabel, amountDescriptionLabel, addressLabel, balanceLabel, totalToPayLabel, totalToPayInfoIconLabel, amountBtcLabel, priceCurrencyLabel,
volumeCurrencyLabel, minAmountBtcLabel, priceDescriptionLabel, volumeDescriptionLabel, currencyTextFieldLabel, volumeCurrencyLabel, minAmountBtcLabel, priceDescriptionLabel, volumeDescriptionLabel, currencyTextFieldLabel,
currencyComboBoxLabel; currencyComboBoxLabel, spinnerInfoLabel;
private TextFieldWithCopyIcon totalToPayTextField; private TextFieldWithCopyIcon totalToPayTextField;
private ComboBox<PaymentAccount> paymentAccountsComboBox; private ComboBox<PaymentAccount> paymentAccountsComboBox;
private ComboBox<TradeCurrency> currencyComboBox; private ComboBox<TradeCurrency> currencyComboBox;
@ -119,6 +122,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private ImageView qrCodeImageView; private ImageView qrCodeImageView;
private ChangeListener<Coin> balanceListener; private ChangeListener<Coin> balanceListener;
private HBox fundingHBox; private HBox fundingHBox;
private Subscription isSpinnerVisibleSubscription;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -165,6 +169,10 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
protected void activate() { protected void activate() {
addBindings(); addBindings();
addListeners(); addListeners();
addSubscriptions();
if (spinner != null && spinner.isVisible())
spinner.setProgress(-1);
directionLabel.setText(model.getDirectionLabel()); directionLabel.setText(model.getDirectionLabel());
amountDescriptionLabel.setText(model.getAmountDescription()); amountDescriptionLabel.setText(model.getAmountDescription());
@ -184,6 +192,11 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
protected void deactivate() { protected void deactivate() {
removeBindings(); removeBindings();
removeListeners(); removeListeners();
removeSubscriptions();
if (spinner != null)
spinner.setProgress(0);
if (balanceTextField != null) if (balanceTextField != null)
balanceTextField.cleanup(); balanceTextField.cleanup();
} }
@ -306,6 +319,8 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
cancelButton1.setManaged(false); cancelButton1.setManaged(false);
cancelButton1.setOnAction(null); cancelButton1.setOnAction(null);
spinner.setProgress(-1);
payFundsPane.setVisible(true); payFundsPane.setVisible(true);
totalToPayLabel.setVisible(true); totalToPayLabel.setVisible(true);
totalToPayInfoIconLabel.setVisible(true); totalToPayInfoIconLabel.setVisible(true);
@ -375,26 +390,18 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
// Bindings, Listeners // Bindings, Listeners
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void addBindings() { private void addBindings() {
amountBtcLabel.textProperty().bind(model.btcCode); amountBtcLabel.textProperty().bind(model.btcCode);
priceCurrencyLabel.textProperty().bind(createStringBinding(() -> priceCurrencyLabel.textProperty().bind(createStringBinding(() -> model.tradeCurrencyCode.get() + "/" + model.btcCode.get(), model.btcCode, model.tradeCurrencyCode));
model.tradeCurrencyCode.get() + "/" + model.btcCode.get(), model.btcCode, model.tradeCurrencyCode));
volumeCurrencyLabel.textProperty().bind(model.tradeCurrencyCode); volumeCurrencyLabel.textProperty().bind(model.tradeCurrencyCode);
minAmountBtcLabel.textProperty().bind(model.btcCode); minAmountBtcLabel.textProperty().bind(model.btcCode);
priceDescriptionLabel.textProperty().bind(createStringBinding(() -> BSResources.get("createOffer.amountPriceBox.priceDescription", model.tradeCurrencyCode.get()), model.tradeCurrencyCode));
priceDescriptionLabel.textProperty().bind(createStringBinding(() -> volumeDescriptionLabel.textProperty().bind(createStringBinding(model.volumeDescriptionLabel::get, model.tradeCurrencyCode, model.volumeDescriptionLabel));
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); 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);
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);
addressTextField.amountAsCoinProperty().bind(model.dataModel.missingCoin); addressTextField.amountAsCoinProperty().bind(model.dataModel.missingCoin);
@ -404,10 +411,14 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
priceTextField.validationResultProperty().bind(model.priceValidationResult); priceTextField.validationResultProperty().bind(model.priceValidationResult);
volumeTextField.validationResultProperty().bind(model.volumeValidationResult); volumeTextField.validationResultProperty().bind(model.volumeValidationResult);
// buttons // funding
fundingHBox.visibleProperty().bind(model.dataModel.isWalletFunded.not().and(model.showPayFundsScreenDisplayed)); fundingHBox.visibleProperty().bind(model.dataModel.isWalletFunded.not().and(model.showPayFundsScreenDisplayed));
fundingHBox.managedProperty().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);
placeOfferButton.visibleProperty().bind(model.dataModel.isWalletFunded.and(model.showPayFundsScreenDisplayed)); placeOfferButton.visibleProperty().bind(model.dataModel.isWalletFunded.and(model.showPayFundsScreenDisplayed));
placeOfferButton.managedProperty().bind(model.dataModel.isWalletFunded.and(model.showPayFundsScreenDisplayed)); placeOfferButton.managedProperty().bind(model.dataModel.isWalletFunded.and(model.showPayFundsScreenDisplayed));
placeOfferButton.disableProperty().bind(model.isPlaceOfferButtonDisabled); placeOfferButton.disableProperty().bind(model.isPlaceOfferButtonDisabled);
@ -435,27 +446,48 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
minAmountTextField.textProperty().unbindBidirectional(model.minAmount); minAmountTextField.textProperty().unbindBidirectional(model.minAmount);
priceTextField.textProperty().unbindBidirectional(model.price); priceTextField.textProperty().unbindBidirectional(model.price);
volumeTextField.textProperty().unbindBidirectional(model.volume); volumeTextField.textProperty().unbindBidirectional(model.volume);
volumeTextField.promptTextProperty().unbindBidirectional(model.volume);
totalToPayTextField.textProperty().unbind(); totalToPayTextField.textProperty().unbind();
addressTextField.amountAsCoinProperty().unbind(); addressTextField.amountAsCoinProperty().unbind();
// Validation
amountTextField.validationResultProperty().unbind(); amountTextField.validationResultProperty().unbind();
minAmountTextField.validationResultProperty().unbind(); minAmountTextField.validationResultProperty().unbind();
priceTextField.validationResultProperty().unbind(); priceTextField.validationResultProperty().unbind();
volumeTextField.validationResultProperty().unbind(); volumeTextField.validationResultProperty().unbind();
// funding
fundingHBox.visibleProperty().unbind(); fundingHBox.visibleProperty().unbind();
fundingHBox.managedProperty().unbind(); fundingHBox.managedProperty().unbind();
spinner.visibleProperty().unbind();
spinner.managedProperty().unbind();
spinnerInfoLabel.visibleProperty().unbind();
spinnerInfoLabel.managedProperty().unbind();
spinnerInfoLabel.textProperty().unbind();
placeOfferButton.visibleProperty().unbind(); placeOfferButton.visibleProperty().unbind();
placeOfferButton.managedProperty().unbind(); placeOfferButton.managedProperty().unbind();
placeOfferButton.disableProperty().unbind(); placeOfferButton.disableProperty().unbind();
cancelButton2.disableProperty().unbind(); cancelButton2.disableProperty().unbind();
// payment account
currencyComboBox.managedProperty().unbind(); currencyComboBox.managedProperty().unbind();
currencyComboBox.prefWidthProperty().unbind();
currencyComboBoxLabel.visibleProperty().unbind(); currencyComboBoxLabel.visibleProperty().unbind();
currencyComboBoxLabel.managedProperty().unbind(); currencyComboBoxLabel.managedProperty().unbind();
currencyTextField.visibleProperty().unbind(); currencyTextField.visibleProperty().unbind();
currencyTextField.managedProperty().unbind(); currencyTextField.managedProperty().unbind();
currencyTextFieldLabel.visibleProperty().unbind(); currencyTextFieldLabel.visibleProperty().unbind();
currencyTextFieldLabel.managedProperty().unbind(); currencyTextFieldLabel.managedProperty().unbind();
currencyComboBox.prefWidthProperty().unbind(); }
volumeTextField.promptTextProperty().unbind();
private void addSubscriptions() {
isSpinnerVisibleSubscription = EasyBind.subscribe(model.isSpinnerVisible,
isSpinnerVisible -> spinner.setProgress(isSpinnerVisible ? -1 : 0));
}
private void removeSubscriptions() {
isSpinnerVisibleSubscription.unsubscribe();
} }
private void createListeners() { private void createListeners() {
@ -787,7 +819,13 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
"Perhaps you don't have one installed?").show(); "Perhaps you don't have one installed?").show();
} }
}); });
fundingHBox.getChildren().addAll(fundFromSavingsWalletButton, label, fundFromExternalWalletButton); spinner = new ProgressIndicator(0);
spinner.setPrefHeight(18);
spinner.setPrefWidth(18);
spinnerInfoLabel = new Label();
spinnerInfoLabel.setPadding(new Insets(5, 0, 0, 0));
fundingHBox.getChildren().addAll(fundFromSavingsWalletButton, label, fundFromExternalWalletButton, spinner, spinnerInfoLabel);
GridPane.setRowIndex(fundingHBox, ++gridRow); GridPane.setRowIndex(fundingHBox, ++gridRow);
GridPane.setColumnIndex(fundingHBox, 1); GridPane.setColumnIndex(fundingHBox, 1);
GridPane.setMargin(fundingHBox, new Insets(15, 10, 0, 0)); GridPane.setMargin(fundingHBox, new Insets(15, 10, 0, 0));

View file

@ -78,6 +78,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
final StringProperty errorMessage = new SimpleStringProperty(); final StringProperty errorMessage = new SimpleStringProperty();
final StringProperty btcCode = new SimpleStringProperty(); final StringProperty btcCode = new SimpleStringProperty();
final StringProperty tradeCurrencyCode = new SimpleStringProperty(); final StringProperty tradeCurrencyCode = new SimpleStringProperty();
final StringProperty spinnerInfoText = new SimpleStringProperty("");
final BooleanProperty isPlaceOfferButtonDisabled = new SimpleBooleanProperty(true); final BooleanProperty isPlaceOfferButtonDisabled = new SimpleBooleanProperty(true);
final BooleanProperty cancelButtonDisabled = new SimpleBooleanProperty(); final BooleanProperty cancelButtonDisabled = new SimpleBooleanProperty();
@ -87,6 +88,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty(); final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty();
final BooleanProperty placeOfferCompleted = new SimpleBooleanProperty(); final BooleanProperty placeOfferCompleted = new SimpleBooleanProperty();
final BooleanProperty showPayFundsScreenDisplayed = new SimpleBooleanProperty(); final BooleanProperty showPayFundsScreenDisplayed = new SimpleBooleanProperty();
final BooleanProperty showTransactionPublishedScreen = new SimpleBooleanProperty();
final BooleanProperty isSpinnerVisible = new SimpleBooleanProperty();
final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>(); final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>();
final ObjectProperty<InputValidator.ValidationResult> minAmountValidationResult = new final ObjectProperty<InputValidator.ValidationResult> minAmountValidationResult = new
@ -151,8 +154,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
setMinAmountToModel(); setMinAmountToModel();
setPriceToModel(); setPriceToModel();
calculateVolume(); calculateVolume();
dataModel.calculateTotalToPay(); dataModel.calculateTotalToPay();
updateButtonDisableState(); updateButtonDisableState();
updateSpinnerInfo();
} }
addBindings(); addBindings();
@ -345,6 +350,8 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
placeOfferCompleted.set(true); placeOfferCompleted.set(true);
errorMessage.set(null); errorMessage.set(null);
}); });
updateSpinnerInfo();
} }
private void stopTimeoutTimer() { private void stopTimeoutTimer() {
@ -392,6 +399,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
void onShowPayFundsScreen() { void onShowPayFundsScreen() {
showPayFundsScreenDisplayed.set(true); showPayFundsScreenDisplayed.set(true);
updateSpinnerInfo();
} }
boolean useSavingsWalletForFunding() { boolean useSavingsWalletForFunding() {
@ -626,6 +634,24 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
return fiatValidator.validate(input); return fiatValidator.validate(input);
} }
private void updateSpinnerInfo() {
if (!showPayFundsScreenDisplayed.get() ||
errorMessage.get() != null ||
showTransactionPublishedScreen.get()) {
spinnerInfoText.set("");
} else if (dataModel.isWalletFunded.get()) {
if (dataModel.isFeeFromFundingTxSufficient.get()) {
spinnerInfoText.set("");
} else {
spinnerInfoText.set("Check if funding tx miner fee is sufficient...");
}
} else {
spinnerInfoText.set("Waiting for funds...");
}
isSpinnerVisible.set(!spinnerInfoText.get().isEmpty());
}
private void updateButtonDisableState() { private void updateButtonDisableState() {
log.debug("updateButtonDisableState"); log.debug("updateButtonDisableState");
boolean inputDataValid = isBtcInputValid(amount.get()).isValid && boolean inputDataValid = isBtcInputValid(amount.get()).isValid &&

View file

@ -149,204 +149,43 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
}; };
} }
@Override @Override
protected void activate() { protected void activate() {
paymentAccountsComboBox.setOnAction(e -> onPaymentAccountsComboBoxSelected()); addBindings();
addSubscriptions();
amountTextField.focusedProperty().addListener(amountFocusedListener); 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()) if (offerAvailabilitySpinner != null && offerAvailabilitySpinner.isVisible())
offerAvailabilitySpinner.setProgress(-1); offerAvailabilitySpinner.setProgress(-1);
if (spinner != null && spinner.isVisible()) if (spinner != null && spinner.isVisible())
spinner.setProgress(-1); spinner.setProgress(-1);
balanceSubscription = EasyBind.subscribe(model.dataModel.balance, newValue -> balanceTextField.setBalance(newValue)); volumeCurrencyLabel.setText(model.dataModel.getCurrencyCode());
totalToPaySubscription = EasyBind.subscribe(model.dataModel.totalToPayAsCoin, newValue -> balanceTextField.setTargetAmount(newValue)); 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 @Override
protected void deactivate() { protected void deactivate() {
paymentAccountsComboBox.setOnAction(null); removeBindings();
removeSubscriptions();
amountTextField.focusedProperty().removeListener(amountFocusedListener); 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) if (offerAvailabilitySpinner != null)
offerAvailabilitySpinner.setProgress(0); offerAvailabilitySpinner.setProgress(0);
if (spinner != null) if (spinner != null)
spinner.setProgress(0); spinner.setProgress(0);
balanceSubscription.unsubscribe(); if (balanceTextField != null)
totalToPaySubscription.unsubscribe(); balanceTextField.cleanup();
} }
@ -444,10 +283,6 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
} }
} }
private void onPaymentAccountsComboBoxSelected() {
model.onPaymentAccountSelected(paymentAccountsComboBox.getSelectionModel().getSelectedItem());
}
private void onShowPayFundsScreen() { private void onShowPayFundsScreen() {
model.onShowPayFundsScreen(); model.onShowPayFundsScreen();
@ -537,6 +372,179 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
closeHandler.close(); closeHandler.close();
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Bindings, Listeners
///////////////////////////////////////////////////////////////////////////////////////////
private void addBindings() {
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);
priceCurrencyLabel.textProperty().bind(createStringBinding(() -> 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 // Build UI elements
@ -597,6 +605,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
}); });
paymentAccountsComboBox.setVisible(false); paymentAccountsComboBox.setVisible(false);
paymentAccountsComboBox.setManaged(false); paymentAccountsComboBox.setManaged(false);
paymentAccountsComboBox.setOnAction(e -> model.onPaymentAccountSelected(paymentAccountsComboBox.getSelectionModel().getSelectedItem()));
Tuple2<Label, TextField> tuple2 = addLabelTextField(gridPane, gridRow, "Payment method:", "", Layout.FIRST_ROW_DISTANCE); Tuple2<Label, TextField> tuple2 = addLabelTextField(gridPane, gridRow, "Payment method:", "", Layout.FIRST_ROW_DISTANCE);
paymentMethodLabel = tuple2.first; paymentMethodLabel = tuple2.first;
paymentMethodTextField = tuple2.second; paymentMethodTextField = tuple2.second;
@ -630,14 +640,14 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
nextButton = new Button(BSResources.get("takeOffer.amountPriceBox.next")); nextButton = new Button(BSResources.get("takeOffer.amountPriceBox.next"));
nextButton.setDefaultButton(true); nextButton.setDefaultButton(true);
nextButton.disableProperty().bind(model.isNextButtonDisabled);
nextButton.setOnAction(e -> onShowPayFundsScreen()); nextButton.setOnAction(e -> onShowPayFundsScreen());
//UserThread.runAfter(() -> nextButton.requestFocus(), 100, TimeUnit.MILLISECONDS); //UserThread.runAfter(() -> nextButton.requestFocus(), 100, TimeUnit.MILLISECONDS);
cancelButton1 = new Button(BSResources.get("shared.cancel")); cancelButton1 = new Button(BSResources.get("shared.cancel"));
cancelButton1.setOnAction(e -> close());
cancelButton1.setDefaultButton(false); cancelButton1.setDefaultButton(false);
cancelButton1.setId("cancel-button"); cancelButton1.setId("cancel-button");
cancelButton1.setOnAction(e -> close());
offerAvailabilitySpinner = new ProgressIndicator(0); offerAvailabilitySpinner = new ProgressIndicator(0);
offerAvailabilitySpinner.setPrefSize(18, 18); offerAvailabilitySpinner.setPrefSize(18, 18);