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.controlsfx.dialog.Dialogs;
import org.reactfx.EventStreams;
import org.reactfx.util.FxTimer;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import java.io.IOException;
import java.nio.file.Paths;
import java.security.Security;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
@ -333,9 +331,9 @@ public class BitsquareApp extends Application {
});
});
// we wait max 5 sec.
FxTimer.runLater(Duration.ofMillis(5000), resultHandler::handleResult);
UserThread.runAfter(resultHandler::handleResult, 5);
} else {
FxTimer.runLater(Duration.ofMillis(500), resultHandler::handleResult);
UserThread.runAfter(resultHandler::handleResult, 1);
}
} catch (Throwable t) {
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.Stage;
import javafx.util.Callback;
import org.reactfx.util.FxTimer;
import org.reactfx.util.Timer;
import javax.annotation.Nullable;
import javax.inject.Inject;
@ -66,10 +64,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
// will be probably only used for arbitration communication, will be renamed and the icon changed
@FxmlView
@ -214,7 +212,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
inputTextArea.setDisable(true);
inputTextArea.clear();
final Timer timer = FxTimer.runLater(Duration.ofMillis(500), () -> {
io.bitsquare.common.Timer timer = UserThread.runAfter(() -> {
sendMsgInfoLabel.setVisible(true);
sendMsgInfoLabel.setManaged(true);
sendMsgInfoLabel.setText("Sending Message...");
@ -222,7 +220,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
sendMsgProgressIndicator.setProgress(-1);
sendMsgProgressIndicator.setVisible(true);
sendMsgProgressIndicator.setManaged(true);
});
}, 500, TimeUnit.MILLISECONDS);
arrivedPropertyListener = (observable, oldValue, newValue) -> {
if (newValue) {
@ -242,14 +240,14 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
disputeCommunicationMessage.storedInMailboxProperty().addListener(storedInMailboxPropertyListener);
}
private void hideSendMsgInfo(Timer timer) {
private void hideSendMsgInfo(io.bitsquare.common.Timer timer) {
timer.stop();
inputTextArea.setDisable(false);
FxTimer.runLater(Duration.ofMillis(5000), () -> {
UserThread.runAfter(() -> {
sendMsgInfoLabel.setVisible(false);
sendMsgInfoLabel.setManaged(false);
});
}, 5);
sendMsgProgressIndicator.setProgress(0);
sendMsgProgressIndicator.setVisible(false);
sendMsgProgressIndicator.setManaged(false);

View file

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

View file

@ -18,6 +18,9 @@
package io.bitsquare.gui.main.offer.createoffer;
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.ViewModel;
import io.bitsquare.gui.util.BSFormatter;
@ -62,15 +65,16 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
final StringProperty errorMessage = new SimpleStringProperty();
final StringProperty btcCode = 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 cancelButtonDisabled = new SimpleBooleanProperty();
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 showWarningInvalidFiatDecimalPlaces = 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> minAmountValidationResult = new
@ -92,10 +96,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
private ChangeListener<Fiat> volumeAsFiatListener;
private ChangeListener<Boolean> isWalletFundedListener;
private ChangeListener<Coin> feeFromFundingTxListener;
private ChangeListener<Boolean> requestPlaceOfferSuccessListener;
private ChangeListener<String> requestPlaceOfferErrorMessageListener;
private ChangeListener<String> errorMessageListener;
private Offer offer;
private Timer timeoutTimer;
///////////////////////////////////////////////////////////////////////////////////////////
@ -156,6 +160,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
protected void deactivate() {
removeBindings();
removeListeners();
stopTimeoutTimer();
}
private void addBindings() {
@ -232,27 +237,19 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
isWalletFundedListener = (ov, oldValue, newValue) -> {
updateButtonDisableState();
isPlaceOfferSpinnerVisible.set(true);
placeOfferSpinnerInfoText.set("Checking funding tx miner fee...");
spinnerInfoText.set("Checking funding tx miner fee...");
};
feeFromFundingTxListener = (ov, oldValue, newValue) -> {
updateButtonDisableState();
if (newValue.isPositive()) {
isPlaceOfferSpinnerVisible.set(false);
placeOfferSpinnerInfoText.set("");
}
};
requestPlaceOfferSuccessListener = (ov, oldValue, newValue) -> {
if (newValue) {
isPlaceOfferButtonDisabled.set(newValue);
isPlaceOfferSpinnerVisible.set(false);
placeOfferSpinnerInfoText.set("");
if (newValue.compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0) {
isSpinnerVisible.set(false);
spinnerInfoText.set("");
}
};
requestPlaceOfferErrorMessageListener = (ov, oldValue, newValue) -> {
if (newValue != null) {
isPlaceOfferSpinnerVisible.set(false);
placeOfferSpinnerInfoText.set("");
isSpinnerVisible.set(false);
spinnerInfoText.set("");
}
};
}
@ -273,7 +270,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
dataModel.isWalletFunded.addListener(isWalletFundedListener);
requestPlaceOfferSuccess.addListener(requestPlaceOfferSuccessListener);
errorMessage.addListener(requestPlaceOfferErrorMessageListener);
}
@ -292,7 +288,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
dataModel.isWalletFunded.removeListener(isWalletFundedListener);
requestPlaceOfferSuccess.removeListener(requestPlaceOfferSuccessListener);
errorMessage.removeListener(requestPlaceOfferErrorMessageListener);
if (offer != null && errorMessageListener != null)
@ -313,25 +308,51 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
// UI actions
///////////////////////////////////////////////////////////////////////////////////////////
void onPlaceOffer(Offer offer) {
void onPlaceOffer(Offer offer, Runnable resultHandler) {
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) -> {
if (newValue != null) {
stopTimeoutTimer();
isPlaceOfferButtonDisabled.set(false);
cancelButtonDisabled.set(false);
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. " +
"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.");
else
this.errorMessage.set(newValue);
errorMessage.set(newValue);
resultHandler.run();
}
};
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) {

View file

@ -21,6 +21,7 @@ import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2;
import io.bitsquare.common.util.Tuple3;
import io.bitsquare.common.util.Utilities;
@ -59,10 +60,9 @@ import org.bitcoinj.core.Coin;
import org.controlsfx.control.PopOver;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
import org.reactfx.util.FxTimer;
import javax.inject.Inject;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import static io.bitsquare.gui.util.FormBuilder.*;
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) {
FxTimer.runLater(Duration.ofMillis(100),
() -> {
new Popup().headLine(BSResources.get("takeOffer.success.headline"))
.feedback(BSResources.get("takeOffer.success.info"))
.actionButtonText("Go to \"Open trades\"")
.onAction(() -> {
close();
FxTimer.runLater(Duration.ofMillis(100),
() -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class)
);
})
.onClose(this::close)
.show();
}
);
UserThread.runAfter(
() -> new Popup().headLine(BSResources.get("takeOffer.success.headline"))
.feedback(BSResources.get("takeOffer.success.info"))
.actionButtonText("Go to \"Open trades\"")
.onAction(() -> {
close();
UserThread.runAfter(
() -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class)
, 100, TimeUnit.MILLISECONDS);
})
.onClose(this::close)
.show(), 100, TimeUnit.MILLISECONDS);
}
});

