Add spinner, status info and blocking popup at place offer

This commit is contained in:
Manfred Karrer 2016-03-03 01:49:54 +01:00
parent 8324c888e4
commit ba727ebb16
15 changed files with 209 additions and 177 deletions

View file

@ -61,14 +61,12 @@ import org.bitcoinj.store.BlockStoreException;
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.controlsfx.dialog.Dialogs; import org.controlsfx.dialog.Dialogs;
import org.reactfx.EventStreams; import org.reactfx.EventStreams;
import org.reactfx.util.FxTimer;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.security.Security; import java.security.Security;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -333,9 +331,9 @@ public class BitsquareApp extends Application {
}); });
}); });
// we wait max 5 sec. // we wait max 5 sec.
FxTimer.runLater(Duration.ofMillis(5000), resultHandler::handleResult); UserThread.runAfter(resultHandler::handleResult, 5);
} else { } else {
FxTimer.runLater(Duration.ofMillis(500), resultHandler::handleResult); UserThread.runAfter(resultHandler::handleResult, 1);
} }
} catch (Throwable t) { } catch (Throwable t) {
log.info("App shutdown failed with exception"); log.info("App shutdown failed with exception");

View file

@ -55,8 +55,6 @@ import javafx.scene.text.TextAlignment;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import javafx.stage.Stage; import javafx.stage.Stage;
import javafx.util.Callback; import javafx.util.Callback;
import org.reactfx.util.FxTimer;
import org.reactfx.util.Timer;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
@ -66,10 +64,10 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.TimeUnit;
// will be probably only used for arbitration communication, will be renamed and the icon changed // will be probably only used for arbitration communication, will be renamed and the icon changed
@FxmlView @FxmlView
@ -214,7 +212,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
inputTextArea.setDisable(true); inputTextArea.setDisable(true);
inputTextArea.clear(); inputTextArea.clear();
final Timer timer = FxTimer.runLater(Duration.ofMillis(500), () -> { io.bitsquare.common.Timer timer = UserThread.runAfter(() -> {
sendMsgInfoLabel.setVisible(true); sendMsgInfoLabel.setVisible(true);
sendMsgInfoLabel.setManaged(true); sendMsgInfoLabel.setManaged(true);
sendMsgInfoLabel.setText("Sending Message..."); sendMsgInfoLabel.setText("Sending Message...");
@ -222,7 +220,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
sendMsgProgressIndicator.setProgress(-1); sendMsgProgressIndicator.setProgress(-1);
sendMsgProgressIndicator.setVisible(true); sendMsgProgressIndicator.setVisible(true);
sendMsgProgressIndicator.setManaged(true); sendMsgProgressIndicator.setManaged(true);
}); }, 500, TimeUnit.MILLISECONDS);
arrivedPropertyListener = (observable, oldValue, newValue) -> { arrivedPropertyListener = (observable, oldValue, newValue) -> {
if (newValue) { if (newValue) {
@ -242,14 +240,14 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
disputeCommunicationMessage.storedInMailboxProperty().addListener(storedInMailboxPropertyListener); disputeCommunicationMessage.storedInMailboxProperty().addListener(storedInMailboxPropertyListener);
} }
private void hideSendMsgInfo(Timer timer) { private void hideSendMsgInfo(io.bitsquare.common.Timer timer) {
timer.stop(); timer.stop();
inputTextArea.setDisable(false); inputTextArea.setDisable(false);
FxTimer.runLater(Duration.ofMillis(5000), () -> { UserThread.runAfter(() -> {
sendMsgInfoLabel.setVisible(false); sendMsgInfoLabel.setVisible(false);
sendMsgInfoLabel.setManaged(false); sendMsgInfoLabel.setManaged(false);
}); }, 5);
sendMsgProgressIndicator.setProgress(0); sendMsgProgressIndicator.setProgress(0);
sendMsgProgressIndicator.setVisible(false); sendMsgProgressIndicator.setVisible(false);
sendMsgProgressIndicator.setManaged(false); sendMsgProgressIndicator.setManaged(false);

View file

@ -21,6 +21,7 @@ import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.app.BitsquareApp; import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.FeePolicy;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2; import io.bitsquare.common.util.Tuple2;
import io.bitsquare.common.util.Tuple3; import io.bitsquare.common.util.Tuple3;
import io.bitsquare.common.util.Utilities; import io.bitsquare.common.util.Utilities;
@ -62,10 +63,9 @@ import javafx.stage.Window;
import javafx.util.StringConverter; import javafx.util.StringConverter;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.controlsfx.control.PopOver; import org.controlsfx.control.PopOver;
import org.reactfx.util.FxTimer;
import javax.inject.Inject; import javax.inject.Inject;
import java.time.Duration; import java.util.concurrent.TimeUnit;
import static io.bitsquare.gui.util.FormBuilder.*; import static io.bitsquare.gui.util.FormBuilder.*;
import static javafx.beans.binding.Bindings.createStringBinding; import static javafx.beans.binding.Bindings.createStringBinding;
@ -81,17 +81,16 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private ImageView imageView; private ImageView imageView;
private AddressTextField addressTextField; private AddressTextField addressTextField;
private BalanceTextField balanceTextField; private BalanceTextField balanceTextField;
private ProgressIndicator placeOfferSpinner; private ProgressIndicator spinner;
private TitledGroupBg payFundsPane; private TitledGroupBg payFundsPane;
private Button nextButton, cancelButton1, cancelButton2, createOfferButton; private Button nextButton, cancelButton1, cancelButton2, placeOfferButton;
private InputTextField amountTextField, minAmountTextField, priceTextField, volumeTextField; private InputTextField amountTextField, minAmountTextField, priceTextField, volumeTextField;
private TextField totalToPayTextField, currencyTextField; private TextField totalToPayTextField, 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, placeOfferSpinnerInfoLabel, currencyTextFieldLabel, volumeCurrencyLabel, minAmountBtcLabel, priceDescriptionLabel, volumeDescriptionLabel, spinnerInfoLabel, currencyTextFieldLabel,
currencyComboBoxLabel; currencyComboBoxLabel;
private ComboBox<PaymentAccount> paymentAccountsComboBox; private ComboBox<PaymentAccount> paymentAccountsComboBox;
private ComboBox<TradeCurrency> currencyComboBox; private ComboBox<TradeCurrency> currencyComboBox;
private PopOver totalToPayInfoPopover; private PopOver totalToPayInfoPopover;
private OfferView.CloseHandler closeHandler; private OfferView.CloseHandler closeHandler;
@ -104,8 +103,8 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private ChangeListener<Boolean> showWarningInvalidFiatDecimalPlacesPlacesListener; private ChangeListener<Boolean> showWarningInvalidFiatDecimalPlacesPlacesListener;
private ChangeListener<Boolean> showWarningAdjustedVolumeListener; private ChangeListener<Boolean> showWarningAdjustedVolumeListener;
private ChangeListener<String> errorMessageListener; private ChangeListener<String> errorMessageListener;
private ChangeListener<Boolean> isPlaceOfferSpinnerVisibleListener; private ChangeListener<Boolean> isSpinnerVisibleListener;
private ChangeListener<Boolean> showTransactionPublishedScreen; private ChangeListener<Boolean> placeOfferCompletedListener;
private ChangeListener<Coin> feeFromFundingTxListener; private ChangeListener<Coin> feeFromFundingTxListener;
private EventHandler<ActionEvent> paymentAccountsComboBoxSelectionHandler; private EventHandler<ActionEvent> paymentAccountsComboBoxSelectionHandler;
@ -175,6 +174,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
removeListeners(); removeListeners();
if (balanceTextField != null) if (balanceTextField != null)
balanceTextField.cleanup(); balanceTextField.cleanup();
if (spinner != null)
spinner.setProgress(0);
} }
@ -186,32 +188,32 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
model.initWithData(direction, tradeCurrency); model.initWithData(direction, tradeCurrency);
ImageView iconView = new ImageView(); ImageView iconView = new ImageView();
createOfferButton.setGraphic(iconView); placeOfferButton.setGraphic(iconView);
if (direction == Offer.Direction.BUY) { if (direction == Offer.Direction.BUY) {
imageView.setId("image-buy-large"); imageView.setId("image-buy-large");
createOfferButton.setId("buy-button-big"); placeOfferButton.setId("buy-button-big");
placeOfferButton.setText("Review place offer for buying bitcoin");
nextButton.setId("buy-button"); nextButton.setId("buy-button");
createOfferButton.setText("Review place offer for buying bitcoin");
iconView.setId("image-buy-white"); iconView.setId("image-buy-white");
} else { } else {
imageView.setId("image-sell-large"); imageView.setId("image-sell-large");
// only needed for sell // only needed for sell
totalToPayTextField.setPromptText(BSResources.get("createOffer.fundsBox.totalsNeeded.prompt")); totalToPayTextField.setPromptText(BSResources.get("createOffer.fundsBox.totalsNeeded.prompt"));
createOfferButton.setId("sell-button-big"); placeOfferButton.setId("sell-button-big");
placeOfferButton.setText("Review place offer for selling bitcoin");
nextButton.setId("sell-button"); nextButton.setId("sell-button");
createOfferButton.setText("Review place offer for selling bitcoin");
iconView.setId("image-sell-white"); iconView.setId("image-sell-white");
} }
} }
// called form parent as the view does not get notified when the tab is closed // called form parent as the view does not get notified when the tab is closed
public void onClose() { public void onClose() {
// we use model.requestPlaceOfferSuccess to not react on close caused by placeOffer // we use model.placeOfferCompleted to not react on close which was triggered by a successful placeOffer
if (model.dataModel.isWalletFunded.get() && !model.requestPlaceOfferSuccess.get()) if (model.dataModel.isWalletFunded.get() && !model.placeOfferCompleted.get())
new Popup().warning("You have already funds paid in.\n" + new Popup().warning("You have already funds paid in.\n" +
"In the \"Funds/Open for withdrawal\" section you can withdraw those funds.").show(); "In the \"Funds/Available for withdrawal\" section you can withdraw those funds.").show();
} }
public void setCloseHandler(OfferView.CloseHandler closeHandler) { public void setCloseHandler(OfferView.CloseHandler closeHandler) {
@ -230,7 +232,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
if (model.isBootstrapped()) { if (model.isBootstrapped()) {
if (model.hasAcceptedArbitrators()) { if (model.hasAcceptedArbitrators()) {
Offer offer = model.createAndGetOffer(); Offer offer = model.createAndGetOffer();
offerDetailsWindow.onPlaceOffer(model::onPlaceOffer) offerDetailsWindow.onPlaceOffer(() ->
model.onPlaceOffer(offer, () ->
offerDetailsWindow.hide()))
.show(offer); .show(offer);
} else { } else {
new Popup().warning("You have no arbitrator selected.\n" + new Popup().warning("You have no arbitrator selected.\n" +
@ -253,6 +257,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
currencyComboBox.setMouseTransparent(true); currencyComboBox.setMouseTransparent(true);
paymentAccountsComboBox.setMouseTransparent(true); paymentAccountsComboBox.setMouseTransparent(true);
spinner.setVisible(true);
spinner.setProgress(-1);
if (!BitsquareApp.DEV_MODE) { if (!BitsquareApp.DEV_MODE) {
String key = "securityDepositInfo"; String key = "securityDepositInfo";
new Popup().backgroundInfo("To ensure that both traders follow the trade protocol they need to pay a security deposit.\n\n" + new Popup().backgroundInfo("To ensure that both traders follow the trade protocol they need to pay a security deposit.\n\n" +
@ -297,7 +304,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
addressTextField.setVisible(true); addressTextField.setVisible(true);
balanceLabel.setVisible(true); balanceLabel.setVisible(true);
balanceTextField.setVisible(true); balanceTextField.setVisible(true);
createOfferButton.setVisible(true); placeOfferButton.setVisible(true);
cancelButton2.setVisible(true); cancelButton2.setVisible(true);
//root.requestFocus(); //root.requestFocus();
@ -379,9 +386,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
volumeTextField.validationResultProperty().bind(model.volumeValidationResult); volumeTextField.validationResultProperty().bind(model.volumeValidationResult);
// buttons // buttons
createOfferButton.disableProperty().bind(model.isPlaceOfferButtonDisabled); placeOfferButton.disableProperty().bind(model.isPlaceOfferButtonDisabled);
cancelButton2.disableProperty().bind(model.cancelButtonDisabled);
placeOfferSpinnerInfoLabel.visibleProperty().bind(model.isPlaceOfferSpinnerVisible); spinnerInfoLabel.visibleProperty().bind(model.isSpinnerVisible);
// payment account // payment account
currencyComboBox.prefWidthProperty().bind(paymentAccountsComboBox.widthProperty()); currencyComboBox.prefWidthProperty().bind(paymentAccountsComboBox.widthProperty());
@ -411,8 +418,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
minAmountTextField.validationResultProperty().unbind(); minAmountTextField.validationResultProperty().unbind();
priceTextField.validationResultProperty().unbind(); priceTextField.validationResultProperty().unbind();
volumeTextField.validationResultProperty().unbind(); volumeTextField.validationResultProperty().unbind();
createOfferButton.disableProperty().unbind(); placeOfferButton.disableProperty().unbind();
placeOfferSpinnerInfoLabel.visibleProperty().unbind(); cancelButton2.disableProperty().unbind();
spinnerInfoLabel.visibleProperty().unbind();
currencyComboBox.managedProperty().unbind(); currencyComboBox.managedProperty().unbind();
currencyComboBoxLabel.visibleProperty().unbind(); currencyComboBoxLabel.visibleProperty().unbind();
currencyComboBoxLabel.managedProperty().unbind(); currencyComboBoxLabel.managedProperty().unbind();
@ -463,14 +471,14 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
}; };
errorMessageListener = (o, oldValue, newValue) -> { errorMessageListener = (o, oldValue, newValue) -> {
if (newValue != null) if (newValue != null)
new Popup().error(BSResources.get("createOffer.amountPriceBox.error.message", model.errorMessage.get()) + UserThread.runAfter(() -> new Popup().error(BSResources.get("createOffer.amountPriceBox.error.message", model.errorMessage.get()) +
"\n\nThere have no funds left your wallet yet.\n" + "\n\nThere have no funds left your wallet yet.\n" +
"Please try to restart you application and check your network connection to see if you can resolve the issue.") "Please try to restart you application and check your network connection to see if you can resolve the issue.")
.show(); .show(), 100, TimeUnit.MILLISECONDS);
}; };
isPlaceOfferSpinnerVisibleListener = (ov, oldValue, newValue) -> { isSpinnerVisibleListener = (ov, oldValue, newValue) -> {
placeOfferSpinner.setProgress(newValue ? -1 : 0); spinner.setProgress(newValue ? -1 : 0);
placeOfferSpinner.setVisible(newValue); spinner.setVisible(newValue);
}; };
feeFromFundingTxListener = (observable, oldValue, newValue) -> { feeFromFundingTxListener = (observable, oldValue, newValue) -> {
@ -503,29 +511,25 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
volumeTextField.clear(); volumeTextField.clear();
}; };
showTransactionPublishedScreen = (o, oldValue, newValue) -> { placeOfferCompletedListener = (o, oldValue, newValue) -> {
if (BitsquareApp.DEV_MODE) { if (BitsquareApp.DEV_MODE) {
newValue = false;
close(); close();
navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class); navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class);
} } else if (newValue) {
// We need a bit of delay to avoid issues with fade out/fade in of 2 popups
if (newValue) { UserThread.runAfter(() ->
FxTimer.runLater(Duration.ofMillis(100),
() -> {
new Popup().headLine(BSResources.get("createOffer.success.headline")) new Popup().headLine(BSResources.get("createOffer.success.headline"))
.feedback(BSResources.get("createOffer.success.info")) .feedback(BSResources.get("createOffer.success.info"))
.actionButtonText("Go to \"My open offers\"") .actionButtonText("Go to \"My open offers\"")
.onAction(() -> { .onAction(() -> {
close(); close();
FxTimer.runLater(Duration.ofMillis(100), UserThread.runAfter(() ->
() -> navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class) navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class),
); 100, TimeUnit.MILLISECONDS);
}) })
.onClose(() -> close()) .onClose(this::close)
.show(); .show(),
} 100, TimeUnit.MILLISECONDS);
);
} }
}; };
} }
@ -544,10 +548,10 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
model.showWarningInvalidFiatDecimalPlaces.addListener(showWarningInvalidFiatDecimalPlacesPlacesListener); model.showWarningInvalidFiatDecimalPlaces.addListener(showWarningInvalidFiatDecimalPlacesPlacesListener);
model.showWarningAdjustedVolume.addListener(showWarningAdjustedVolumeListener); model.showWarningAdjustedVolume.addListener(showWarningAdjustedVolumeListener);
model.errorMessage.addListener(errorMessageListener); model.errorMessage.addListener(errorMessageListener);
model.isPlaceOfferSpinnerVisible.addListener(isPlaceOfferSpinnerVisibleListener); model.isSpinnerVisible.addListener(isSpinnerVisibleListener);
model.dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener); model.dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
model.requestPlaceOfferSuccess.addListener(showTransactionPublishedScreen); model.placeOfferCompleted.addListener(placeOfferCompletedListener);
// UI actions // UI actions
paymentAccountsComboBox.setOnAction(paymentAccountsComboBoxSelectionHandler); paymentAccountsComboBox.setOnAction(paymentAccountsComboBoxSelectionHandler);
@ -568,10 +572,10 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
model.showWarningInvalidFiatDecimalPlaces.removeListener(showWarningInvalidFiatDecimalPlacesPlacesListener); model.showWarningInvalidFiatDecimalPlaces.removeListener(showWarningInvalidFiatDecimalPlacesPlacesListener);
model.showWarningAdjustedVolume.removeListener(showWarningAdjustedVolumeListener); model.showWarningAdjustedVolume.removeListener(showWarningAdjustedVolumeListener);
model.errorMessage.removeListener(errorMessageListener); model.errorMessage.removeListener(errorMessageListener);
model.isPlaceOfferSpinnerVisible.removeListener(isPlaceOfferSpinnerVisibleListener); model.isSpinnerVisible.removeListener(isSpinnerVisibleListener);
model.dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener); model.dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
model.requestPlaceOfferSuccess.removeListener(showTransactionPublishedScreen); model.placeOfferCompleted.removeListener(placeOfferCompletedListener);
// UI actions // UI actions
paymentAccountsComboBox.setOnAction(null); paymentAccountsComboBox.setOnAction(null);
@ -713,17 +717,19 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
balanceTextField.setVisible(false); balanceTextField.setVisible(false);
Tuple3<Button, ProgressIndicator, Label> placeOfferTuple = addButtonWithStatusAfterGroup(gridPane, ++gridRow, ""); Tuple3<Button, ProgressIndicator, Label> placeOfferTuple = addButtonWithStatusAfterGroup(gridPane, ++gridRow, "");
createOfferButton = placeOfferTuple.first; placeOfferButton = placeOfferTuple.first;
createOfferButton.setVisible(false); placeOfferButton.setVisible(false);
createOfferButton.setOnAction(e -> onPlaceOffer()); placeOfferButton.setOnAction(e -> onPlaceOffer());
createOfferButton.setMinHeight(40); placeOfferButton.setMinHeight(40);
createOfferButton.setPadding(new Insets(0, 20, 0, 20)); placeOfferButton.setPadding(new Insets(0, 20, 0, 20));
placeOfferSpinner = placeOfferTuple.second; spinner = placeOfferTuple.second;
placeOfferSpinner.setPrefSize(18, 18); spinner.setProgress(0);
placeOfferSpinnerInfoLabel = placeOfferTuple.third; spinner.setVisible(false);
placeOfferSpinnerInfoLabel.textProperty().bind(model.placeOfferSpinnerInfoText); spinner.setPrefSize(18, 18);
placeOfferSpinnerInfoLabel.setVisible(false); spinnerInfoLabel = placeOfferTuple.third;
spinnerInfoLabel.textProperty().bind(model.spinnerInfoText);
spinnerInfoLabel.setVisible(false);
cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel")); cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel"));
cancelButton2.setOnAction(e -> close()); cancelButton2.setOnAction(e -> close());

View file

@ -18,6 +18,9 @@
package io.bitsquare.gui.main.offer.createoffer; package io.bitsquare.gui.main.offer.createoffer;
import io.bitsquare.app.BitsquareApp; import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.common.Timer;
import io.bitsquare.common.UserThread;
import io.bitsquare.gui.common.model.ActivatableWithDataModel; import io.bitsquare.gui.common.model.ActivatableWithDataModel;
import io.bitsquare.gui.common.model.ViewModel; import io.bitsquare.gui.common.model.ViewModel;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
@ -62,15 +65,16 @@ 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 placeOfferSpinnerInfoText = new SimpleStringProperty(); final StringProperty spinnerInfoText = new SimpleStringProperty("Waiting for funds...");
final BooleanProperty isPlaceOfferButtonDisabled = new SimpleBooleanProperty(true); final BooleanProperty isPlaceOfferButtonDisabled = new SimpleBooleanProperty(true);
final BooleanProperty cancelButtonDisabled = new SimpleBooleanProperty();
final BooleanProperty isNextButtonDisabled = new SimpleBooleanProperty(true); final BooleanProperty isNextButtonDisabled = new SimpleBooleanProperty(true);
final BooleanProperty isPlaceOfferSpinnerVisible = new SimpleBooleanProperty(false); final BooleanProperty isSpinnerVisible = new SimpleBooleanProperty(true);
final BooleanProperty showWarningAdjustedVolume = new SimpleBooleanProperty(); final BooleanProperty showWarningAdjustedVolume = new SimpleBooleanProperty();
final BooleanProperty showWarningInvalidFiatDecimalPlaces = new SimpleBooleanProperty(); final BooleanProperty showWarningInvalidFiatDecimalPlaces = new SimpleBooleanProperty();
final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty(); final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty();
final BooleanProperty requestPlaceOfferSuccess = new SimpleBooleanProperty(); final BooleanProperty placeOfferCompleted = 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
@ -92,10 +96,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
private ChangeListener<Fiat> volumeAsFiatListener; private ChangeListener<Fiat> volumeAsFiatListener;
private ChangeListener<Boolean> isWalletFundedListener; private ChangeListener<Boolean> isWalletFundedListener;
private ChangeListener<Coin> feeFromFundingTxListener; private ChangeListener<Coin> feeFromFundingTxListener;
private ChangeListener<Boolean> requestPlaceOfferSuccessListener;
private ChangeListener<String> requestPlaceOfferErrorMessageListener; private ChangeListener<String> requestPlaceOfferErrorMessageListener;
private ChangeListener<String> errorMessageListener; private ChangeListener<String> errorMessageListener;
private Offer offer; private Offer offer;
private Timer timeoutTimer;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -156,6 +160,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
protected void deactivate() { protected void deactivate() {
removeBindings(); removeBindings();
removeListeners(); removeListeners();
stopTimeoutTimer();
} }
private void addBindings() { private void addBindings() {
@ -232,27 +237,19 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
isWalletFundedListener = (ov, oldValue, newValue) -> { isWalletFundedListener = (ov, oldValue, newValue) -> {
updateButtonDisableState(); updateButtonDisableState();
isPlaceOfferSpinnerVisible.set(true); spinnerInfoText.set("Checking funding tx miner fee...");
placeOfferSpinnerInfoText.set("Checking funding tx miner fee...");
}; };
feeFromFundingTxListener = (ov, oldValue, newValue) -> { feeFromFundingTxListener = (ov, oldValue, newValue) -> {
updateButtonDisableState(); updateButtonDisableState();
if (newValue.isPositive()) { if (newValue.compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0) {
isPlaceOfferSpinnerVisible.set(false); isSpinnerVisible.set(false);
placeOfferSpinnerInfoText.set(""); spinnerInfoText.set("");
}
};
requestPlaceOfferSuccessListener = (ov, oldValue, newValue) -> {
if (newValue) {
isPlaceOfferButtonDisabled.set(newValue);
isPlaceOfferSpinnerVisible.set(false);
placeOfferSpinnerInfoText.set("");
} }
}; };
requestPlaceOfferErrorMessageListener = (ov, oldValue, newValue) -> { requestPlaceOfferErrorMessageListener = (ov, oldValue, newValue) -> {
if (newValue != null) { if (newValue != null) {
isPlaceOfferSpinnerVisible.set(false); isSpinnerVisible.set(false);
placeOfferSpinnerInfoText.set(""); spinnerInfoText.set("");
} }
}; };
} }
@ -273,7 +270,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener); dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
dataModel.isWalletFunded.addListener(isWalletFundedListener); dataModel.isWalletFunded.addListener(isWalletFundedListener);
requestPlaceOfferSuccess.addListener(requestPlaceOfferSuccessListener);
errorMessage.addListener(requestPlaceOfferErrorMessageListener); errorMessage.addListener(requestPlaceOfferErrorMessageListener);
} }
@ -292,7 +288,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener); dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
dataModel.isWalletFunded.removeListener(isWalletFundedListener); dataModel.isWalletFunded.removeListener(isWalletFundedListener);
requestPlaceOfferSuccess.removeListener(requestPlaceOfferSuccessListener);
errorMessage.removeListener(requestPlaceOfferErrorMessageListener); errorMessage.removeListener(requestPlaceOfferErrorMessageListener);
if (offer != null && errorMessageListener != null) if (offer != null && errorMessageListener != null)
@ -313,25 +308,51 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
// UI actions // UI actions
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
void onPlaceOffer(Offer offer) { void onPlaceOffer(Offer offer, Runnable resultHandler) {
errorMessage.set(null); errorMessage.set(null);
isPlaceOfferSpinnerVisible.set(true);
requestPlaceOfferSuccess.set(false);
placeOfferSpinnerInfoText.set(BSResources.get("createOffer.fundsBox.placeOfferSpinnerInfo"));
isPlaceOfferButtonDisabled.set(true);
cancelButtonDisabled.set(true);
if (timeoutTimer == null) {
timeoutTimer = UserThread.runAfter(() -> {
stopTimeoutTimer();
isPlaceOfferButtonDisabled.set(false);
cancelButtonDisabled.set(false);
errorMessage.set("A timeout occurred at publishing the offer.");
resultHandler.run();
}, 30);
}
errorMessageListener = (observable, oldValue, newValue) -> { errorMessageListener = (observable, oldValue, newValue) -> {
if (newValue != null) { if (newValue != null) {
stopTimeoutTimer();
isPlaceOfferButtonDisabled.set(false);
cancelButtonDisabled.set(false);
if (offer.getState() == Offer.State.OFFER_FEE_PAID) if (offer.getState() == Offer.State.OFFER_FEE_PAID)
this.errorMessage.set(newValue + errorMessage.set(newValue +
"\n\nThe offer fee is already paid. In the worst case you have lost that fee. " + "\n\nThe offer fee is already paid. In the worst case you have lost that fee. " +
"We are sorry about that but keep in mind it is a very small amount.\n" + "We are sorry about that but keep in mind it is a very small amount.\n" +
"Please try to restart you application and check your network connection to see if you can resolve the issue."); "Please try to restart you application and check your network connection to see if you can resolve the issue.");
else else
this.errorMessage.set(newValue); errorMessage.set(newValue);
resultHandler.run();
} }
}; };
offer.errorMessageProperty().addListener(errorMessageListener); offer.errorMessageProperty().addListener(errorMessageListener);
dataModel.onPlaceOffer(offer, transaction -> requestPlaceOfferSuccess.set(true)); dataModel.onPlaceOffer(offer, transaction -> {
stopTimeoutTimer();
placeOfferCompleted.set(true);
resultHandler.run();
errorMessage.set(null);
});
}
private void stopTimeoutTimer() {
if (timeoutTimer != null) {
timeoutTimer.stop();
timeoutTimer = null;
}
} }
public void onPaymentAccountSelected(PaymentAccount paymentAccount) { public void onPaymentAccountSelected(PaymentAccount paymentAccount) {

View file

@ -21,6 +21,7 @@ import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon; import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.app.BitsquareApp; import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.FeePolicy; import io.bitsquare.btc.FeePolicy;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2; import io.bitsquare.common.util.Tuple2;
import io.bitsquare.common.util.Tuple3; import io.bitsquare.common.util.Tuple3;
import io.bitsquare.common.util.Utilities; import io.bitsquare.common.util.Utilities;
@ -59,10 +60,9 @@ import org.bitcoinj.core.Coin;
import org.controlsfx.control.PopOver; import org.controlsfx.control.PopOver;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.Subscription;
import org.reactfx.util.FxTimer;
import javax.inject.Inject; import javax.inject.Inject;
import java.time.Duration; import java.util.concurrent.TimeUnit;
import static io.bitsquare.gui.util.FormBuilder.*; import static io.bitsquare.gui.util.FormBuilder.*;
import static javafx.beans.binding.Bindings.createStringBinding; import static javafx.beans.binding.Bindings.createStringBinding;
@ -223,21 +223,18 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
} }
if (newValue && model.getTrade() != null && model.getTrade().errorMessageProperty().get() == null) { if (newValue && model.getTrade() != null && model.getTrade().errorMessageProperty().get() == null) {
FxTimer.runLater(Duration.ofMillis(100), UserThread.runAfter(
() -> { () -> new Popup().headLine(BSResources.get("takeOffer.success.headline"))
new Popup().headLine(BSResources.get("takeOffer.success.headline"))
.feedback(BSResources.get("takeOffer.success.info")) .feedback(BSResources.get("takeOffer.success.info"))
.actionButtonText("Go to \"Open trades\"") .actionButtonText("Go to \"Open trades\"")
.onAction(() -> { .onAction(() -> {
close(); close();
FxTimer.runLater(Duration.ofMillis(100), UserThread.runAfter(
() -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class) () -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class)
); , 100, TimeUnit.MILLISECONDS);
}) })
.onClose(this::close) .onClose(this::close)
.show(); .show(), 100, TimeUnit.MILLISECONDS);
}
);
} }
}); });