View file

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

View file

@ -21,7 +21,7 @@ import io.bitsquare.gui.main.overlays.Overlay;
import org.slf4j.Logger;
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());
public void onReadyForDisplay() {

View file

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

View file

@ -19,6 +19,7 @@ package io.bitsquare.gui.main.overlays.windows;
import io.bitsquare.btc.Restrictions;
import io.bitsquare.btc.WalletService;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2;
import io.bitsquare.gui.components.InputTextField;
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.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import org.reactfx.util.FxTimer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter;
import javax.inject.Inject;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import static io.bitsquare.gui.util.FormBuilder.*;
@ -138,9 +138,9 @@ public class EmptyWalletWindow extends Overlay<EmptyWalletWindow> {
addressTextField.setText(formatter.formatCoinWithCode(walletService.getAvailableBalance()));
emptyWalletButton.setDisable(true);
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.")
.onClose(() -> blurAgain()).show());
.onClose(() -> blurAgain()).show(), Transitions.DEFAULT_DURATION, TimeUnit.MILLISECONDS);
},
(errorMessage) -> {
emptyWalletButton.setDisable(false);

View file

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

View file

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

View file

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

View file

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

View file

@ -63,7 +63,7 @@ createOffer.fundsBox.total=Total:
createOffer.fundsBox.showAdvanced=Show advanced settings
createOffer.fundsBox.hideAdvanced=Hide advanced settings
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.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
//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_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_SEC = 50; // With MAX_MSG_SIZE of 100kb results in bandwidth of 5 mbit/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);
public static int getMaxMsgSize() {