View file

@ -38,12 +38,11 @@ import javafx.stage.Stage;
import javafx.stage.StageStyle; import javafx.stage.StageStyle;
import javafx.stage.Window; import javafx.stage.Window;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.reactfx.util.FxTimer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static io.bitsquare.gui.util.FormBuilder.addCheckBox; import static io.bitsquare.gui.util.FormBuilder.addCheckBox;
@ -294,7 +293,7 @@ public abstract class Overlay<T extends Overlay> {
} }
protected void blurAgain() { protected void blurAgain() {
FxTimer.runLater(Duration.ofMillis(Transitions.DEFAULT_DURATION), MainView::blurLight); UserThread.runAfter(MainView::blurLight, Transitions.DEFAULT_DURATION, TimeUnit.MILLISECONDS);
} }
public void display() { public void display() {
@ -479,7 +478,7 @@ public abstract class Overlay<T extends Overlay> {
closeButton = new Button(closeButtonText == null ? "Close" : closeButtonText); closeButton = new Button(closeButtonText == null ? "Close" : closeButtonText);
closeButton.setOnAction(event -> { closeButton.setOnAction(event -> {
hide(); hide();
closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run()); closeHandlerOptional.ifPresent(Runnable::run);
}); });
if (actionHandlerOptional.isPresent() || actionButtonText != null) { if (actionHandlerOptional.isPresent() || actionButtonText != null) {
@ -489,7 +488,7 @@ public abstract class Overlay<T extends Overlay> {
//actionButton.requestFocus(); //actionButton.requestFocus();
actionButton.setOnAction(event -> { actionButton.setOnAction(event -> {
hide(); hide();
actionHandlerOptional.ifPresent(actionHandler -> actionHandler.run()); actionHandlerOptional.ifPresent(Runnable::run);
}); });
Pane spacer = new Pane(); Pane spacer = new Pane();

View file

@ -21,7 +21,7 @@ import io.bitsquare.gui.main.overlays.Overlay;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
public class Popup<T extends Overlay> extends Overlay<Popup> { public class Popup extends Overlay<Popup> {
protected final Logger log = LoggerFactory.getLogger(this.getClass()); protected final Logger log = LoggerFactory.getLogger(this.getClass());
public void onReadyForDisplay() { public void onReadyForDisplay() {

View file

@ -25,6 +25,7 @@ import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.btc.exceptions.TransactionVerificationException; import io.bitsquare.btc.exceptions.TransactionVerificationException;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2; import io.bitsquare.common.util.Tuple2;
import io.bitsquare.gui.main.overlays.Overlay; import io.bitsquare.gui.main.overlays.Overlay;
import io.bitsquare.gui.main.overlays.popups.Popup; import io.bitsquare.gui.main.overlays.popups.Popup;
@ -44,14 +45,13 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.reactfx.util.FxTimer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import java.time.Duration;
import java.util.Date; import java.util.Date;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static io.bitsquare.gui.util.FormBuilder.*; import static io.bitsquare.gui.util.FormBuilder.*;
@ -389,8 +389,9 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
disputeManager.sendDisputeResultMessage(disputeResult, dispute, text); disputeManager.sendDisputeResultMessage(disputeResult, dispute, text);
if (!finalPeersDispute.isClosed()) if (!finalPeersDispute.isClosed())
FxTimer.runLater(Duration.ofMillis(Transitions.DEFAULT_DURATION), () -> UserThread.runAfter(() ->
new Popup().instruction("You need to close also the trading peers ticket!").show()); new Popup().instruction("You need to close also the trading peers ticket!").show(),
Transitions.DEFAULT_DURATION, TimeUnit.MILLISECONDS);
hide(); hide();

View file

@ -19,6 +19,7 @@ package io.bitsquare.gui.main.overlays.windows;
import io.bitsquare.btc.Restrictions; import io.bitsquare.btc.Restrictions;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2; import io.bitsquare.common.util.Tuple2;
import io.bitsquare.gui.components.InputTextField; import io.bitsquare.gui.components.InputTextField;
import io.bitsquare.gui.main.overlays.Overlay; import io.bitsquare.gui.main.overlays.Overlay;
@ -34,13 +35,12 @@ import javafx.scene.layout.HBox;
import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.InsufficientMoneyException;
import org.reactfx.util.FxTimer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.crypto.params.KeyParameter;
import javax.inject.Inject; import javax.inject.Inject;
import java.time.Duration; import java.util.concurrent.TimeUnit;
import static io.bitsquare.gui.util.FormBuilder.*; import static io.bitsquare.gui.util.FormBuilder.*;
@ -138,9 +138,9 @@ public class EmptyWalletWindow extends Overlay<EmptyWalletWindow> {
addressTextField.setText(formatter.formatCoinWithCode(walletService.getAvailableBalance())); addressTextField.setText(formatter.formatCoinWithCode(walletService.getAvailableBalance()));
emptyWalletButton.setDisable(true); emptyWalletButton.setDisable(true);
log.debug("wallet empty successful"); log.debug("wallet empty successful");
FxTimer.runLater(Duration.ofMillis(Transitions.DEFAULT_DURATION), () -> new Popup() UserThread.runAfter(() -> new Popup()
.feedback("The balance of your wallet was successfully transferred.") .feedback("The balance of your wallet was successfully transferred.")
.onClose(() -> blurAgain()).show()); .onClose(() -> blurAgain()).show(), Transitions.DEFAULT_DURATION, TimeUnit.MILLISECONDS);
}, },
(errorMessage) -> { (errorMessage) -> {
emptyWalletButton.setDisable(false); emptyWalletButton.setDisable(false);

View file

@ -19,7 +19,7 @@ package io.bitsquare.gui.main.overlays.windows;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import io.bitsquare.common.crypto.KeyRing; import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.common.util.Tuple2; import io.bitsquare.common.util.Tuple3;
import io.bitsquare.gui.Navigation; import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.main.MainView; import io.bitsquare.gui.main.MainView;
import io.bitsquare.gui.main.account.AccountView; import io.bitsquare.gui.main.account.AccountView;
@ -36,9 +36,7 @@ import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.Preferences; import io.bitsquare.user.Preferences;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.control.Button; import javafx.scene.control.*;
import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -48,7 +46,6 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject; import javax.inject.Inject;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer;
import static io.bitsquare.gui.util.FormBuilder.*; import static io.bitsquare.gui.util.FormBuilder.*;
@ -62,8 +59,9 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
private final Navigation navigation; private final Navigation navigation;
private Offer offer; private Offer offer;
private Coin tradeAmount; private Coin tradeAmount;
private Optional<Consumer<Offer>> placeOfferHandlerOptional = Optional.empty(); private Optional<Runnable> placeOfferHandlerOptional = Optional.empty();
private Optional<Runnable> takeOfferHandlerOptional = Optional.empty(); private Optional<Runnable> takeOfferHandlerOptional = Optional.empty();
private ProgressIndicator spinner;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -100,7 +98,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
} }
public OfferDetailsWindow onPlaceOffer(Consumer<Offer> placeOfferHandler) { public OfferDetailsWindow onPlaceOffer(Runnable placeOfferHandler) {
this.placeOfferHandlerOptional = Optional.of(placeOfferHandler); this.placeOfferHandlerOptional = Optional.of(placeOfferHandler);
return this; return this;
} }
@ -111,7 +109,6 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Protected // Protected
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -236,41 +233,57 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
boolean isBuyOffer = offer.getDirection() == Offer.Direction.BUY; boolean isBuyOffer = offer.getDirection() == Offer.Direction.BUY;
boolean isBuyerRole = isPlaceOffer ? isBuyOffer : !isBuyOffer; boolean isBuyerRole = isPlaceOffer ? isBuyOffer : !isBuyOffer;
String placeOffer = isBuyerRole ? "Confirm place offer for buying bitcoin" : "Confirm place offer for selling bitcoin"; String placeOfferButtonText = isBuyerRole ? "Confirm place offer for buying bitcoin" : "Confirm place offer for selling bitcoin";
String takeOffer = isBuyerRole ? "Confirm take offer for buying bitcoin" : "Confirm take offer for selling bitcoin"; String takeOfferButtonText = isBuyerRole ? "Confirm take offer for buying bitcoin" : "Confirm take offer for selling bitcoin";
Tuple2<Button, Button> tuple = add2ButtonsAfterGroup(gridPane,
++rowIndex,
isPlaceOffer ? placeOffer : takeOffer,
"Cancel");
Button placeButton = tuple.first;
ImageView iconView = new ImageView(); ImageView iconView = new ImageView();
iconView.setId(isBuyerRole ? "image-buy-white" : "image-sell-white"); iconView.setId(isBuyOffer ? "image-buy-white" : "image-sell-white");
placeButton.setGraphicTextGap(10);
placeButton.setGraphic(iconView);
placeButton.setId(isBuyerRole ? "buy-button" : "sell-button"); Tuple3<Button, ProgressIndicator, Label> placeOfferTuple = addButtonWithStatusAfterGroup(gridPane, ++rowIndex, isPlaceOffer ? placeOfferButtonText : takeOfferButtonText);
placeButton.setOnAction(e -> { Button button = placeOfferTuple.first;
button.setGraphic(iconView);
button.setId(isBuyOffer ? "buy-button" : "sell-button");
button.setText(isPlaceOffer ? placeOfferButtonText : takeOfferButtonText);
spinner = placeOfferTuple.second;
spinner.setPrefSize(18, 18);
spinner.setVisible(false);
Label spinnerInfoLabel = placeOfferTuple.third;
Button cancelButton = addButton(gridPane, ++rowIndex, BSResources.get("shared.cancel"));
cancelButton.setDefaultButton(false);
cancelButton.setId("cancel-button");
cancelButton.setOnAction(e -> {
closeHandlerOptional.ifPresent(Runnable::run);
hide();
});
button.setOnAction(e -> {
if (user.getAcceptedArbitrators().size() > 0) { if (user.getAcceptedArbitrators().size() > 0) {
if (isPlaceOffer) button.setDisable(true);
placeOfferHandlerOptional.get().accept(offer); cancelButton.setDisable(true);
else spinner.setVisible(true);
spinner.setProgress(-1);
if (isPlaceOffer) {
spinnerInfoLabel.setText(BSResources.get("createOffer.fundsBox.placeOfferSpinnerInfo"));
placeOfferHandlerOptional.get().run();
} else {
spinnerInfoLabel.setText("Take offer in progress...");
takeOfferHandlerOptional.get().run(); takeOfferHandlerOptional.get().run();
}
} else { } else {
new Popup().warning("You have no arbitrator selected.\n" + new Popup().warning("You have no arbitrator selected.\n" +
"Please select at least one arbitrator.").show(); "Please select at least one arbitrator.").show();
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, ArbitratorSelectionView.class); navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, ArbitratorSelectionView.class);
} }
hide();
}); });
}
Button cancelButton = tuple.second; @Override
cancelButton.setId("cancel-button"); protected void onHidden() {
cancelButton.setOnAction(e -> { if (spinner != null)
closeHandlerOptional.ifPresent(Runnable::run); spinner.setProgress(0);
hide();
});
} }
} }

View file

@ -18,6 +18,7 @@
package io.bitsquare.gui.main.overlays.windows; package io.bitsquare.gui.main.overlays.windows;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.common.UserThread;
import io.bitsquare.crypto.ScryptUtil; import io.bitsquare.crypto.ScryptUtil;
import io.bitsquare.gui.components.PasswordTextField; import io.bitsquare.gui.components.PasswordTextField;
import io.bitsquare.gui.main.overlays.Overlay; import io.bitsquare.gui.main.overlays.Overlay;
@ -32,13 +33,12 @@ import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import org.bitcoinj.core.Wallet; import org.bitcoinj.core.Wallet;
import org.bitcoinj.crypto.KeyCrypterScrypt; import org.bitcoinj.crypto.KeyCrypterScrypt;
import org.reactfx.util.FxTimer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.crypto.params.KeyParameter;
import javax.inject.Inject; import javax.inject.Inject;
import java.time.Duration; import java.util.concurrent.TimeUnit;
public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> { public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> {
private static final Logger log = LoggerFactory.getLogger(WalletPasswordWindow.class); private static final Logger log = LoggerFactory.getLogger(WalletPasswordWindow.class);
@ -152,10 +152,10 @@ public class WalletPasswordWindow extends Overlay<WalletPasswordWindow> {
hide(); hide();
} else { } else {
FxTimer.runLater(Duration.ofMillis(Transitions.DEFAULT_DURATION), () -> new Popup() UserThread.runAfter(() -> new Popup()
.warning("You entered the wrong password.\n\n" + .warning("You entered the wrong password.\n\n" +
"Please try entering your password again, carefully checking for typos or spelling errors.") "Please try entering your password again, carefully checking for typos or spelling errors.")
.onClose(() -> blurAgain()).show()); .onClose(this::blurAgain).show(), Transitions.DEFAULT_DURATION, TimeUnit.MILLISECONDS);
} }
}); });
} else { } else {

View file

@ -21,6 +21,7 @@ import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.BitcoinNetwork; import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.btc.WalletService; import io.bitsquare.btc.WalletService;
import io.bitsquare.common.Clock; import io.bitsquare.common.Clock;
import io.bitsquare.common.UserThread;
import io.bitsquare.gui.common.model.Activatable; import io.bitsquare.gui.common.model.Activatable;
import io.bitsquare.gui.common.view.ActivatableViewAndModel; import io.bitsquare.gui.common.view.ActivatableViewAndModel;
import io.bitsquare.gui.common.view.FxmlView; import io.bitsquare.gui.common.view.FxmlView;
@ -40,11 +41,10 @@ import javafx.util.StringConverter;
import org.bitcoinj.core.Peer; import org.bitcoinj.core.Peer;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription; import org.fxmisc.easybind.Subscription;
import org.reactfx.util.FxTimer;
import javax.inject.Inject; import javax.inject.Inject;
import java.time.Duration;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@FxmlView @FxmlView
@ -137,7 +137,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
.actionButtonText("Apply and shut down") .actionButtonText("Apply and shut down")
.onAction(() -> { .onAction(() -> {
preferences.setUseTorForBitcoinJ(selected); preferences.setUseTorForBitcoinJ(selected);
FxTimer.runLater(Duration.ofMillis(500), BitsquareApp.shutDownHandler::run); UserThread.runAfter(BitsquareApp.shutDownHandler::run, 500, TimeUnit.MILLISECONDS);
}) })
.closeButtonText("Cancel") .closeButtonText("Cancel")
.onClose(() -> useTorCheckBox.setSelected(!selected)) .onClose(() -> useTorCheckBox.setSelected(!selected))
@ -204,7 +204,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
"Do you want to shut down now?") "Do you want to shut down now?")
.onAction(() -> { .onAction(() -> {
preferences.setBitcoinNetwork(netWorkComboBox.getSelectionModel().getSelectedItem()); preferences.setBitcoinNetwork(netWorkComboBox.getSelectionModel().getSelectedItem());
FxTimer.runLater(Duration.ofMillis(500), BitsquareApp.shutDownHandler::run); UserThread.runAfter(BitsquareApp.shutDownHandler::run, 500, TimeUnit.MILLISECONDS);
}) })
.actionButtonText("Shut down") .actionButtonText("Shut down")
.closeButtonText("Cancel") .closeButtonText("Cancel")

View file

@ -801,17 +801,16 @@ public class FormBuilder {
double top) { double top) {
HBox hBox = new HBox(); HBox hBox = new HBox();
hBox.setSpacing(10); hBox.setSpacing(10);
Button button = new Button(buttonTitle); Button button = new Button(buttonTitle);
button.setDefaultButton(true); button.setDefaultButton(true);
ProgressIndicator progressIndicator = new ProgressIndicator(0); ProgressIndicator progressIndicator = new ProgressIndicator(0);
progressIndicator.setPrefHeight(24); progressIndicator.setPrefHeight(24);
progressIndicator.setPrefWidth(24); progressIndicator.setPrefWidth(24);
progressIndicator.setVisible(false);
Label label = new Label(); Label label = new Label();
label.setPadding(new Insets(5, 0, 0, 0)); hBox.setAlignment(Pos.CENTER_LEFT);
hBox.getChildren().addAll(button, progressIndicator, label); hBox.getChildren().addAll(button, progressIndicator, label);
GridPane.setRowIndex(hBox, rowIndex); GridPane.setRowIndex(hBox, rowIndex);

View file

@ -63,7 +63,7 @@ createOffer.fundsBox.total=Total:
createOffer.fundsBox.showAdvanced=Show advanced settings createOffer.fundsBox.showAdvanced=Show advanced settings
createOffer.fundsBox.hideAdvanced=Hide advanced settings createOffer.fundsBox.hideAdvanced=Hide advanced settings
createOffer.fundsBox.placeOffer=Place offer createOffer.fundsBox.placeOffer=Place offer
createOffer.fundsBox.placeOfferSpinnerInfo=Offer fee payment is in progress... createOffer.fundsBox.placeOfferSpinnerInfo=Offer publishing is in progress...
createOffer.fundsBox.paymentLabel=Bitsquare trade with ID {0} createOffer.fundsBox.paymentLabel=Bitsquare trade with ID {0}
createOffer.advancedBox.title=Advanced settings createOffer.advancedBox.title=Advanced settings

View file

@ -61,8 +61,8 @@ public class Connection implements MessageListener {
private static final int MAX_MSG_SIZE = 100 * 1024; // 100 kb of compressed data private static final int MAX_MSG_SIZE = 100 * 1024; // 100 kb of compressed data
//TODO decrease limits again after testing //TODO decrease limits again after testing
private static final int MSG_THROTTLE_PER_SEC = 10; // With MAX_MSG_SIZE of 100kb results in bandwidth of 10 mbit/sec private static final int MSG_THROTTLE_PER_SEC = 50; // With MAX_MSG_SIZE of 100kb results in bandwidth of 5 mbit/sec
private static final int MSG_THROTTLE_PER_10_SEC = 100; // With MAX_MSG_SIZE of 100kb results in bandwidth of 100 mbit/sec for 10 sec private static final int MSG_THROTTLE_PER_10_SEC = 500; // With MAX_MSG_SIZE of 100kb results in bandwidth of 50 mbit/sec for 10 sec
private static final int SOCKET_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(60); private static final int SOCKET_TIMEOUT = (int) TimeUnit.SECONDS.toMillis(60);
public static int getMaxMsgSize() { public static int getMaxMsgSize() {